Flutter Form Validation
This section will introduce creating, validating, and handling form submissions in Flutter.
* * *
## TextFormField - Form Input
TextFormField is a TextField specifically designed for forms, supporting built-in validation features.
## Example: Basic Form
class LoginForm extends StatefulWidget {
const LoginForm({super.key});
@override
State createState()=> _LoginFormState();
}
class _LoginFormState extends State{
// Form key (used for validation)
final _formKey = GlobalKey();
// Input controllers
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose(){
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context){
return Form(
key: _formKey,
child: Column(
children:[
// Username input
TextFormField(
controller: _usernameController,
decoration:const InputDecoration(
labelText:'Username',
prefixIcon: Icon(Icons.person),
),
// Validator
validator:(value){
if(value ==null|| value.isEmpty){
return'Please enter username';
}
if(value.length<3){
return'Username must be at least 3 characters';
}
return null;
},
),
const SizedBox(height:16),
// Password input
TextFormField(
controller: _passwordController,
obscureText:true,// Hide password
decoration:const InputDecoration(
labelText:'Password',
prefixIcon: Icon(Icons.lock),
),
validator:(value){
if(value ==null|| value.isEmpty){
return'Please enter password';
}
if(value.length<6){
return'Password must be at least 6 characters';
}
return null;
},
),
const SizedBox(height:24),
// Submit button
ElevatedButton(
onPressed: _submit,
child:const Text('Login'),
),
],
),
);
}
void _submit(){
// Validate form
if(_formKey.currentState?.validate()??false){
// Validation passed
print('Username: ${_usernameController.text}');
print('Password: ${_passwordController.text}');
// Submit to server...
}else{
// Validation failed
print('Form validation failed');
}
}
}
* * *
## Common Validators
## Example: Common Validation Rules
// Validate username
String? validateUsername(String? value){
if(value ==null|| value.isEmpty){
return'Please enter username';
}
if(!RegExp(r'^+$').hasMatch(value)){
return'Only letters, numbers and underscores are allowed';
}
return null;
}
// Validate email
String? validateEmail(String? value){
if(value ==null|| value.isEmpty){
return'Please enter email';
}
final emailRegex = RegExp(r'^[w-.]+@(+.)+{2,4}$');
if(!emailRegex.hasMatch(value)){
return'Please enter a valid email address';
}
return null;
}
// Validate password strength
String? validatePassword(String? value){
if(value ==null|| value.isEmpty){
return'Please enter password';
}
if(value.length<8){
return'Password must be at least 8 characters';
}
if(!RegExp(r'').hasMatch(value)){
return'Password must contain at least one uppercase letter';
}
if(!RegExp(r'').hasMatch(value)){
return'Password must contain at least one digit';
}
return null;
}
// Validate phone number
String? validatePhone(String? value){
if(value ==null|| value.isEmpty){
return'Please enter phone number';
}
final phoneRegex = RegExp(r'^1d{9}$');
if(!phoneRegex.hasMatch(value)){
return'Please enter a valid phone number';
}
return null;
}
// Validate password confirmation
String? validateConfirmPassword(String? value, String password){
if(value ==null|| value.isEmpty){
return'Please confirm password';
}
if(value != password){
return'Passwords do not match';
}
return null;
}
* * *
## Complex Form Example
## Example: Registration Form
class RegisterForm extends StatefulWidget {
const RegisterForm({super.key});
@override
State createState()=> _RegisterFormState();
}
class _RegisterFormState extends State{
final _formKey = GlobalKey();
final _usernameController = TextEditingController();
final _emailController = TextEditingController();
final _phoneController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
bool _acceptTerms =false;
@override
Widget build(BuildContext context){
return Form(
key: _formKey,
child: ListView(
padding:const EdgeInsets.all(16),
children:[
// Username
TextFormField(
controller: _usernameController,
decoration:const InputDecoration(labelText:'Username'),
validator: validateUsername,
),
const SizedBox(height:16),
// Email
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration:const InputDecoration(labelText:'Email'),
validator: validateEmail,
),
const SizedBox(height:16),
// Phone number
TextFormField(
controller: _phoneController,
keyboardType: TextInputType.phone,
decoration:const InputDecoration(labelText:'Phone Number'),
validator: validatePhone,
),
const SizedBox(height:16),
// Password
TextFormField(
controller: _passwordController,
obscureText:true,
decoration:const InputDecoration(labelText:'Password'),
validator: validatePassword,
),
const SizedBox(height:16),
// Confirm password
TextFormField(
controller: _confirmPasswordController,
obscureText:true,
decoration:const InputDecoration(labelText:'Confirm Password'),
validator:(value)=> validateConfirmPassword(
value,
_passwordController.text,
),
),
const SizedBox(height:16),
// Accept terms
CheckboxListTile(
value: _acceptTerms,
onChanged:(value){
setState((){
_acceptTerms = value ??false;
});
},
title:const Text('I agree to the terms of service'),
controlAffinity: ListTileControlAffinity.leading,
),
const SizedBox(height:24),
// Submit button
ElevatedButton(
onPressed: _acceptTerms ? _submit :null,
child:const Text('Register'),
),
],
),
);
}
void _submit(){
if(_formKey.currentState?.validate()??false){
// Submit form
print('Registration successful!');
}
}
@override
void dispose(){
_usernameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
}
> Form validation should be performed on both client-side (user experience) and server-side (security), do not rely solely on client-side validation.
YouTip