Obum
Posted on January 19, 2022
What we will be building ...
A one-screen flutter app containing a form. The form can be used to create/update a question and its options (and the correct option too). This interface is for setting questions that could be used elsewhere like an online quiz. The whole question data can then be saved to a database like Firebase Firestore. The screen looks like this:
Prerequisites
- Have basic understanding of flutter and probably programming as a whole.
- Have installed flutter and its working.
Steps
1. Setup
Initialize the flutter project by running the following command in your terminal
flutter create questions
2. Coding
a. Open the project in your favorite editor.
b. Delete the contents of lib/main.dart
file, paste the following code, and save the file.
3. Run
Run the code with the following command
flutter run
The app should run in your attached device and it should show something similar to the above UI.
More on the Question Form
Iteration/Looping
It is good practice to make code short or DRY (Don't Repeat Yourself). The code for the "correct option radio buttons" and the "TextFormFields for the option values" are the same. For these two sections of Widget
s, we are looping over the list of options
const options = ['A', 'B', 'C', 'D'];
to return each necessary Widget.
The Radio
, Text
(for each option), and spacer (const SizedBox(width: 16)
) are been repeated together, reason why we had to call the expand
method on Iterable
to expand the list of lists been returned. Same is applicable for the TextFormField
for each option's value and its own spacer (const SizedBox(height: 32)
) as they are been returned together too in the map
function.
\\ ... ,
const Text('Correct Option'),
Row(
children: options
.map((option) => [
Radio<String>(
value: option,
groupValue: _question['correct'] as String,
onChanged: (v) => setState(() => _question['correct'] = v!),
),
Text(option),
const SizedBox(width: 16)
])
.expand((w) => w)
.toList()),
const SizedBox(height: 32),
...options
.asMap()
.entries
.map((entry) => [
TextFormField(
controller: _optionCtrls[entry.key],
decoration: InputDecoration(labelText: 'Option ${entry.value}*'),
validator: (v) =>
v!.isEmpty ? 'Please fill in Option ${entry.value}' : null,
),
const SizedBox(height: 32),
])
.expand((w) => w),
\\ ...
Validation
Form validation in flutter is achieved by providing a validator function as an argument to the validator
named parameter of the TextFormField
. The validator function should return null
if the input is valid, else it should return an error message if the input is invalid (or empty in our case).
validator: (v) => v!.isEmpty ? 'Please fill in the Question' : null
Feedback to user with SnackBar
The submit button of forms are usually disabled if the form is invalid (has errors) in some apps. However, the user might not know why the button is disabled and that will be poor user experience.
Good user experience entails giving feedback to user when they take actions on your app. That's why those error messages on the Form show up when the user leaves the fields empty. Instead of disabling the submit button, we instead check for the form's validity when the button is pressed.
If the form is valid, we collect the question data and show the snackbar with success message. If on the other hand the form is invalid, we show the snackbar with the error message.
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_question['value'] = _questionCtrl.text;
_question['options'] = _optionCtrls.asMap().entries.map(
(entry) {
return {
'index': options[entry.key],
'value': entry.value.text,
};
},
);
if (kDebugMode) {
print(_question);
}
showSnackbar(
success: true,
text: 'Question updated successfully.',
);
} else {
showSnackbar(
success: false,
text: 'Please fill all the required fields.',
);
}
},
child: const Text('Update'),
)
Customizing the SnackBar
Displaying a SnackBar is easy with Flutter. By default, the snackbar pops in the bottom with full screen with. But we can customize the snackbar to appear at the top of the screen, in order to quickly catch the user's attention. We can also make the snackbar float by giving it some left and right margins.
Notice how we used a function: showSnackbar
, that takes an IconData
(to be displayed in the snackbar), text to be displayed, and a success bool
, that is used to determine the color of the displayed icon.
void showSnackbar({required bool success, required String text}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).size.height - 96,
right: 16,
left: 16,
),
behavior: SnackBarBehavior.floating,
content: Row(children: [
Icon(
success ? Icons.gpp_good : Icons.error,
color: success ? Colors.greenAccent : Colors.redAccent,
),
const SizedBox(width: 8),
Text(text),
]),
),
);
}
Final Thoughts
Flutter is a good framework for mobile development. Developer experience with flutter is smooth and the lines of code are shorter compared to other frameworks.
Forms are an important part of many apps as they help in collecting data. In our flutter app, we used a form to collect data about a question been set/created. This Flutter Question Form may not do much because the data is not been submitted anywhere. But then, we have successfully built a UI that can serve for setting questions that could be saved to some database.
Up Next
We will use this form to build an app that can edit many questions to be set for an online quiz.
Posted on January 19, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.