Leave a rating/review
This video Work with Forms was last updated on Nov 9 2021
Forms are very essential in almost every app. Flutter gives us various ways to handle user inputs. We can manually handle inputs using various techniques but in this episode, we’ll be using the Form
widget which is more robust. I’ve linked the FAB to navigate to the CreateArticle
page.
This is included in the starter project of this episode.
Before we start coding the form, we need a way to hold the data that is being entered in the form. For this, i’ve provided the FormData model which is stored inside the models folder. Go ahead and open it up. It has all the values we intend collecting from the form. Normally, we should have used the Article
mode. But it contains only strings and that’s kind of limiting for this example since we would be covering other input widgets. And that’s why the custom FormData
model was created.
Let’s go ahead and make use of it. Head back to the create article page and enter the following code:
...
FormData formData = FormData();
...
This creates a formData object that would hold our form field values. Next, let’s create a basic form. Paste in the following code:
body: Padding(
padding: const EdgeInsets.all(16),
child: Form(
child: ListView(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(labelText: 'Title'),
validator: (value) =>
value!.isEmpty ? 'Please enter Title' : null,
onSaved: (value) => formData.title = value,
),
ElevatedButton(
onPressed: () {},
child: const Text('Save'),
)
],
),
),
),
Save your work. And you can see the corresponding UI. We returned a Form
which contains a TextFormField
and a ElevatedButton
. A Form
is simply a Container
for form fields just like the one we added. You’ll see the benefit of using a Form
shorthly.
A TextFormField
is a FormField
that contains a TextField
. With this widget, you get a TextEditingController
automatically. If you used a TextField
, you would have to manually connect a TextEditingController
to retrieve its value.
We set its label text in the InputDecoration
class. This class is used to style inputs with borders, icons, hint text and other styling attributes. Also, we added a validator. The validator callback function checks to see if the text is empty. If it is empty, the validation fails and returns an error message. If there are no errors, the validator must return null.
Next, the onSaved
method is called when we submit the form. Here, set the of the formData
’s title to the input value. Now, you might ask: How do we submit the form? Well, that leads us to the next stage.
The form needs a key. Keys are used in Flutter to uniquely identify widgets and they contain some information. We would be using a GlobalKey
to store the state of the Form. GlobalKey
s are unique across the entire app. Okay, let’s add it now:
...
final _formKey = GlobalKey<FormState>(); // As class member
...
child: Form(
key: _formKey,
...
In here, we created a GlobalKey
of type FormState
. A FormState
object can be used to save, reset and validate every FormField
that is inside the corresponding form.
Next, we assign the _formKey
to the key
property of the Form
widget. Let add the code to save the Form
when we hit the save button. Add the following code to the onPressed
method of the button:
...
onPressed: () {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
print(formData);
form.reset();
}
FocusScope.of(context).unfocus();
},
...
Save your work after the update. We get the currentState
of the Form
from the formKey
variable. Then we check to see if the form is valid. The validate method, executes the validator of each FormField
. It returns true if every FormField
is valid. The form would then rebuild to report its result. This is possible, because the Form
is a StatefulWidget
.
If the form is valid, then we call the save method and this calls the onSaved
method for all the FormField
s that is associated with the Form. And the onSaved
method saves the value of the textfield to the formData
object. And we can see that from the TextFormField we created earlier.
Next, we print the formData
to the debugConsole and finally, we clear the form by calling the reset method. You see, all these bulk operations are possible because of we’re using the Form widget which has the necessary functionalities to process a inputs effectively. And the last line programmatically hides the keyboard when you press the submit button. Okay, enough talk, let’s try it out.
First, let’t submit the form with an empty title field. You can see the validator kicks in and declares the form invalid.
And shows the corresponding error message. Now, let’s add some text. And we submit the form. You can see the TextField
is cleared and let’s check the console to see if it prints out the value. Open up the debug console by pressing Ctrl + Shift + Y
on windows or Shift + Cmd + Y
for mac users.
And you can see it prints out an instance of the FormData class. Now we got the form setup with the title field, let’s implement other fields. I’ll update this file and explain the new additions.
I’ve added other form fields here. Some of them are very similar to the title text. I also added some custom validation, although in a production app, you’ll write better validations or use a library. Let’s go over the easy ones first. This is the image url text form field.
And its validator simply checks to see if the text entered is an actual url.
The content validator check if the field is empty or has a length less than 10. The email validator calls a function that validates the email.
Let’s scoll up to that function.In this method, i declared a string pattern that conforms to the format of an email address. Next, we pass this string to a RegularExpression. We check to to see if the value entered matches the regular expression pattern. If it doesn’t match, we return an error message. If it does, we return null to signify the value was valid. And by the way, i spent 2 years memorizing this string pattern. It’s very difficult but doable.
Okay seriously, you can find regular expression patterns all over the internet. I copied this one from the internet. Next, we have the SwitchListTile
widget. It is a Switch
widget with some additional styling to make it look like a ListTile
. This widget does not manage its state like the TextFormField
so we manually call setState
to update the formData
with its value when the user toggles the switch. And we do this in the onChanged
callback function.
Next, is the RadioListTile
widget. These are radio buttons where the user selects only one value from the group. Just like the SwitchListTile
, it does not manage its state so we use setState
when the radio button is selected. And one property to take note of is the groupValue
. You assign both buttons the same groupValue
variable. This links the two radio buttons together so that you can only select one of them.
Now that we’re done with all the explanations, we need to fill the form and try it out. But before that, we need to reset the state of both the Switch
and the Radio
button to their initial values since they are not linked to the form’s state.
Enter the following code in the onPressed
method:
...
setState(() {
formData.isBreaking = false;
formData.category = null;
});
Save your work. And let’s fill up the form. Then you click on the save button. And you have the form values saved successfully and the form reset. Open up your debugConsole to see the values. And you can see the formData object has all the form values.