Theory
The last screen of the semester
Two jobs remain on FestConnect Flutter's list, and they close the whole subject.
The committee wants an attendance chart: day 1 vs day 2 vs day 3, bars.
And registration still accepts an empty name and a 3-digit phone: the form needs validation that scolds precisely and blocks submission until clean.
One of these jobs Flutter refuses to ship a widget for: and both endings teach the framework's deepest habit.
Theory
Charts: the honest situation
Flutter has no built-in chart widget. The real-world route is a package from pub.dev, declared in pubspec.yaml:
dependencies:
fl_chart: ^0.66.0
then flutter pub get, import, and use its BarChart/LineChart widgets. (fl_chart is today's standard; charts_flutter was Google's older library.)
But for a 3-bar attendance chart, there is a purer answer: compose it: a Row of Containers whose heights ARE the data: no package, runs anywhere, and it is the ImageSlider lesson's moral again: missing widgets are assemblies waiting to happen.
Practical
A bar chart from nothing but Containers
final attendance = {'Day 1': 320.0, 'Day 2': 410.0, 'Day 3': 275.0};
Row(
crossAxisAlignment: CrossAxisAlignment.end, // bars grow from a floor
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: attendance.entries.map((e) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('${e.value.toInt()}'),
Container(
width: 48,
height: e.value / 2, // data drives the pixels: 410 -> 205
color: Colors.deepOrange,
),
Text(e.key),
],
);
}).toList(),
)
Theory
Form: validation as a system
Three pieces turn scattered fields into a validating Form:
- the key:
final _formKey = GlobalKey<FormState>();handed toForm(key: _formKey): your handle to the form's state - TextFormField: TextField plus a
validator:function:(v) => v!.isEmpty ? 'Enter your name' : null - the submit call:
_formKey.currentState!.validate(): runs EVERY field's validator, paints each returned message under its own field, and answers true only if all returned null
Read the validator's contract carefully: returning null means VALID: a String is the error message. That inversion is this topic's certain exam question.
Practical
The finished registration form
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Your name'),
validator: (v) =>
v == null || v.isEmpty ? 'Enter your name' : null,
),
TextFormField(
decoration: const InputDecoration(labelText: 'Phone'),
keyboardType: TextInputType.number,
validator: (v) =>
v != null && v.length == 10 ? null : 'Enter a 10-digit phone',
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// every validator returned null: register!
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Registered for the fest!')));
}
},
child: const Text('Register'),
),
],
),
)
Quiz
A TextFormField's validator returns null. What does that mean?
- The field failed validation with no specific message
- The field is VALID: null means no error to report; a returned String is the error message
- The validator crashed on a null input
- The field was left empty
Show the answer
The field is VALID: null means no error to report; a returned String is the error message
The contract is inverted from instinct and therefore examined forever: the validator's return value IS the error message, so nothing-to-report: null: means the field passed, while any String both fails the field and becomes the red text painted beneath it. validate() then aggregates: true only when every field answered null. Option A reads null with the usual something-went-wrong instinct this question exists to break. Options C and D confuse the INPUT (v, which may be null or empty and is what you test) with the RETURN (your verdict). Null is a clean bill of health here.
Think first
The whole journey, in one screen
Look at the finished form and name where each of its pieces was learned: the Column, the TextFormField's decoration, the keyboardType, the setState-less validate flow, the SnackBar, and the named parameters carrying it all. Then tap for the roll call.
Show the answer
Column: the invisible-widgets lesson. decoration/InputDecoration and keyboardType: the TextField lesson. The button and its rank: buttons-slider. SnackBar: last lesson. Named parameters everywhere: the Dart functions lesson, exactly as promised. And validate() needing no setState: the FORM object holds field state via the key: a fitting last variation on the state theme. One screen, eleven lessons, two languages, one subject: FestConnect Mobile began as Java widgets in Unit 1 and ends here, rebuilt, validated and cross-platform. That arc: patterns surviving platforms: was the real syllabus.
Watch out
Final-topic traps
Inventing a built-in chart widget: name the honest options: pub.dev packages (fl_chart) or composition: pretending Flutter ships BarChart natively loses the mark.
validator logic inverted: returning 'OK' on success paints OK as an error: null passes, String fails, always.
Forgetting the key: validate() reaches the form only through the GlobalKey; a Form without one is decoration.
Validating on every keystroke by default: validate() on submit is the calm default; live validation is opt-in (autovalidateMode), not free.
Theory
The subject closes
Count what you now carry: Android's widget catalogue and its adapter/recycling machinery, a second language learned in a week by transfer, Flutter's widget algebra (visible/invisible, stateless/stateful, controlled by state), and one app built TWICE. When BCA503-02 and real internships hand you React or whatever comes next, walk in asking the same 3 questions: how does it show, how does it change, how does data flow: and you will feel at home by lunch. FestConnect Mobile 2: shipped.
Summary
Key takeaways
- No built-in charts: pub.dev packages (fl_chart) via pubspec dependencies + flutter pub get, or compose bars from data-driven Containers.
- Form(key: GlobalKey<FormState>()) wraps TextFormFields: TextField plus a validator.
- Validator contract: return null = VALID; return a String = invalid, and that String is the painted error.
- _formKey.currentState!.validate() runs all validators, shows messages, returns true only if all passed.
- Submit pattern: if (validate()) proceed: no setState needed; the form state lives behind the key.
- Composition remains the Flutter answer to missing widgets.
- Memory hook: null passes, Strings scold, and what Flutter lacks, you compose.