In this example we need to look at how to build a form within Flutter.
Forms are important if we want to get information from our users to send to a server so it can be used in other areas. Our first step will be to build the generic app, and then remove most everything below the main function.
We’ve built this generic Flutter app before, and you can find out how here if you don’t remember. For my example code, I’m going to create a project called form_example.
To clean out the code, we are going to remove most of the content. We will remove everything Stateful Widget that it creates, which eliminates most of our code.
Now under MyApp you will have an error a home: MyHomePage
cannot work, as you do not have a Widget MyHomePage. Therefore, we will make a change that you see below. This will create the MaterialApp like you see below:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
const appTitle = 'Form Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: null, // TODO: Apply the Widget Holding the Form here
),
);
}
}
If you launch this, you won’t see anything besides the title bar, as the body is null, and there is nothing to display there. We are going to build a Widget to hold our form and display it in the body now.
Adding the Form Widget
We will create a Stateful Widget to hold our form. This is so it can keep our data as changes are made to the screen. This will be applied to our application which we will define.
As part of this widget, we will need also create a GlobalKey
. A GlobalKey will uniquely identify the form and allow validation when we do that in a later step.
You could create this in a separate file, like we demonstrated earlier, but for simplicity sake, I’m going to keep it all in one file.
class MyForm extends StatefulWidget {
const MyForm({Key? key}) : super(key: key);
@override
MyFormState createState() {
return MyFormState();
}
}
// Define a corresponding State class to hold data related to the form.
class MyFormState extends State<MyForm> {
// The global key that uniquely identifies the Form widget
// and allows validation of the form.
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
children: <Widget>[
// TODO: Add Form Elements here.
],
),
);
}
}
As you can see, we have the Stateful Widget and the associated State objects which will old the form.
Note: May IDEs look for TODO as a way to remind of us things we will need to add to our code later. When we add them, we remove them, like crossing an item off a check list.
Next, where the TODO is, we will add a Textfield
, and an ElevatedButton
.
Now the build method would look like the following:
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
/* TODO add form validation info */
),
ElevatedButton(
onPressed: null, // TODO need an event handler here
child: const Text('Submit'),
),
],
),
);
}
Notice that we removed the TODO to add the form elements, but added a couple as our form fields are there, but not complete. While we’ve created the base form, we are not displaying it because it isn’t showing in the MyApp. The home property’s scaffold has the body still pointing to null, so let’s change that real quick.
The body property should be const MyForm()
. With that, a hot reload will have the form fields displaying on the screen. At this time the ElevatedButton will appear disabled, and we’ll need to add info to the TextFormField before we finish as well.
Coding the ElevatedButton
We need to code the ElevatedButton onPress event handler. Right now it is null, because we have to have it, but it doesn’t do anything. We’re going to create an anonymous function which will check the form validation for us.
() {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar.
// In the real world, you'd call a server or save the info
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
First we check to see if the GlobalKey, _formKey, is the current State is validated. If so, it will display a message. This wouldn’t normally happen in a real world environment, as we’d pass along the validated data to a server, or method to work with the data in some other manner.
Coding the TextFormField
If you hover over the TextFormField, you will see you have a lot of possible attributes you can set when you create it, such as initialValue, style, readOnly, etc. What we will want to do is to use the validator property. This is an event handler listening for a validate event. When we call the validate() method for _formKey, the Global Key, it performs this event handler, and checks to see if it validates.
Form field validation can take on lots of forms, in this case, we’re just making sure that the field isn’t left blank.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
From this, you can see that value is going to be passed in, and it is the value in the form field. It is checking to make sure the string is not empty or null, if it is, it returns some corrective text which will appear in the form field and prevent it from continuing to submit the form.
At this point, you can save the document, and test the form field validation by clicking on the submit both with and without content inside the field.
Enhancing the TextFormField
Unfortunately, one of the issues with the TextFormField out of the box is that it can easily fade into the form as it only has an underline for the field. That doesn’t have to be however as you can apply styles to the form fields.
Additionally, you can add labels, and other features to make it more user friendly.
Each change should be built around either, making the form easier to use for the end user, or making it fit within your design goals. Preferably it should be both.
Let’s look at enhancing the TextFormField.
Adding a Label
The first thing we’re going to do, is add a label. To do this we will add a property to our TextFormField constructor, the decoration
property.
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Enter your username'
),
Here we applies an InputDecoration
, which allows us to add a labelText
and a bottom border, or basically keep the border we did have.
Adding Spacing
The form fields are right on top of one another. So adding a Padding object can be used to provide separation. In the example below, we’ll add separation top and bottom, but leave it left and right.
Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 16),
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Enter your username'),
),
),
Notice how the TextFormField is just used as the child of Padding, and Padding takes it place among the children of the parent Widget.
Setting AutoFocus
You can also set the focus of a form field automatically. To do this, set the property autofocus
to true, like you see below in the form field’s constructor.
autofocus: true
Building a Form in Flutter was originally found on Access 2 Learn
3 Comments
Comments are closed.