Build a form with validation
Apps often require users to enter information into a text field. For example, you might require users to log in
with an email address and password combination.
To make apps secure and easy to use, check whether the information the user has provided is valid. If the user
has correctly filled out the form, process the information. If the user submits incorrect information, display a
friendly error message letting them know what went wrong.
In this example, learn how to add validation to a form that has a single text field using the following steps:
- Create a Form with a GlobalKey
- Add a TextFormField with validation logic
- Create a button to validate and submit the form
- Example:
First, create a Form. The Form widget acts as a container for grouping and
validating multiple form fields.
When creating the form, provide a GlobalKey. This uniquely
identifies the Form, and allows validation of the
form in a later step.
import 'package:flutter/material.dart';
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Define a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a `GlobalKey<FormState>`,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
children: <Widget>[
// Add TextFormFields and ElevatedButton here.
],
),
);
}
}
Tip:
Using a GlobalKey is the recommended way to access a form. However, if you have a more complex widget tree, you can use the Form.of() method to access the form within nested widgets.
Although the Form is in place, it doesn’t have a way for users to enter text. That’s the job of a
TextFormField. The TextFormField widget renders a material design text field and
can display validation
errors when they occur.
Validate the input by providing a validator() function to the TextFormField. If the user’s input isn’t
valid, the validator function returns a String containing an error message. If there are no errors, the
validator must return null.
For this example, create a validator that ensures the TextFormField isn’t empty. If it is empty, return a
friendly error message.
TextFormField(
// The validator receives the text that the user has entered.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Now that you have a form with a text field, provide a button that the user can tap to submit the
information.
When the user attempts to submit the form, check if the form is valid. If it is, display a success message.
If it isn’t (the text field has no content) display the error message.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter form with validation';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyCustomForm(),
),
);
}
}
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
class MyCustomFormState extends State<MyCustomForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Padding(
padding: const EdgeInsets.all(18.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
hintText: "Username",
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your username';
} else if (value.length < 3) {
return "username must have a minimum length of 3";
}
return null;
},
),
TextFormField(
decoration: InputDecoration(
hintText: "Email",
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
} else if (!value.contains("@")) {
return 'Please enter a valid email';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: const Text('Submit'),
),
],
),
),
);
}
}
How does this work?
To validate the form, use the _formKey created in step 1. You can use the _formKey.currentState() method to access the FormState, which is automatically created by Flutter when building a Form. The FormState class contains the validate() method. When the validate() method is called, it runs the validator() function for each text field in the form. If everything looks good, the validate() method returns true. If any text field contains errors, the validate() method rebuilds the form to display any error messages and returns false.