Why Validate Forms?
Form validation ensures that user input meets the required criteria before it is submitted. It prevents invalid data from being sent to your backend, improves user experience by providing immediate feedback, and reduces bugs. In Flutter, the Form widget combined with TextFormField provides a powerful way to validate user input. This guide covers everything from basic required field validation to advanced async checks.
Basic Validation with TextFormField
TextFormField has a validator property that takes a function returning a String?. If the function returns a non‑null string, that string is displayed as an error below the field. Returning null means the field is valid.
The validator is called automatically when the form is validated (e.g., _formKey.currentState!.validate()). It's also called on each change if autovalidateMode is set to AutovalidateMode.onUserInteraction.
Using a GlobalKey<FormState>
To validate a form, you need a GlobalKey<FormState>. This key gives you access to the form's state, allowing you to call validate() and save().
Common Validators
Here are reusable validator functions for common scenarios:
- Required field:
if (value == null || value.isEmpty) return 'Required';
- Required field:
- Email validation:
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) return 'Invalid email';
- Email validation:
- Minimum length:
if (value.length < 6) return 'At least 6 characters';
- Minimum length:
- Number validation:
if (double.tryParse(value) == null) return 'Must be a number';
- Number validation:
- Password confirmation: compare with another field's value (store both in state).
AutovalidateMode
autovalidateMode controls when validation errors are displayed. Options:
AutovalidateMode.disabled(default): Errors appear only aftervalidate()is called (e.g., on submit).
AutovalidateMode.onUserInteraction: Errors appear as soon as the user starts typing or changes the field.
AutovalidateMode.always: Errors are always shown, even before user interaction (use with caution).
Custom FormField for Complex Validations
If you need validation for custom input (like a group of radio buttons or a date picker), you can create a FormField widget that manages its own validation state. Here's an example for a radio group:
Async Validation
Sometimes you need to check a field against a server (e.g., username availability). You can't return a Future directly from validator, so you need to manage async validation manually. A common pattern is to use a StatefulWidget with a loading indicator and an error state, and call validation on user input.
Best Practices
- Use reusable validator functions to keep code DRY.
- Set
autovalidateModetoonUserInteractionfor immediate feedback, but be careful not to overwhelm the user with errors before they finish typing.
- Set
- Provide clear, actionable error messages (e.g., 'Password must be at least 8 characters' rather than 'Invalid').
- Validate on submit as well; even if you use
onUserInteraction, always callvalidate()before submission to ensure all fields are valid.
- Validate on submit as well; even if you use
- Use
GlobalKey<FormState>for each form, and don't reuse the same key across multiple forms.
- Use
- For complex forms, consider using a state management solution like Provider or BLoC to separate validation logic from UI.
Common Mistakes
- Not calling
validate()before submission: The form may be submitted with invalid data.
- Not calling
- Returning
nullfrom validator on error: The error message won't be shown.
- Returning
- Using
nullcheck without handling empty strings:value.isEmptywill throw ifvalueis null; always check null first.
- Using
- Not setting
autovalidateMode: Errors only appear after first submit, which can be confusing.
- Not setting
- Using the same
GlobalKeyfor multiple forms: This will cause conflicts.
- Using the same
- Forgetting to dispose controllers (if you use them in validation).
Key Takeaways
- Use
GlobalKey<FormState>to manage a form.
- Use
- Define
validatorfunctions that returnString?–nullmeans valid.
- Define
- Use
autovalidateModeto control when errors appear.
- Use
- Create reusable validators for common cases (email, required, etc.).
- For custom widgets, use
FormFieldto integrate validation.
- For custom widgets, use
- Handle async validation with manual state management and loading indicators.