mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-19 05:23:43 +02:00
feat: add validation to disable next button
This commit is contained in:
parent
15aa74ebda
commit
277b38f39e
7 changed files with 57 additions and 19 deletions
|
@ -35,9 +35,8 @@ class AuthScreen extends StatefulWidget {
|
||||||
final String previousBtnTitle;
|
final String previousBtnTitle;
|
||||||
final AppBar? customAppBar;
|
final AppBar? customAppBar;
|
||||||
final Color? customBackgroundColor;
|
final Color? customBackgroundColor;
|
||||||
final Widget Function(
|
final Widget Function(Future<void> Function()? onPressed, String label,
|
||||||
Future<void> Function() onPressed, String label, int step)?
|
int step, bool enabled)? nextButtonBuilder;
|
||||||
nextButtonBuilder;
|
|
||||||
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
||||||
previousButtonBuilder;
|
previousButtonBuilder;
|
||||||
final Widget? titleWidget;
|
final Widget? titleWidget;
|
||||||
|
@ -52,6 +51,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
final _pageController = PageController();
|
final _pageController = PageController();
|
||||||
final _animationDuration = const Duration(milliseconds: 300);
|
final _animationDuration = const Duration(milliseconds: 300);
|
||||||
final _animationCurve = Curves.ease;
|
final _animationCurve = Curves.ease;
|
||||||
|
bool _formValid = false;
|
||||||
|
|
||||||
AppBar get _appBar =>
|
AppBar get _appBar =>
|
||||||
widget.customAppBar ??
|
widget.customAppBar ??
|
||||||
|
@ -60,6 +60,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
);
|
);
|
||||||
|
|
||||||
void onPrevious() {
|
void onPrevious() {
|
||||||
|
_validate(_pageController.page!.toInt() - 1);
|
||||||
_pageController.previousPage(
|
_pageController.previousPage(
|
||||||
duration: _animationDuration,
|
duration: _animationDuration,
|
||||||
curve: _animationCurve,
|
curve: _animationCurve,
|
||||||
|
@ -95,6 +96,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
_validate(_pageController.page!.toInt() + 1);
|
||||||
_pageController.nextPage(
|
_pageController.nextPage(
|
||||||
duration: _animationDuration,
|
duration: _animationDuration,
|
||||||
curve: _animationCurve,
|
curve: _animationCurve,
|
||||||
|
@ -102,6 +104,29 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _validate(int currentPage) {
|
||||||
|
bool isStepValid = true;
|
||||||
|
|
||||||
|
// Loop through each field in the current step
|
||||||
|
for (var field in widget.steps[currentPage].fields) {
|
||||||
|
for (var validator in field.validators) {
|
||||||
|
String? validationResult = validator(field.value);
|
||||||
|
if (validationResult != null) {
|
||||||
|
// If any validator returns an error, mark step as invalid and break
|
||||||
|
isStepValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isStepValid) {
|
||||||
|
break; // No need to check further fields if one is already invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_formValid = isStepValid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -136,7 +161,9 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
if (field.title != null) ...[
|
if (field.title != null) ...[
|
||||||
field.title!,
|
field.title!,
|
||||||
],
|
],
|
||||||
field.build(),
|
field.build(context, () {
|
||||||
|
_validate(i);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -197,16 +224,21 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
.call(onPrevious, widget.previousBtnTitle, i)!
|
.call(onPrevious, widget.previousBtnTitle, i)!
|
||||||
],
|
],
|
||||||
widget.nextButtonBuilder?.call(
|
widget.nextButtonBuilder?.call(
|
||||||
() async {
|
!_formValid
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
await onNext(widget.steps[i]);
|
await onNext(widget.steps[i]);
|
||||||
},
|
},
|
||||||
widget.steps.last == widget.steps[i]
|
widget.steps.last == widget.steps[i]
|
||||||
? widget.submitBtnTitle
|
? widget.submitBtnTitle
|
||||||
: widget.nextBtnTitle,
|
: widget.nextBtnTitle,
|
||||||
i,
|
i,
|
||||||
|
_formValid,
|
||||||
) ??
|
) ??
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: !_formValid
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
await onNext(widget.steps[i]);
|
await onNext(widget.steps[i]);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
|
@ -28,9 +28,8 @@ class RegistrationOptions {
|
||||||
final VoidCallback afterRegistration;
|
final VoidCallback afterRegistration;
|
||||||
final RegistrationRepository registrationRepository;
|
final RegistrationRepository registrationRepository;
|
||||||
final AppBar Function(String title)? customAppbarBuilder;
|
final AppBar Function(String title)? customAppbarBuilder;
|
||||||
final Widget Function(
|
final Widget Function(Future<void> Function()? onPressed, String label,
|
||||||
Future<void> Function() onPressed, String label, int step)?
|
int step, bool enabled)? nextButtonBuilder;
|
||||||
nextButtonBuilder;
|
|
||||||
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
||||||
previousButtonBuilder;
|
previousButtonBuilder;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
|
@ -24,12 +24,13 @@ class AuthBoolField extends AuthField {
|
||||||
final Function(String value)? onChange;
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build() {
|
Widget build(BuildContext context, Function onValueChanged) {
|
||||||
return FlutterFormInputBool(
|
return FlutterFormInputBool(
|
||||||
widgetType: widgetType,
|
widgetType: widgetType,
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
value = v;
|
value = v;
|
||||||
onChange?.call(value);
|
onChange?.call(value);
|
||||||
|
onValueChanged();
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
for (var validator in validators) {
|
for (var validator in validators) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class AuthDropdownField extends AuthField {
|
||||||
final Icon icon;
|
final Icon icon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build() {
|
Widget build(BuildContext context, Function onValueChanged) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
|
@ -41,6 +41,7 @@ class AuthDropdownField extends AuthField {
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
selectedValue = newValue;
|
selectedValue = newValue;
|
||||||
onChanged(newValue);
|
onChanged(newValue);
|
||||||
|
onValueChanged();
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (validators.isNotEmpty) {
|
if (validators.isNotEmpty) {
|
||||||
|
|
|
@ -7,9 +7,10 @@ import 'package:flutter/material.dart';
|
||||||
abstract class AuthField<T> {
|
abstract class AuthField<T> {
|
||||||
AuthField({
|
AuthField({
|
||||||
required this.name,
|
required this.name,
|
||||||
|
required this.value,
|
||||||
|
this.onValueChanged,
|
||||||
this.title,
|
this.title,
|
||||||
this.validators = const [],
|
this.validators = const [],
|
||||||
required this.value,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
|
@ -17,5 +18,7 @@ abstract class AuthField<T> {
|
||||||
List<String? Function(T?)> validators;
|
List<String? Function(T?)> validators;
|
||||||
T value;
|
T value;
|
||||||
|
|
||||||
Widget build();
|
final Function(T)? onValueChanged; // Callback for value changes
|
||||||
|
|
||||||
|
Widget build(BuildContext context, Function onValueChanged);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class AuthPassField extends AuthField {
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build() {
|
Widget build(BuildContext context, Function onValueChanged) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: FlutterFormInputPassword(
|
child: FlutterFormInputPassword(
|
||||||
|
@ -37,6 +37,7 @@ class AuthPassField extends AuthField {
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
value = v;
|
value = v;
|
||||||
onChange?.call(value);
|
onChange?.call(value);
|
||||||
|
onValueChanged();
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
for (var validator in validators) {
|
for (var validator in validators) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ class AuthTextField extends AuthField {
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build() {
|
Widget build(BuildContext context, Function onValueChanged) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
@ -38,6 +38,7 @@ class AuthTextField extends AuthField {
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
value = v;
|
value = v;
|
||||||
onChange?.call(value);
|
onChange?.call(value);
|
||||||
|
onValueChanged();
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
for (var validator in validators) {
|
for (var validator in validators) {
|
||||||
|
|
Loading…
Reference in a new issue