Theory
The form asks two kinds of question
FestConnect Flutter's registration form has reached its choices section:
Add a carry bag? Get event updates?: independent yes/nos: tick any, tick both.
Payment: Cash, Card or UPI?: exactly one.
You have met this split twice: BCA404's CheckBox-vs-RadioButton and Unit 1's Android widgets. Flutter keeps the split, keeps the controlled-widget law from last lesson: and quietly relocates where radio EXCLUSIVITY comes from, in a way that catches every Android and VB veteran.
Theory
Checkbox: one bool each, forever independent
Each checkbox is a controlled widget wrapped around its own bool:
bool carryBag = false;
Checkbox(
value: carryBag,
onChanged: (v) => setState(() { carryBag = v!; }),
)
The slider's loop verbatim: value displays the state, onChanged publishes the flip. Two checkboxes = 2 bools = total independence: which is precisely the any-of semantics. The labelled convenience form is CheckboxListTile (checkbox + title in one tappable row).
Practical
Both question kinds, one form
class _RegisterState extends State<RegisterScreen> {
bool carryBag = false; // checkbox: own bool each
bool wantsUpdates = true;
String payMode = 'upi'; // ONE variable = ONE radio group
@override
Widget build(BuildContext context) {
return Column(
children: [
CheckboxListTile(
title: const Text('Add a carry bag (Rs 10)'),
value: carryBag,
onChanged: (v) => setState(() { carryBag = v!; }),
),
CheckboxListTile(
title: const Text('Send me event updates'),
value: wantsUpdates,
onChanged: (v) => setState(() { wantsUpdates = v!; }),
),
RadioListTile<String>(
title: const Text('Cash'),
value: 'cash', // this radio's identity
groupValue: payMode, // the SHARED group variable
onChanged: (v) => setState(() { payMode = v!; }),
),
RadioListTile<String>(
title: const Text('Card'),
value: 'card',
groupValue: payMode,
onChanged: (v) => setState(() { payMode = v!; }),
),
RadioListTile<String>(
title: const Text('UPI'),
value: 'upi', // equals payMode: renders selected
groupValue: payMode,
onChanged: (v) => setState(() { payMode = v!; }),
),
],
);
}
}
Theory
Radio: exclusivity by shared variable
Read the radio trio's machinery:
- each Radio carries a value: its identity ('cash', 'card', 'upi')
- all 3 point their groupValue at the SAME state variable, payMode
- the radio whose
value == groupValuerenders selected: with payMode = 'upi', the UPI row shows the dot - tapping Card runs setState(payMode = 'card'): ONE assignment, and the rebuild repaints ALL 3 radios against the new groupValue: Card gains the dot, UPI loses it
No RadioGroup, no GroupBox, no container anywhere: the group IS the shared variable.
Quiz
In Flutter, what makes 3 Radio widgets behave as one exclusive group?
- Placing them inside the same Column or container
- All 3 sharing the same groupValue state variable
- Giving them the same value parameter
- Wrapping them in a RadioGroup widget, like Android
Show the answer
All 3 sharing the same groupValue state variable
The group lives in STATE, not layout: 3 radios reading one payMode variable are one group wherever they sit on screen: even scattered across different Rows. Option A is the reflex this question hunts: BCA404's GroupBox and Android's RadioGroup defined groups by CONTAINER, and Flutter deliberately does not. Option C would give 3 radios the same IDENTITY: they would all select together, the opposite of exclusive. Option D imports a widget Flutter does not ship. Exam sentence: exclusivity follows the shared groupValue; selection is value == groupValue.
Think first
Two questions, so how many variables?
The form grows a second radio question: Delivery: Take away / Home delivery. In BCA404 this needed a second GroupBox. What does it need in Flutter, and what goes wrong if you lazily reuse payMode for both questions? Trace it, then tap.
Show the answer
A second state variable: String delivery = 'takeaway'; and the 2 new radios point groupValue at THAT. Reuse payMode for both and all 5 radios form ONE accidental group: choosing Home delivery (payMode = 'home') deselects UPI, and your payment silently becomes 'home': the same merged-group bug as BCA404's radios on a bare form, relocated from layout into state. The mapping to write in your notes: one QUESTION = one groupValue variable, exactly as one question = one container in the old worlds.
Watch out
Choice-widget slips
onChanged: null freezes them: like buttons, a null handler renders the control disabled: useful deliberately, baffling accidentally.
The v! bang: onChanged hands a nullable (bool? / T?); v! asserts non-null before storing: modern Dart's null safety showing its teeth.
Checkbox for one-of questions: 2 payment checkboxes both ticked is a data bug born in the form design: the BCA404 rule (any-of = check, one-of = radio) survives all 3 platforms.
Theory
The controlled-widget law, second and third witnesses
Checkbox and Radio just re-testified: value from state, change through onChanged + setState: and Radio added the insight that even GROUPING is state in Flutter. This is the philosophical heart of the framework: layout arranges, state decides, widgets merely display. Unit 5 opens next with the progress indicators and a deeper Stack: the loading overlay every real booking flow needs.
Summary
Key takeaways
- Checkbox: value (its own bool) + onChanged + setState: independent any-of choices; CheckboxListTile for labelled rows.
- Radio<T>: value is the radio's identity; groupValue points at the shared state variable; selected when value == groupValue.
- Exclusivity comes from the SHARED groupValue: not from containers (the Android/VB.NET contrast).
- One question = one groupValue variable; reusing one across questions merges the groups.
- onChanged: null disables; handlers receive nullables (hence v!).
- Any-of = checkbox, one-of = radio: the rule survives its third platform.
- Memory hook: the group is a variable, not a box.