Building Validated Forms
Contents
Form Architecture
Implement forms using a Form widget to group and validate multiple input fields together.
- Use a StatefulWidget: Always host your
Forminside aStatefulWidget. - Persist the GlobalKey: Instantiate a
GlobalKey<FormState>exactly once as a final variable within theStateclass. Do not generate a newGlobalKeyinside thebuildmethod. - Bind the Key: Pass the
GlobalKey<FormState>to thekeyproperty of theFormwidget. - Alternative Access: Use
Form.of(context)to access theFormStatefrom a descendant widget.
Field Validation
Use TextFormField for Material Design text inputs with built-in validation.
- Implement the Validator: Provide a
validator()callback to eachTextFormField. - Return Error Messages: If invalid, return a
Stringwith the error message. - Return Null for Success: If valid, return
null.
Workflow: Implementing a Validated Form
- Create a
StatefulWidgetand itsStateclass. - Instantiate
final _formKey = GlobalKey<FormState>();in theStateclass. - Return a
Formwidget inbuildand assignkey: _formKey. - Add
TextFormFieldwidgets as descendants of theForm. - Write a
validatorfunction for each field (returnStringon error,nullon success). - Add a submit button.
- Validate in
onPressedusing_formKey.currentState!.validate().
Validation Decision Logic
- Call
_formKey.currentState!.validate(). - If
true: Proceed with submission (save data, API call) and show success (SnackBar). - If
false: Error messages display automatically. User adjusts input and resubmits.
Examples
Complete Validated Form
class UserRegistrationForm extends StatefulWidget {
const UserRegistrationForm({super.key});
@override
State<UserRegistrationForm> createState() => _UserRegistrationFormState();
}
class _UserRegistrationFormState extends State<UserRegistrationForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'Username',
hintText: 'Enter your username',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a username';
}
if (value.length < 4) {
return 'Username must be at least 4 characters';
}
return null;
},
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: const Text('Submit'),
),
],
),
);
}
}
Source: openplaybooks-dev/converge — distributed by TomeVault.