Analytics
Because the stack is plain observable data, tracking navigation doesn't mean
reverse-engineering Route names from a NavigatorObserver. NavStackObserver
reports events in your key type.
final observer = NavStackObserver<AppKey>(
stack,
onScreen: (key) => analytics.logScreen(key.runtimeType.toString()), // screen_view
onPush: (key) => debugPrint('→ $key'),
onPop: (key) => debugPrint('← $key'),
);
// when you're done (it doesn't own the stack):
observer.dispose();onScreenfires whenever the visible top changes — yourscreen_viewsignal — and once on construction for the initial screen (passemitInitial: falseto skip that).onPush/onPopfire per destination that enters or leaves. A singlereplaceAllreports each added and removed screen exactly once, and reconciled survivors (the same key kept across a change) don't re-fire.
It works by diffing entry identities, so the events reflect what actually changed on the stack — not every rebuild.
Debugging live
For eyeballing the stack while you build, drop in BackStackInspector<K>()
anywhere below the display. It lists every entry bottom-to-top, marks the current
one, and updates on every push/pop — no DevTools setup, because the stack is just
data.