Form validation in Flutter can be implemented using simple regex validators as shown in provided application
Text validators can be implemented as functions of type FormFieldValidator
which return a string in case of validation error or null if validated
Simple example which validates if input string contains at least one non-space character
static FormFieldValidator<String> validateNotEmpty(String input) {
if (input != null && input.trim().length > 0)
return null;
return "Error: Null/Empty string"
}
Check Flutter installation to setup Flutter
Use flutter create
command to create a new Flutter project (here form_validation_app) :
flutter create form_validation_app
Create a class variable of type GlobalKey<FormState>
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
Declare a Form widget and assign key as declared variable
Use one or more widgets containing validator
property as child widget(s) of Form
new Form(
key: this._formKey,
child: new TextFormField(
keyboardType: TextInputType.emailAddress, // Use email input type for emails.
decoration: new InputDecoration(hintText: 'email', labelText: 'Email'),
validator: Validator.validateEmail(),
onSaved: (String value) { this.userData.email = value; }
),
)
Declare a function to check whether value entered for each child widget is valid and save form state using currentState
of key variable
void submit() {
if (this._formKey.currentState.validate()) {
_formKey.currentState.save();
}
}
Assign function as callback to a button denoting Submit
new RaisedButton(
child: new Text('Validate', style: new TextStyle(color: Colors.white),),
onPressed: this.submit,
color: Colors.blue,
)
A validateRegex
function is declared with parameters regex
, errorMsg
and a boolean not
It returns a new function which takes an argument value
and matches it with the regex pattern
If boolean not
is false, then it checks for presence of regex pattern in value
and returns error message if not present
If boolean not
is true, then it returns error message if pattern is present
static FormFieldValidator<String> validateRegex(String regex, String errorMsg, bool not) {
return (value) {
if (RegExp(regex).hasMatch(value))
return not? errorMsg:null;
return not? null:errorMsg;
};
}
static FormFieldValidator<String> validatePassword() {
return (value) {
Function validate = validateRegex(r"[A-Z]+", "Password should contain an uppercase character", true) ;
String err = validate(value);
if(err == null) {
validate = validateNotRegex(r"[#]+", "Password should not contain #", false);
err = validate(value);
}
return err;
};
}
A function validatePassword()
can be declared which also returns a function and uses validateRegex()
to check for two patterns for input value
Later validatePassowrd()
method can be used as validator
for a TextFormField
new TextFormField(
obscureText: true, // To display typed char with *
decoration: new InputDecoration(
hintText: 'Password',
labelText: 'Enter your password'
),
validator: validatePassword(),
onSaved: (String value) { this.userData.password = value; }
)
Similarly more patterns can be added such that subsequent patterns are checked only if previous validation returns null
Following example app code contains function to take a list of validateRegex()
methods to validate different fields like username, email, password
import 'package:flutter/material.dart';
import 'package:validate/validate.dart';
import 'dart:convert';
import 'dart:async';
import 'dart:typed_data';
void main() => runApp(new MaterialApp(
title: 'Nc Form Validation',
home: new LoginPage(),
));
class UserData {
String username = '';
String email = '';
String password = '';
}
class LoginPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
UserData userData = new UserData();
void submit() {
if (this._formKey.currentState.validate()) {
_formKey.currentState.save();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => UserPage(userData),),
);
}
}
@override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
return new Scaffold(
appBar: new AppBar(title: new Text('Login'),),
body: new Container(
padding: new EdgeInsets.all(20.0),
child: new Form(
key: this._formKey,
child: new ListView(
children: <Widget>[
new TextFormField(
keyboardType: TextInputType.emailAddress, // Use email input type for emails.
decoration: new InputDecoration(hintText: 'email', labelText: 'Email'),
validator: Validator.validateEmail(),
onSaved: (String value) { this.userData.email = value; }
),
new TextFormField(
obscureText: true, // To display typed char with *
decoration: new InputDecoration(
hintText: 'Password',
labelText: 'Enter your password'
),
validator: Validator.validatePassword(),
onSaved: (String value) { this.userData.password = value; }
),
new TextFormField(
decoration: new InputDecoration(hintText: 'ign', labelText: 'Username'),
onSaved: (String value) { this.userData.username = value; },
validator: Validator.validateUsername(),
),
new Container(
width: screenSize.width,
child: new RaisedButton(
child: new Text('Validate', style: new TextStyle(color: Colors.white),),
onPressed: this.submit,
color: Colors.blue,
),
margin: new EdgeInsets.only(top: 20.0),
),
],
),
),
),
);
}
}
class UserPage extends StatelessWidget {
UserData userData = new UserData();
UserPage(this.userData);
@override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
return new Scaffold(
appBar: new AppBar(
title: new Text('Next page'),
),
body: Center(
child: new Container(
height: 90,
color: Colors.teal[400],
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Next page of previous page"),
Text("Initially configured to be navigated into after validation"),
Text("Password ${userData.password}"),
],
),
),
),
);
}
}
class Validator {
static FormFieldValidator<String> _validateRegex(String regex, String errorMsg, bool not) {
return (value) {
if (RegExp(regex).hasMatch(value))
return not? errorMsg:null;
return not? null:errorMsg;
};
}
static FormFieldValidator<String> validateRegex(String regex, String errorMsg) {
return _validateRegex(regex, errorMsg, false);
}
static FormFieldValidator<String> validateNotRegex(String regex, String errorMsg) {
return _validateRegex(regex, errorMsg, true);
}
static FormFieldValidator<String> validate(List<FormFieldValidator<String>> validators) {
return (value) {
value = value.trim();
for (final validator in validators) {
final validateMsg = validator(value);
if (validateMsg != null)
return validateMsg;
}
return null;
};
}
static FormFieldValidator<String> validateEmail() {
return validate([
validateRegex(r".+", "Email should not be empty"),
validateRegex(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$",
"Invalid email format")
]);
}
static FormFieldValidator<String> validatePassword() {
return validate([
validateRegex(r".+", "Password should not be empty"),
validateRegex(r".{6}", "Password should be minimum 6 characters"),
validateRegex(r"^.{6,12}$", "Password should be maximum 12 characters"),
validateRegex(r"[A-Z]+", "Password should contain an uppercase character"),
validateRegex(r"[a-z]+", "Password should contain a lowercase character"),
validateRegex(r"[0-9]+", "Password should contain a digit"),
validateRegex(r"[\.!$%&\'*+/=?^_`{|}~\-:;@]+", "Password should contain minimum one of !\$%&\'*+/=?^_`{|}~\-:;@"),
validateNotRegex(r"[#]+", "Password should not contain #"),
]);
}
static FormFieldValidator<String> validateUsername() {
return validate([
validateRegex(r".+", "Username should not be empty"),
validateRegex(r"^username$", "Username should be username")
]);
}
}
Ensure a supported device is connected or emulator/simulator is started
Go to project directory
Use flutter run
command to run
flutter run
It builds and runs app on an available android/ios device