Form validation in Flutter

Requesting input from users and validating it is one of the core functionalities of a mobile application.

Blog Cover Image
Vaishnavi Ambidi

Vaishnavi Ambidi

Calender

November 01, 2022

Forms.

Requesting input from users is one of the core functionalities of a mobile application. A good example would be the login or registration of the user. For this, users generally need to enter their email addresses along with their passwords. To ensure that the user enters valid information, Flutter offers a widget called “Form”, We use forms that serve the following purposes:

  • Check whether the information which the user has provided is valid.
  • To collect specific data from users, For example, the user’s name, email, address, phone number, date of birth, etc..
  • Displays a friendly error message letting them know what went wrong.

Building a form

Building a form and adding validation will take following three steps:

  1. Create a form with a global key
  2. Add Input Widgets with validation logic
  3. Create a button to validate and submit the form

1) Create a form with a global key

To create a form we must provide a global key. But what is a global key? The global key is used to uniquely identify the Form and allows validation of the form in a later step. It is declared as follows.

final _formKey = GlobalKey<FormState>();

What is validation and how does it actually work?

Validations are the checkpoints for the data to be collected from the user. For example, if we want to collect some data about a person, let’s say his email or phone number. These validations ensure that person entered a valid email, and phone number using validate method If the user enters any invalid input, then the Validator function in flutter will detect it & return an appropriate error message.

2) Add Input widgets with validation logic

There are two frequently used input widgets in flutter that are as follows.

  • TextFormField
  • DropdownFormField

TextFormField

It is used to take input from the user in a flutter. We can perform any operation on that user input data using TextFormField. TextFormField widget is especially used in the app to store the data input by the user while using the app. Such as when users register their accounts to the app. Basic Example of TextformField. sample-form

These are the following properties in the textformfield widget.

autovalidateMode

It is used to validate the input as soon as we enter the data. It has three values as follows:

  1. AutovalidateMode.always: It will auto-validate even without user interaction.
  2. AutovalidateMode.disabled: It is used to disable/stop auto-validation.
  3. AutovalidateMode.onUserInteraction: This value is used to auto-validate only after its content changes,i.e. If the user has made any changes.

Controller

When working with a TextFormField you can define a TextEditingController to get information about the current value, or perhaps clear the text from the textfield using TextEditingController.clear(). Also used to control the text being edited.

obscure text

We can use this property if we want to hide the input, as we do in the case of the password.

keyboardType

It is used to specify the keyboard for different types of fields for example for phone number we use TextInputType.phone, and for the email we use TextInputType.emailAddress,

onSaved

It is used to assign the input text to the variable of that specific field Like in this case, we are assigning the value to the _name variable as shown below.

onSaved: (String value) {
   _name = value; 
},

Decoration

As you can see we have a simple TextFormField widget. By default when you add a TextFormField you will get the material styling. however, if you want to make it more customized then you can use inputdecoration in the decoration property which provides you with the following options.

Icon

It is used to add an icon to the input field as shown in the below image,

Hint Text

It displays the hint text inside the input area to help the user understand what input is required,

Helper Text

It displays the helper text.

Counter Text

It displays the count of the characters in the input, which is displayed below the input area.

Border

It is used to add a border to the input field.

Prefix

It displays Prefix before the input area.

Suffix

Suffix after the input area.

Following are outputs of Icon, hint text, helper text, counter text, prefix, and suffix.

image

Validator This property validates an input. Returns an error string to display if the input is invalid, Here you can see that we are returning “Please enter a valid email address” if an email does not match the regular expression else we are returning null.

validator: (String email) {
bool isEmailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(email);
   if(!isEmailValid )
     return 'Please enter a valid email address;
   else
     return null;
 },

Let’s see different error messages for different conditions. image

Let’s see all the code:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  AutovalidateMode _autoValidate = AutovalidateMode.disabled;
  String? _name;
  String? _mobile;
  String? _email;
  void _validateInputs() {
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();
    } else {
      setState(() {
        _autoValidate = AutovalidateMode.always;
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Form Validation'),
        ),
        body: SingleChildScrollView(
          child: Container(
            margin: const EdgeInsets.all(15.0),
            child: Form(
              key: _formKey,
              autovalidateMode: _autoValidate,
              child: formUI(),
            ),
          ),
        ),
      ),
    );
  }

  Widget formUI() {
    return Column(
      children: <Widget>[
        TextFormField(
          decoration: const InputDecoration(labelText: 'Name'),
          keyboardType: TextInputType.text,
          validator: validateName,
          onSaved: (String? val) {
            _name = val;
          },
        ),
        TextFormField(
          decoration: const InputDecoration(labelText: 'Mobile'),
          keyboardType: TextInputType.phone,
          validator: validateMobile,
          onSaved: (String? val) {
            _mobile = val;
          },
        ),
        TextFormField(
          decoration: const InputDecoration(labelText: 'Email'),
          keyboardType: TextInputType.emailAddress,
          validator: validateEmail,
          onSaved: (String? val) {
            _email = val;
          },
        ),
        const SizedBox(
          height: 10.0,
        ),
        OutlinedButton(
          onPressed: _validateInputs,
          child: const Text('Validate'),
        )
      ],
    );
  }
  String? validateName(String? value) {
    if (value!.isEmpty) {
      return 'Name cannot be empty';
    }
    if (value.length < 3) {
      return 'Name must be more than 2 charater';
    } else {
      return null;
    }
  }

  String? validateMobile(String? value) {
    if (value!.isEmpty) {
      return 'Phone number cannot be empty';
    }
    if (value.length != 10) {
      return 'Mobile Number must be of 10 digit';
    } else {
      return null;
    }
  }

  String? validateEmail(String? value) {
    String pattern =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
    RegExp regex = RegExp(pattern);
    if (value!.isEmpty) {
      return 'Email cannot be empty';
    }
    if (!regex.hasMatch(value)) {
      return 'Enter Valid Email';
    } else {
      return null;
    }
  }
}

DropdownButtonFormField

It is the Flutter widget that is the combination of the dropdown widget and a formfield. Which takes a list of items/values and displays them in the dropdown, This widget has similar properties as textfromfeild.

The following are the properties of a dropdown button form field.

Value

It is the property that keeps the track of selected values from the dropdown. In our case, we name it “_selectedValue”.

Hint

It is a property that takes a widget, which is used to display an Icon or text In this example we used Text Widget which says” Choose One”.

onChanged

This is a required property that is used for changing the values in the dropdown. It is called when the user selects an item/value from the dropdown.

onSaved

It is used to assign the selected value of the dropdown to the variable Like in this case, we are assigning the value to the _selectedValue variable as shown below.

 onSaved: (value) {
   setState(() {
     _selectedValue = value;
   });
},

validator

This property validates the dropdown. And returns an error string to display if the input is invalid, Here you can see that we are returning “Please choose the value from the dropdown” if an email does not match the regular expression else we are returning null.

validator: (String? value) {
                      if (value == null || value.isEmpty)           {
   return "Please choose the value from the dropdown";
                      } else {
                        return null;
                      }
                    },

dropdown-validator

Items

This property takes a list of items/values( in this example we are passing a list of String) and displays the values in the dropdown using a text widget, By mapping each value of that list into dropdownmenuitem as shown below.

items: listOfValue.map((String val) {
                      return DropdownMenuItem(
                        value: val,
                        child: Text(
                          val,
                        ),
                      );
                    }).toList(),

Here’s the code for DropDownButtonFormField.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  AutovalidateMode _autoValidate = AutovalidateMode.disabled;
  String? _selectedValue = '1';
  List<String> listOfValue = ['1', '2', '3', '4', '5'];
  void _validateInputs() {
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();
    } else {
      setState(() {
        _autoValidate = AutovalidateMode.always;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Form Validation'),
        ),
        body: SingleChildScrollView(
          child: Container(
            margin: const EdgeInsets.all(15.0),
            child: Form(
              key: _formKey,
              autovalidateMode: _autoValidate,
              child: Column(
                children: [
                  DropdownButtonFormField(
                    value: _selectedValue,
                    hint: const Text(
                      'choose one',
                    ),
                    isExpanded: true,
                    onChanged: (value) {
                      setState(() {
                        _selectedValue = value;
                      });
                    },
                    onSaved: (value) {
                      setState(() {
                        _selectedValue = value;
                      });
                    },
                    validator: (String? value) {
                      if (value == null || value.isEmpty) {
                        return "Please choose the value from the dropdown";
                      } else {
                        return null;
                      }
                    },
                    items: listOfValue.map((String val) {
                      return DropdownMenuItem(
                        value: val,
                        child: Text(
                          val,
                        ),
                      );
                    }).toList(),
                  ),
                  OutlinedButton(
                    onPressed: _validateInputs,
                    child: const Text('Validate'),
                  ),
                  Text("Selected Value: ${_selectedValue ?? 0}")
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

These were the properties of the DropDownButtonFormField and TextFormField. However, you could be wondering at this point where to call the validators or how those conditions operate. Well, the answer to that question is in the next step…

3) Add the validation method and submit the form.

Let’s create a method called _validateInputs to check the validation of the fields which is used to submit the form. There is a method called bool validate() defined under the form class which is used to validate the form. As soon as method bool validate() is called all the validators will be called. If it does get any string then it will show that string as an error and if it does not get any string means no Error. Suppose there is no error then we will call the method void save() and it will call the onSave property. So, that all the form input could be stored in a variable corresponding to each input Field.

void _validateInputs() {
 if (_formKey.currentState.validate()) {
   _formKey.currentState.save();
 } else {
   setState(() {
      _autoValidate = AutovalidateMode.always;
   });
 }
}

These are the examples of dropdownbuttonformfield after successful validation dropdown-button