TextFormField validation when focus changes

Viewed 8893

I am using a form and some TextFormFields. I can validate the user input as below:

final _form = GlobalKey<FormState>();

void saveForm(){

_form.currentState.validate()

}

//saveForm runs when a save button is pressed.

I want to know whether it's possible to run validate function for a TextField when it loses its focus or not.(I don't want to validate the input by clicking a button and instead, I want to run validate function when user changes the TextField.)

5 Answers

I think you just want to add autovalidate: true, in your form so when your focus is change validation is call

 Form(
         key: _formKey,
         autovalidate: true,
         child:/*...*/
    )

Create your form like

  var _formKey = GlobalKey<FormState>();
  bool _autoValidate = false;

  @override
  Widget build(BuildContext context) {
    return Form(
          key: _formKey,
          autovalidate: _autoValidate,
          child: /* ... */
        ),
      ),
    );
  }

when you click on save

void _onSignUpClicked(BuildContext context) {
    if (_formKey.currentState.validate()) {
      Scaffold.of(context).showSnackBar(SnackBar(content: Text("Successfully sign up")));
    } else {
      setState(() {
        _autoValidate = true;
      });
    }
  }

Put TextEditingController to TextField and use addListener for getting every change. And do validate process inside that listener.

To validate a text field when focus changes from one field to the next, you need a FocusNode to detect when focus has been removed and a GlobalKey<FormFieldState> on the TextFormField to tell it to validate. Here is a sample:

class FormValidationDemo extends StatefulWidget {
  const FormValidationDemo({Key key}) : super(key: key);

  @override
  _FormValidationDemoState createState() => _FormValidationDemoState();
}

class _FormValidationDemoState extends State<FormValidationDemo> {
  final _formKey = GlobalKey<FormState>();

  FocusNode focusNode1;
  FocusNode focusNode2;
  final field1Key = GlobalKey<FormFieldState>();
  final field2Key = GlobalKey<FormFieldState>();

  @override
  void initState() {
    super.initState();
    focusNode1 = FocusNode();
    focusNode2 = FocusNode();
    focusNode1.addListener(() {
      if (!focusNode1.hasFocus) {
        field1Key.currentState.validate();
      }
    });
    focusNode2.addListener(() {
      if (!focusNode2.hasFocus) {
        field2Key.currentState.validate();
      }
    });
  }

  @override
  void dispose() {
    focusNode1.dispose();
    focusNode2.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Form Demo')),
      body: Form(
        key: _formKey,
        child: Column(
          children: [
            TextFormField(
              key: field1Key,
              focusNode: focusNode1,
              validator: (value) {
                if (value.isEmpty) {
                  return 'Please enter a value.';
                }
                return null;
              },
            ),
            TextFormField(
              key: field2Key,
              focusNode: focusNode2,
              validator: (value) {
                if (value.isEmpty) {
                  return 'Please enter a value.';
                }
                return null;
              },
            ),
          ],
        ),
      ),
    );
  }
}




To notice, the accepted solution now is deprecated after 1.19
Now, new parameters has been introduced AutovalidateMode with three essential constants:

  1. always : Used to auto-validate Form and FormField even without user interaction.
  2. disabled : No auto validation will occur.
  3. onUserInteraction : Used to auto-validate Form and FormField only after each user interaction.
Related