skip to content

In Flutter, how do you show a SnackBar through ScaffoldMessenger, and how does a SnackBar with an action behave by default since Flutter 3.38?

level: middleimportance: should knowfreq 48%

answer

  1. the messenger, not the Scaffold
  2. survives a route change
  3. one at a time, queued
  4. 4 seconds unless it has an action
  5. persist: false restores auto-dismiss

basics

~10 s

Call ScaffoldMessenger.of(context).showSnackBar(SnackBar(...)); the messenger from MaterialApp shows it on the current Scaffold and queues extras. Since Flutter 3.38 a SnackBar with an action stays until dismissed unless persist is false.

solid answer

~40 s

I call `ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: ...))`. `MaterialApp` provides a root `ScaffoldMessenger`, which shows the snack bar on whichever `Scaffold` is current and keeps it across route changes; that replaced `Scaffold.of(context).showSnackBar` in Flutter 2.0. Only one snack bar shows at a time, and further calls queue behind it; `hideCurrentSnackBar`, `removeCurrentSnackBar` and `clearSnackBars` manage the queue. A plain snack bar closes after its `duration`, 4 seconds by default. Since 3.38, one with a `SnackBarAction` has `persist` defaulting to `true`, so it stays until the user acts or dismisses it; pass `persist: false` to restore the timeout. It cannot be shown during `build`, and after an `await` I check `context.mounted` first; for code outside the widget tree, `MaterialApp.scaffoldMessengerKey` gives access to the messenger.

code

dart · 14 lines
dart
Future<void> _cancelReservation(BuildContext context) async {
  final undo = await _rentals.cancelCurrent(); // returns a callback that restores it
  if (!context.mounted) return;
  ScaffoldMessenger.of(context)
    ..clearSnackBars()
    ..showSnackBar(
      SnackBar(
        content: const Text('Reservation cancelled'),
        persist: false, // auto-dismiss after duration despite the action
        duration: const Duration(seconds: 6),
        action: SnackBarAction(label: 'Undo', onPressed: undo),
      ),
    );
}

go deeper

for a junior

Recall the call ScaffoldMessenger.of(context).showSnackBar, that MaterialApp provides the messenger, and that a plain snack bar closes after 4 seconds.

for a middle

Explain why the messenger replaced Scaffold.of, how the queue and its hide, remove and clear methods work, and the 3.38 persist default for snack bars with actions.

for a senior

Show safe usage in async flows: mounted checks, clearing bursts, a scaffoldMessengerKey for messages from outside the tree, and an explicit choice about persist.

for a principal

Decide the app's feedback policy, which events deserve a snack bar, a banner or a dialog, and how undo windows balance accessibility and clutter.

## ScaffoldMessenger in one paragraph A **`SnackBar`** is a short message at the bottom of the screen, optionally with one action. In Flutter it is managed by a **`ScaffoldMessenger`**, an ancestor widget that keeps a queue of snack bars and shows each one on the `Scaffold`s registered below it. `MaterialApp` inserts a root messenger above all routes, so `ScaffoldMessenger.of(context).showSnackBar(...)` works from any screen. ## Why the messenger replaced Scaffold.of Before Flutter 2.0, snack bars were shown with `Scaffold.of(context).showSnackBar`. That had two problems: - the snack bar belonged to one screen's Scaffold, so navigating away dropped it mid-display; - calling it after an `await`, when the route had changed and the Scaffold was gone, threw errors. The messenger lives above the routes, so a snack bar shown just before `Navigator.push` keeps displaying on the new screen's Scaffold. ## Queueing and dismissal One snack bar is visible at a time. Calling `showSnackBar` again while one is showing adds the new one to a queue, displayed after the earlier ones close. To control the queue: | Method | Effect | |---|---| | `hideCurrentSnackBar()` | animates the current one out, next one follows | | `removeCurrentSnackBar()` | removes the current one immediately, without animation | | `clearSnackBars()` | removes the current one and empties the queue | A common pattern for a burst of messages is `messenger..clearSnackBars()..showSnackBar(...)`, so only the latest message shows. ## Duration, actions and persist 1. A `SnackBar` without an action closes after `duration`, which defaults to 4 seconds. 2. Since Flutter 3.38, a `SnackBar` with an `action` has `persist` defaulting to `true`: the timer still runs, but on timeout the messenger leaves it on screen until the user taps the action or it is dismissed. 3. Passing `persist: false` restores the old behaviour, auto-dismissing after `duration` even with an action; `persist: true` keeps any snack bar until dismissed. The change was made for accessibility: an action the user must act on should not vanish before a screen-reader user reaches it. Before 3.38, a snack bar with an action auto-dismissed unless an accessibility service such as TalkBack was on. Other useful parameters are `showCloseIcon`, which adds a close button, and `behavior: SnackBarBehavior.floating`, which floats the bar above the floating action button and bottom navigation bar. ## Where not to call it - **During build.** `showSnackBar` inside a `build` method fails with 'The showSnackBar() method cannot be called during build.' Call it from an event handler or after the frame. - **After an await without a check.** The widget whose `context` you hold may have been removed while waiting; check `context.mounted` first. - **With no Scaffold below the messenger.** It asserts that there are no descendant Scaffolds to present to, for example on a screen built without a `Scaffold`. ## Outside the widget tree For messages triggered from a service or a state object without a `BuildContext`, give `MaterialApp` a `scaffoldMessengerKey`, a `GlobalKey<ScaffoldMessengerState>`, and call `key.currentState?.showSnackBar(...)`. ## Bike-rental example When a ride ends, the app shows 'Ride ended: 3.2 km' as a plain snack bar that closes after 4 seconds. When the user cancels a reservation, it shows 'Reservation cancelled' with an 'Undo' action; since 3.38 that one stays until the user taps Undo or dismisses it, unless the team passes `persist: false` for a timed undo window. ## Choosing the right Material overlay | Need | Component | |---|---| | a brief status, optionally one action | `SnackBar` via `ScaffoldMessenger` | | a persistent message at the top of the screen, with actions | `MaterialBanner` via `ScaffoldMessenger.showMaterialBanner` | | a decision the user must make now | `showDialog` with an `AlertDialog` | | a set of options or details for one item | `showModalBottomSheet` | A snack bar should never be the only place an important error appears: if the user must act before continuing, a dialog or an inline message is the better choice.

  • How would you show a snack bar from a repository or state object that has no BuildContext?
    Create a `GlobalKey<ScaffoldMessengerState>` once, pass it to `MaterialApp.scaffoldMessengerKey`, and call `key.currentState?.showSnackBar(...)` from wherever the event is handled. It reaches the root messenger without a context, so it still shows on the current Scaffold. Keep such UI calls at the edge of the app rather than deep in data code.
  • What changed for users of screen readers with the 3.38 SnackBar change?
    Before 3.38 a snack bar with an action auto-dismissed unless an accessibility service like TalkBack was enabled. Since 3.38 any snack bar with an action persists by default for everyone, so the action cannot disappear before the user reaches it; apps that want a timed undo now opt out explicitly with `persist: false`.

saying these in an interview costs you the question

  • Show snack bars with Scaffold.of(context).showSnackBar in current Flutter.
  • A new snack bar immediately replaces the one on screen.
  • A SnackBar with an action still auto-dismisses after 4 seconds by default.
  • Showing a snack bar inside build is fine if it is guarded by a flag.
  • A snack bar shown before Navigator.push disappears when the new route opens.