The Startup
Published in

The Startup

Flutter Essentials: Forms

There are two ways to handle forms in Flutter:

Each of these could take a separate post to be explained, but I’ll try to get you started in a single post. Let’s begin.

Controllers driven fields

The first thing we’re going to see is how to have fields managed by a controller. A controller is a powerful object that helps you handle some complex scenarios with forms as well as the most simple ones, as we’re going to see.

When you have many fields, usually you don’t want to use the controller approach as the Form widget handles those cases better, even from a readability point of view. But the controller approach is handy in some situations, so it’s good to know about it.

The controller

Let’s start by declaring a controller. Its’ as easy as the following:

The TextEditingController can take a default value for your field inside the text parameter.

Next, whenever you declare a controller, you need to dispose of it, too. The best place to do so is inside the dispose function, which is called when the widget will be removed, and it’s used to release the resources.

The input field

To render an input field, you can make use of the TextField widget. It has a lot of parameters you can use to customize it; it’s worth giving a shot to the documentation.

In the example above, the following happens:

  1. the controller is linked to the TextField. You can now access the value inside the field with _controller.text;
  2. The input field is decorated with a prefix icon that can be used to suggest the meaning of the field;
  3. when the field contains a value, an “X” shaped icon appears following the field, and tapping the icon will erase the content of the field, calling _controller.clear.

The controller provides you tools to

  • access the value of the field through _controller.text;
  • change the value of the field, assigning a new value to _controller.text;
  • delete the value inside the field calling _controller.clear();
  • react to changes in the field by adding listeners with _controller.addListener((){ ...do something... });

Use example

Let’s put all together through an example:

In the example above, a list of tasks and a search field are rendered.

The items of the list are filtered by the values inserted in the field. This is achieved by adding _updateValue as a listener inside the initState method, which causes the list to be rebuilt.

In the example above, we’re reacting whenever something happens to the field, including the value changes, moving the cursor, and selecting a part of the text. if you want to listen only to changes in the value, it is better to do so in the onChanged callback of the TextField.

The Form widget

As we’ve seen so far, the controller is a powerful tool when you need to have control over your fields. Most of the time, you have multiple fields in your form, and you need to validate the fields and retrieve the data when submitting. In this case, handling all the fields through a controller may be upsetting.

Flutter comes with a Form widget that helps in handling multiple fields. Let’s see how it works.

The GlobalKey object

A GlobalKey is a unique key in your application. It is used to identify an element, and it provides access to the state of that element. We’re going to use it to identify our form and access the state of the form.

Let’s declare our GlobalKey:

Next, we need to declare the Form widget and assign the above GlobalKey:

Create the form

Let’s add fields to the form. Instead of the TextFields widget, we’ll use the TextFormField. A TextFormField is a TextField wrapped with a FormField, which makes available some methods needed by the Form widget, as we’re going to see really soon.

Validate the form

Each field comes with a callback function to check the value of the field. In case there’s something wrong with the value, you can return a string that will be shown to the user. Otherwise, return null. A field with the validation in place looks like this:

Submit the form

Here’s where our GlobalKey plays its role. With _formKey.currentState we can access the state of the form. This does not mean that we can directly access the fields' values, but it provides methods to act on the form.

When you want to submit the form, you can use the validate and the save methods of the form’s state.

The validate function will call all the validator callbacks on the fields of the form. If there’s an error, the validate will return false and rebuild the form showing the errors; otherwise, it returns true.

If there’s something wrong with the values, stop the submission.
If the values are ok, the next step is calling the save function, which will call the onSaved callback on the fields. In this callback function, you want to add the logic to process the value of the field. Usually, you add the value to a global object and then process the object elsewhere as you don’t want to mix business logic with the presentation layer.

A login form may be designed as follows:

As you can see, there’s a check on the values in the validator function, and in the onSaved callback, the value is added to a Credential object that can then be processed elsewhere.

Final words

That’s all. As I stated initially, there could be a separate post for each method shown above, but I wanted to summarize most of what you need to work with forms.

Reach out for any questions or suggestions.

Thanks for reading.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Taranjit Singh

Taranjit Singh

Graduated in computer science | working as fullstack developer