You've successfully subscribed to developer.school
Great! Next, complete checkout for full access to developer.school
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
Success! Your billing info is updated.
Billing info update failed.

Basic Form Validation with Flutter

Paul Halliday
Paul Halliday

I recently wrote an article on the Flutter Stepper widget which allows you to take a form (or similar content) and break it down into parts. If you're new to Flutter you're probably thinking... yeah, that's cool, but I have no idea how to work with forms to begin with.

Well, if this is you, we'll be remedying that by jumping into a Form example and subsequent validation.

Project setup

As always, let's create a blank project that we can use as a starting point:

$ flutter create flutter_forms && cd flutter_forms

$ code .

We can then go ahead and update main.dart to simply return an AccountPage as the home Widget at /pages/account_page.dart:

import 'package:flutter/material.dart';
import 'package:flutter_forms/pages/account_page.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: AccountPage());
  }
}

Our AccountPage will simply be a StatelessWidget that returns a StatefulWidget named AccountForm.

import 'package:flutter/material.dart';
import 'package:flutter_forms/forms/account_form.dart';

class AccountPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Account Validation'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: AccountForm(),
      ),
    );
  }
}

Let's go ahead and create the AccountForm at /forms/account_form.dart:

import 'package:flutter/material.dart';

class AccountForm extends StatefulWidget {
  @override
  _AccountFormState createState() => new _AccountFormState();
}

class _AccountFormState extends State<AccountForm> {

  @override
  Widget build(BuildContext context) {
    return Form()
  }
}

Creating a form

We'll start off by creating a uniquely identifiable key for our AccountForm of type GlobalKey<FormState>.

This allows us to reference our form in the future.

final _formKey = GlobalKey<FormState>();

Make sure you don't make the mistake of making this of type AccountFormState! :)

This also gives us the ability to call methods such as save, saving the value of each descendant FormFieldwithin our Form. We can also use reset, validate, and more - all things to look forward to!

We can now add the _formKey to our Form:

@override
Widget build(BuildContext context) {
  return Form(key: _formKey);
}

Adding a field

Next up, we'll look at adding a form field to our Form. In order to keep this as simple as possible, we'll stick with one field responsible for a person's fullName.

  @override
  Widget build(BuildContext context) {
    return Form(
        key: _formKey,
        child: Column(
          children: <Widget>[
            TextFormField(
              decoration: InputDecoration(
                  hintText: 'Name',
                  helperText: 'This has to be over two characters in length.'),
            ),
          ],
        ));
  }

Adding validation

Sweet. We've now got a field with the placeholder text of Name. We can go ahead and add some validationrules to this:

TextFormField(
  validator: (value) {
    if (value.isEmpty) {
      return "You can't have an empty name.";
    }

    if (value.length < 2) {
      return "Name must be more than one character.";
    }
  },
  decoration: InputDecoration(
      hintText: 'Name',
      helperText: 'This has to be over two characters in length.'),
),

For our example application we're making the arbitrary decision that a name can't be lower than two characters. Change this to match the validation rules of your particular form field.

Determining if a Form is valid

Next, let's see if a Form is valid by checking the validate method when a RaisedButton is clicked:

  @override
  Widget build(BuildContext context) {
    return Form(
        key: _formKey,
        child: Column(
          children: <Widget>[
            TextFormField(
              validator: (value) {
                if (value.isEmpty) {
                  return "You can't have an empty name.";
                }

                if (value.length < 2) {
                  return "Name must be more than one character.";
                }
              },
              decoration: InputDecoration(
                  hintText: 'Name',
                  helperText: 'This has to be over two characters in length.'),
            ),
            RaisedButton(
              onPressed: () {
                _formKey.currentState.validate()
                    ? Scaffold.of(context)
                        .showSnackBar(SnackBar(content: Text('This is valid!')))
                    : Scaffold.of(context)
                        .showSnackBar(SnackBar(content: Text('Not valid!')));
              },
              child: Text('Submit'),
            )
          ],
        ));

This gives us the following results:

Form Validity

Accessing form values

Earlier in the article I mentioned the use of a save method. Let's look at how we can add an onSaved event to our TextFormField and update a local variable such as _name:

Firstly, we declare a variable such as name at the class level:

class _AccountFormState extends State<AccountForm> {
  String _name;
}

Next, on our TextFormField itself we can add the onSaved which returns the entered value as a String:

TextFormField(
  onSaved: (String val) => setState(() => _name = val),
);

We're using this to update the value of _name and can show it on our screen like this:

Text('Name: $_name')

We can now run save whenever our form is valid, checking the validity first with validate:

RaisedButton(
  onPressed: () {
    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      Scaffold.of(context)
          .showSnackBar(SnackBar(content: Text('This is valid!')));
    } else {
      Scaffold.of(context)
          .showSnackBar(SnackBar(content: Text('Not valid!')));
    }
  },
  child: Text('Submit'),
),

Here's what this gives us:

Form validation: Save

Auto validation

What if we wanted to validate the form without having to press Submit? Turns out we can do that with a nifty flag named autovalidate:

return Form(
  key: _formKey,
  autovalidate: true,
)

Now any time we start typing, the form is automatically validated!

Summary

We've learned the basics of Form validation with Flutter. Using this new knowledge, go forth and validate!

Flutter

Paul Halliday

πŸ‘‹ Want to see more content? Head over to the YouTube channel: https://youtube.com/c/paulhalliday!