Handling Forms in Flutter: Form Validation & Saving In Flutter

Haris Bin Nasir Avatar

·

·

Forms are an integral part of many applications, and handling them efficiently can significantly enhance user experience. In this comprehensive guide, we will explore how to work with form within Flutter—a popular open-source UI software development toolkit. We will cover creating a form, managing its state, validating different fields, and saving form data. Additionally, we’ll implement a registration page example where users can input their email, phone number, and password. The form will only proceed when the given information is in the correct format.

If you prefer watching a video tutorial here is a link to that.

Setting Up the Project

Creating an Empty Project

To get started with our form handling app, initialize a new Flutter project. If you’ve already got Flutter installed, you can set up a new project using the command:

flutter create form_handling_example

Navigate into your project directory:

cd form_handling_example

Initializing the HomePage Widget

Creating a Stateful Widget

Inside your lib directory, create a new Dart file named homepage.dart. Here, define a stateful widget to represent our home page:

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Registration Page'),
      ),
      body: buildUI(),
    );
  }

  Widget buildUI() {
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          // Further implementation will come here
        ),
      ),
    );
  }
}

Returning an Empty Scaffold

We initialize our HomePage widget with an empty Scaffold which serves as a starting point for our layout.

Setting HomePage as Home in main.dart

Now, open main.dart, and set the HomePage widget as the home property in your MaterialApp:

import 'package:flutter/material.dart';
import 'homepage.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

Building the UI

Adding an AppBar

The AppBar is already added in the previous step to help navigate within the application. It holds the title ‘Registration Page’.

Defining the Body

Calling the buildUI Function

Within the body parameter of the Scaffold, we call the buildUI function which returns a SafeArea widget. This function aims to protect our content from any operating system interface intrusions, such as the notch or the status bar.

Defining the buildUI Function

Returning a SafeArea Widget

This widget ensures that our UI layout respects the safe areas of different devices maintaining a more uniform look.

Adding a Padding Widget

We nest a Padding widget inside the SafeArea for consistent spacing within our form.

Adding a Form Widget

Within the Padding widget, we add a Form widget which helps in grouping the form fields and managing their state.

child: Form(
  key: _formKey,
  child: Column(
    children: [
      // Form fields will be added here
    ],
  ),
)

Managing Form State

Creating a GlobalKey

Defining a FormKey Variable

We need a GlobalKey to manage the state of our form. Inside your _HomePageState, define a form key:

final _formKey = GlobalKey<FormState>();

Initializing the FormKey

Initialize the form key and link it with the Form widget as shown above.

Linking the Form with the FormKey

By setting the key property of the Form widget to _formKey, we can manage the form’s state throughout its lifecycle.

Adding Form Fields

Creating a Reusable Widget

To simplify our code and avoid redundancy, we create a reusable form field widget.

Creating a utils/widgets Folder

Create a new directory structure lib/utils/widgets to hold our custom widgets.

Creating a CustomFormField Widget

Inside lib/utils/widgets, create a new Dart file named custom_form_field.dart and define the CustomFormField stateless widget:

import 'package:flutter/material.dart';

class CustomFormField extends StatelessWidget {
  final bool obscureText;
  final String hintText;
  final FormFieldValidator<String> validator;
  final FormFieldSetter<String> onSaved;

  CustomFormField({
    this.obscureText = false,
    @required this.hintText,
    @required this.validator,
    @required this.onSaved,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextFormField(
        decoration: InputDecoration(
          hintText: hintText,
        ),
        obscureText: obscureText,
        validator: validator,
        onSaved: onSaved,
      ),
    );
  }
}

Using the CustomFormField Widget

Adding CustomFormField as a Child to the Column

Return to homepage.dart and add instances of CustomFormField within the column inside the Form widget:

child: Column(
  children: [
    CustomFormField(
      hintText: 'Email',
      validator: (value) {
        if (value.isEmpty || !value.isValidEmail) {
          return 'Please enter a valid email';
        }
        return null;
      },
      onSaved: (value) {
        _email = value;
      },
    ),
    CustomFormField(
      hintText: 'Password',
      obscureText: true,
      validator: (value) {
        if (value.isEmpty || value.length < 8) {
          return 'Password must be at least 8 characters';
        }
        return null;
      },
      onSaved: (value) {
        _password = value;
      },
    ),
    CustomFormField(
      hintText: 'Phone',
      validator: (value) {
        if (value.isEmpty || !value.isValidPhone) {
          return 'Please enter a valid phone number';
        }
        return null;
      },
      onSaved: (value) {
        _phone = value;
      },
    ),
    ElevatedButton(
      onPressed: _validateAndSave,
      child: Text('Register'),
    ),
  ],
)

Customizing the CustomFormField Widget

Adding Padding

Padding is already added around each TextFormField for spacing.

Using a TextFormField

The TextFormField is configured to use validators and form state management mechanisms.

Adding an obscureText Parameter

This parameter ensures secure text entry, suitable for password fields.

Adding a hintText Parameter

The hintText parameter provides contextual information about what data each field expects.

Updating the HomePage to Use the Parameters

Each form field now has custom validation logic and hint text to guide users.

Form Validation

Adding a Register Button

Creating an ElevatedButton

Add a button that users can press to submit the form.

Defining the onPressed Callback

Calling the validate() Method on the Form

Define the validation logic to ensure the form fields meet specified criteria:

void _validateAndSave() {
  if (_formKey.currentState.validate()) {
    _formKey.currentState.save();
    // Handle successful form submission
  }
}

Defining Validation Logic

Creating an extensions Folder

Create a directory lib/utils/extensions.

Creating a StringExtensions File

Inside extensions, create a file named string_extensions.dart:

extension StringExtensions on String {
  bool get isValidEmail {
    final RegExp emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+');
    return emailRegex.hasMatch(this);
  }

  bool get isValidPassword {
    return this.length >= 8;
  }

  bool get isValidPhone {
    final RegExp phoneRegex = RegExp(r'^[0-9]{10}$');
    return phoneRegex.hasMatch(this);
  }
}
Using Regular Expressions

Regular expressions are used to match specific string patterns for validation.

Adding a Validator Parameter to CustomFormField

The validator parameter has already been added to our CustomFormField.

Implementing Validators for Each Field in HomePage

The validation for each field is implemented using the StringExtensions.

Saving Form Data

Creating Variables to Store Form Data

Within _HomePageState, define variables to store the form data:

String _email, _password, _phone;

Adding an onSaved Parameter to CustomFormField

The onSaved parameter has been added to handle saving input data.

Implementing onSaved Callbacks

Saving Email Data

Save the email data to _email:

onSaved: (value) {
  _email = value;
}

Saving Password Data

Save the password data to _password:

onSaved: (value) {
  _password = value;
}

Saving Phone Data

Save the phone data to _phone:

onSaved: (value) {
  _phone = value;
}

Calling the save() Method on the Form

Invoke the save method in _validateAndSave:

_formKey.currentState.save();

Printing the Saved Data

Print the saved data to the console for debugging:

print('Email: $_email, Password: $_password, Phone: $_phone');

Additional Features

Resetting the Form

Occasionally, you may want to provide a means to reset the form fields.

Using the reset() Method on the FormKey

Add a reset button to the UI:

ElevatedButton(
  onPressed: () {
    _formKey.currentState.reset();
  },
  child: Text('Reset'),
),

Get Source Code for free:

Conclusion

Managing form in Flutter can be achieved efficiently with the Form widget. This comprehensive guide has walked you through setting up a form, managing its state, validating inputs, and saving form data. By creating reusable form field widgets and leveraging extensions for validation, we’ve built a robust registration page that ensures data integrity. By mastering these techniques, you can significantly improve form handling in your Flutter applications.

Happy Coding…!

Leave a Reply

Your email address will not be published. Required fields are marked *