'How to disable button until text form field has valid data Flutter
What I want to do is disable the elevated button until the text form field is valid. And then once the data is valid the elevated button should be enabled. I have reviewed several SO threads and a few articles on Google about how to disable a button until the text form field is validated. They all focused on whether or not the text form field was empty or not which is not what I am asking about here. I'm using regex to determine if the user has entered a valid email address. Only when the data entered is a valid email is the data considered valid. That is when I want the button to become enabled. If I try to call setState with a boolean in the validateEmail method I get the error:
setState() or markNeedsBuild() called during build.
Any help will be appreciated. Thank you.
class ResetPasswordForm extends StatefulWidget {
const ResetPasswordForm({Key? key}) : super(key: key);
@override
_ResetPasswordFormState createState() => _ResetPasswordFormState();
}
class _ResetPasswordFormState extends State<ResetPasswordForm> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
String? validateEmail(String? value) {
String pattern = ValidatorRegex.emailAddress;
RegExp regex = RegExp(pattern);
if (value == null || value.isEmpty || !regex.hasMatch(value)) {
return ValidatorString.enterValidEmail;
} else {
return null;
}
}
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Form(
key: _formKey,
child: TextFormField(
controller: _emailController,
validator: (value) => validateEmail(value),
),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Auth().resetPassword(
context,
_emailController.text.trim(),
);
}
},
child: const Text('Reset Password'),
),
],
);
}
}
Solution 1:[1]
you can do somthing like that:
class ResetPasswordForm extends StatefulWidget {
const ResetPasswordForm({Key? key}) : super(key: key);
@override
_ResetPasswordFormState createState() => _ResetPasswordFormState();
}
class _ResetPasswordFormState extends State<ResetPasswordForm> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
final bool _isValidated = false;
String? validateEmail(String? value) {
String pattern = ValidatorRegex.emailAddress;
RegExp regex = RegExp(pattern);
if (value == null || value.isEmpty || !regex.hasMatch(value)) {
return ValidatorString.enterValidEmail;
} else {
setState(){
_isValidated = true;
}
return null;
}
}
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Form(
key: _formKey,
child: TextFormField(
controller: _emailController,
validator: (value) => validateEmail(value),
),
),
ElevatedButton(
onPressed:_isValidated
? () {
//do stuff
}
: null,,
child: const Text('Reset Password'),
),
],
);
}
}
if onPressed be null, the button is disabled.
Solution 2:[2]
Enabling and disabling functionality is the same for most widgets
set onPressed property as shown below
onPressed : null returns a disabled widget while
onPressed: (){} or onPressed: _functionName returns enabled widget
in this case it'll be this way:
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Auth().resetPassword(
context,
_emailController.text.trim(),
);
} else {
print('disabled');
}
},
child: const Text('Reset Password'),
),
Solution 3:[3]
First, move the logic into a named function
void _sendData (){
if (_formKey.currentState!.validate()) {
Auth().resetPassword( context,
_emailController.text.trim(), );
}
Now in onpressed
onpressed: _emailController.text.trim.isNotEmpty?_sendData : null;
Solution 4:[4]
Best for this is to just create a form key for this
final formGlobalKey = GlobalKey <FormState> ();
Assign it to form like:
Form(
key: formGlobalKey,
And now you just have to check the validation for this like:
ElevatedButton(
style: style,
onPressed: formGlobalKey.currentState==null?null: formGlobalKey.currentState!.validate()? () {
This is body of button} : null,
child: const Text('Enabled'),
),
**If you didn't use first condition (formGlobalKey.currentState==null?) it will take you towards null exception **
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | CharlyKeleb |
| Solution 3 | O'neya |
| Solution 4 | M.Adnan Ijaz |
