Dialogs, sheets & transitions
A destination can carry its own presentation — a dialog, a sheet, a custom
transition — right in its registration. No global pageBuilder switch:
entries.on<ConfirmDelete>(
(context, key) => const ConfirmDeleteContent(),
page: (context, key, child, pageKey) =>
DialogPage<void>(key: pageKey, builder: (_) => AlertDialog(content: child)),
);child is the built (and decorator-wrapped) screen; return any Page, keeping
the supplied pageKey as its key (a debug assert catches a dropped one — it
would silently desync system back).
Modals as stack entries
DialogPage and SheetPage make a dialog or bottom sheet a real stack
entry: stack.push(const ConfirmDelete()) shows it, back dismisses it,
pop(result) completes a pushForResult awaiter, and under the Router the URL
can track it — instead of an imperative showDialog floating outside the stack.
Transitions per destination
Same mechanism, with TransitionPage:
entries.on<Detail>(
(context, key) => DetailScreen(id: key.id),
page: (context, key, child, pageKey) =>
TransitionPage<void>.sharedAxisScaled(key: pageKey, child: child),
);The catalog covers the common cases plus the full Material motion set, all dependency-free. Pick the motion by the relationship between screens:
sharedAxisHorizontal— a step forward among peers (a wizard, onboarding).sharedAxisVertical— the vertical sibling.sharedAxisScaled— a step into a hierarchy (list → detail).fadeThrough— switching between unrelated destinations (e.g. tabs), where there's no spatial link to imply.- Plus
fade,slideUp,scale, andnone.
The shared-axis and fade-through motions animate the outgoing screen too, the way Material intends — at the render-object level, with no per-frame widget rebuilds.
Alternatives
NavPage mixin — the key builds its own Page, useful when the
presentation really is part of the destination's identity:
class Toast extends NavKey with NavPage {
const Toast(this.message);
final String message;
@override
Page<void> buildPage(BuildContext context, Widget child, LocalKey pageKey) =>
TransitionPage<void>.fade(key: pageKey, child: child);
}pageBuilder — one switch for everything, on NavDisplay /
BackStackApp, when you'd rather keep all presentation in one place:
pageBuilder: (context, key, pageKey) => switch (key) {
Step2() => TransitionPage.sharedAxisHorizontal(key: pageKey, child: screenFor(key)),
_ => MaterialPage(key: pageKey, child: screenFor(key)),
};