Theory
The tap that deletes a booking
FestConnect Flutter's My Bookings list gains a Cancel button per row: one accidental tap from deleting a student's Garba Night seat.
Destructive actions have earned a ritual on every platform you know: BCA404 froze the form with ShowDialog and read a DialogResult; the web used modals. Flutter's ritual looks familiar: but its dialogs hide a genuinely elegant idea about WHERE a dialog lives, and that idea is what the exam actually tests.
Theory
AlertDialog, summoned
Two pieces: the SHOWING function and the dialog WIDGET:
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Cancel booking?'),
content: Text('Your Garba Night seat will be released.'),
actions: [ ...buttons... ],
),
);
It floats modally over a dimmed scrim: the screen behind is untouchable: and actions: holds the buttons, ranked by last lesson's law: quiet TextButton for Keep it, one louder button for the destructive confirmation.
Practical
The confirm dialog that answers back
Future<void> confirmCancel() async {
final sure = await showDialog<bool>( // showDialog RETURNS a Future
context: context,
builder: (context) => AlertDialog(
title: const Text('Cancel booking?'),
content: const Text('Your Garba Night seat will be released.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false), // answer: no
child: const Text('Keep it'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true), // answer: yes
child: const Text('Cancel booking'),
),
],
),
);
if (sure == true) {
// release the seat (sure is null if they tapped outside!)
}
}
// And the hint that prevents the accident in the first place:
Tooltip(
message: 'Cancels your booking permanently',
child: IconButton(icon: const Icon(Icons.delete),
onPressed: confirmCancel),
)
Theory
The elegant idea: dialogs are routes
Why does Navigator.pop(context) close a dialog: the same call that closes a SCREEN?
Because in Flutter, a dialog IS a mini-route, pushed onto the navigation stack over the current screen. Closing is popping: and popping can carry the answer back: Navigator.pop(context, true): which showDialog delivers as its Future's value.
That is BCA404's DialogResult reborn: ask modally, await the answer, branch: with one honest extra: tapping OUTSIDE dismisses with null, so test sure == true, never just sure. (Forbid outside-dismissal with barrierDismissible: false when a choice is compulsory.)
Quiz
How is an open AlertDialog closed in Flutter, and why that mechanism?
- dialog.close(): every widget has a close method
- Navigator.pop(context): the dialog was pushed onto the navigation stack like a mini-route
- setState(() { showingDialog = false; })
- It closes itself after 5 seconds
Show the answer
Navigator.pop(context): the dialog was pushed onto the navigation stack like a mini-route
Dialogs live on the SAME stack as screens: showDialog pushes, pop removes: one navigation model for everything that appears and disappears, and pop's optional second argument is how the answer travels back to the awaiting showDialog. Option A invents an API; widgets are not windows. Option C is the reflex from the loading-overlay lesson: reasonable machinery, but not how dialogs work: they are routes, not conditional children. Option D describes a toast's temperament: next lesson's widget, never a dialog's.
Think first
Audit the three exits
The dialog has 3 ways out: Keep it, Cancel booking, and tapping the dim area outside. Trace what value each delivers to sure, and what the if (sure == true) guard protects against, then tap.
Show the answer
Keep it: pop(context, false): sure = false. Cancel booking: pop(context, true): sure = true. Tapping outside: dismissed with NO value: sure = null. The guard sure == true treats null as no: exactly right for a destructive action, since a shrug must never delete a seat. Write if (sure) instead and null crashes the condition (Dart bools refuse null): the == true idiom is both the safety AND the null-safety. Three exits, three values, one honest guard: that trace is the full exam answer.
Watch out
Dialog and tooltip slips
Forgetting the pops: buttons without Navigator.pop leave the dialog eternally open: every action closes, with its answer.
Testing if (sure): null from an outside-tap breaks it: sure == true, always.
Dialog for non-decisions: "Booking saved!" needs no modal interrogation: that is toast/SnackBar territory, next lesson.
Tooltip as the only label: long-press hints are discoverable by few: tooltips SUPPLEMENT icons (and serve screen readers): they never replace visible text for critical actions.
Theory
The modal pattern, third platform
Line them up: BCA404's ShowDialog() returning DialogResult, the web's modal boxes, and Flutter's await showDialog returning a Future: ask modally, await the verdict, branch on it: one pattern, three wardrobes. And Tooltip completes a small reunion too: BCA404's ToolTip component, now a wrapper widget. One lesson remains in the whole subject: toast, switch, charts and the Form: FestConnect Flutter's finale.
Summary
Key takeaways
- showDialog(context, builder) floats a modal AlertDialog: title, content, actions.
- Dialogs are mini-routes on the navigation stack: Navigator.pop(context) closes; pop(context, value) answers.
- await showDialog<bool> receives the answer: true/false from pops, NULL from an outside tap.
- Guard with sure == true; barrierDismissible: false forces a button choice.
- Rank the actions: quiet TextButton beside one louder confirming button.
- Tooltip(message, child) adds a long-press hint (IconButton's tooltip: shorthand): supplement, not replacement.
- Memory hook: dialogs are routes; pop carries the verdict; outside taps answer null.