NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
You own the back stack. Navigation is a list you push and pop — type-safe, observable, no route graph.
Last release 2 months ago
14 Jul 2026
Too new to tell
only 2 release windows
Nearly every release is documented
notes for 17 of 17 stable releases
Nothing withdrawn
no release was ever pulled
3 months old
17 releases · first in 2026
One column per month.
A correctness pass over the async-gating, deep-link, and page-memo edges. Still zero dependencies.
A correctness pass over the async-gating, deep-link, and page-memo edges. Still zero dependencies.
AsyncRedirect.invalidate() now cancels in-flight checks. A check already awaiting (auth/session lookup) could complete and apply a decision made from the very state invalidate() declared stale. Each check is now pinned to a cache generation; one that resolves after an invalidate() is dropped (not cached, not applied) and any held navigation released, so the next navigation re-checks against fresh state.initialLink / linkStream themselves are now caught and dropped — the app stays put and the stream keeps delivering later links — consistent with the existing "bad links don't crash" behavior.decorators list now invalidates the memoized pages. With a stable builder, existing pages kept their old decorated subtree; the decorator list now participates in memo invalidation (compared element-wise, so stable decorators keep the memo).>=3.24.0 to match the API surface actually used (Navigator.onDidRemovePage, PopScope.onPopInvokedWithResult).Fixes #1.
A correctness pass over the async-gating, deep-link, and page-memo edges. Still zero dependencies.
AsyncRedirect.invalidate() now cancels in-flight checks. Previously it
only cleared resolved cache entries: a check already awaiting (an auth or
session lookup) could still complete and apply a decision made from the very
state invalidate() declared stale — e.g. redirecting a now-signed-in user
to Login. Each check is pinned to a cache generation; a check that resolves
after an invalidate() is dropped (not cached, not applied) and any held
navigation is released, so the next navigation re-checks against fresh state.BackStackApp/BackStackTabsApp hardened URI decoding, but an
error from initialLink or linkStream itself went uncaught. Both are now
caught and dropped — the app stays put and the stream keeps delivering later
links — consistent with the existing "bad links don't crash" behavior.decorators list now invalidates the memoized pages.
NavDisplay re-memoized pages on a stack/builder/entries/pageBuilder
change but not on a decorators change, so with a stable builder existing
pages kept their old decorated subtree. The decorator list now participates
in memo invalidation (compared element-wise, so stable decorators keep the
memo).>=3.24.0 to match the API surface
actually used (Navigator.onDidRemovePage,
PopScope.onPopInvokedWithResult); the old >=1.17.0 was far too low.(The redirect-safe pushForResult path reported alongside these was already
fixed in 0.5.0; a regression test now locks it in.)
The edge-swipe back gesture now works on pages with custom transitions: on iOS the drag drives the page's own transitionsBuilder in reverse and pops p
The edge-swipe back gesture now works on pages with custom transitions:
on iOS the drag drives the page's own transitionsBuilder in reverse and
pops past halfway or on a fling, like CupertinoPageRoute. New swipeBack
parameter (default true; false for .none) to opt out. Gesture respects
popGestureEnabled (root, fullscreenDialog, PopScope), handles RTL, and
the pop syncs the NavStack.
TransitionPageTransitionPage used to build a plain PageRoute, so on iOS the standard
drag-from-the-left-edge gesture did nothing — a screen pushed with .fade
or a shared-axis transition could only go back via a button. On iOS
(defaultTargetPlatform), dragging from the leading edge now drives the
page's own transitionsBuilder in reverse and pops past the halfway point
or on a fling, exactly like CupertinoPageRoute — and the pop syncs the
NavStack as usual. RTL layouts swipe from the right edge.swipeBack parameter on every TransitionPage constructor to opt a
page out (defaults to true; TransitionPage.none defaults to false).
The gesture also stays off for fullscreenDialog pages, non-opaque
pages, the root of the stack, and any screen whose
PopScope/ConfirmPopScope blocks the pop — same rules as the
framework's popGestureEnabled. (Like the AppBar back button, it goes
through the Navigator, so the programmatic-pop veto stack.popGuard
doesn't apply.)Type-level breaking change: the delegates' Router config type is now NavRouteInformation (uri + state) instead of Uri; BackStackApp/BackStackTabsApp u…
Correctness release for everything back-button-adjacent, driven by an adversarial audit of 0.4.0.
Highlights:
Type-level breaking change: the delegates' Router config type is now NavRouteInformation (uri + state) instead of Uri; BackStackApp/BackStackTabsApp users are unaffected.
Full details in the CHANGELOG.
A correctness release for everything back-button-adjacent, driven by an adversarial audit of 0.4.0. Still zero dependencies.
Navigator first.
MultiNavStackRouterDelegate.popRoute() (and MultiNavDisplay's own
PopScope) used to pop the active tab's stack model directly, skipping
the tab's routes entirely — back would pop the page under an open dialog,
or leave the app with a dialog still up, and a screen's
PopScope/ConfirmPopScope inside a tab never saw the gesture. Back now
goes through the active tab's navigator via maybePop (dialog → screen's
PopScope → tab page → first-tab fallback → app close), tracked live via
NavigationNotification so an open dialog is known even at a tab root. The
delegate exposes its per-tab navigatorKeys, and MultiNavDisplay accepts
navigatorKeys: for custom Router setups.NavDisplay(nested: true) routes back through its child navigator the
same way, instead of popping the stack directly — dialogs, sheets and
PopScopes inside a nested display now get first refusal.gates: predicate — declare what's protected. Navigation matching
gates is now held off screen while check runs: the stack stays
where it is (as the docs always claimed), resolving drives your scrim,
and the held navigation is applied — or bounced — when the check resolves.
Everything not matching gates navigates instantly and is never checked,
so pops and pushForResult results are unaffected. Without gates the
old show-then-correct behavior remains (holding everything would swallow
ordinary results); pass gates to get the hold.check now fails closed: the navigation is dropped, the
user stays put, and the error is reported via FlutterError.reportError
(it used to be silently allowed — an auth gate that failed open).pushForResult into an undecided gated destination resolves null
immediately; un-gated destinations are unaffected. Documented.RouteInformation.state), so a browser back/forward hop restores the
entire typed stack — in-app depth the URL can't express included — instead
of rebuilding from the URL's projection. Automatic in
BackStackApp/BackStackTabsApp when links (or restoreWith) is set;
custom Router setups opt in via the delegates' new stateCodec. A missing,
corrupt, or URL-disagreeing snapshot falls back to the URL mapping, exactly
like a deep link. The tabs snapshot covers every tab's stack plus the
active tab.Uri to NavRouteInformation (uri + state), and
NavStackRouteInformationParser now parses that type. Apps using
BackStackApp/BackStackTabsApp are unaffected; direct
setNewRoutePath(uri) callers wrap the uri: NavRouteInformation(uri).
The imperative link API (handleLink/handleLinkAsync) still takes a
plain Uri.observers could never work — replaced with observersFor. A
NavigatorObserver belongs to exactly one Navigator (the framework
asserts it), but the tabs widgets attached the same list to every tab's
navigator — any non-empty observers with 2+ tabs crashed in debug on the
first frame. MultiNavDisplay/MultiNavStackRouterDelegate/
BackStackTabsApp now take observersFor: (tabIndex) => [...], called
once per tab; observers remains for single-tab hosts and is rejected
with a clear message otherwise.redirect no longer breaks pushForResult. _commit
reconciled the redirect's result against the pre-change stack, so any
attached redirect — even one that allowed the navigation unchanged —
rebuilt the just-pushed entry with a fresh identity and its
pushForResult awaiter resolved null. The redirect result now
reconciles against the proposed entries, keeping the pushed entry's id
(and awaiter) when it's allowed through.BackStackApp with no links/onLink: a runtime deep link now keeps
the app where it is (as documented) instead of resetting navigation to
its launch state.builder/entries now rebuilds when a Theme / localization / media-query
dependency changes — previously a live entry's screen was built exactly
once and silently never updated.NavStackCodec: documented that browser back/forward rebuilds the stack
from the URL via decode, like a deep link — in-app depth beyond the
URL's projection isn't preserved across a history hop.0.4.0: NavLinks URL table, one-widget BackStackApp/BackStackTabsApp, …
0.4.0: NavLinks URL table, one-widget BackStackApp/BackStackTabsApp, …
The "handled internally" release: the app shapes and URL plumbing every real app needs, each reduced to one parameter — plus a hardening pass over the core. Still zero dependencies.
NavLinks — the URL table. One entry per linkable destination declares
both directions at once ('/products/:id' ⇄ Product), with typed
parameter reads (NavMatch), query parameters, *rest catch-alls, a
per-destination parents: (the layer-vs-replace choice), and notFound.
Deep links, the web address bar, browser back/forward, shareable links
(linkFor) and state restoration all derive from the same table, so the two
directions can never drift apart. It's a NavStackCodec, so it plugs in
anywhere one goes — usually BackStackApp(links: …). It never owns the
stack; back_stack still has no route graph.onLink/toLink bug, caught at
development time.BackStackApp is now a true one-widget app: onLink is optional
(BackStackApp(stack: …, entries: …) is a complete app), it accepts
links: and entries: directly, and it forwards the previously missing
MaterialApp knobs (onGenerateTitle, scaffoldMessengerKey, locale
callbacks, high-contrast themes, shortcuts/actions, appBuilder). A
shell: parameter wraps persistent chrome (side rail, overlays) around the
navigating area, under MaterialApp. Config (onLink/toLink/builder)
is read live, so hot reload and rebuilds take effect without losing
navigation state.BackStackTabsApp — the tabbed app, bundled. Bottom navigation with a
persistent back stack per tab used to take up to nine hand-wired objects;
it's now one widget: pass tabs: + destinations: (built-in
NavigationBar, selection and re-tap-to-pop-to-root wired) or shell:
(your own chrome). Works with links: — the target tab is inferred from
the decoded stack's root — plus onLinkAsync, initialLink/linkStream,
lazy tabs, and automatic restoration of every tab's stack + the active tab.onLinkAsync on both apps (and asyncDecode
on both router delegates): await a lookup before deciding what a link
shows. Race-safe by design: a newer link supersedes an in-flight one, and
errors fall through to the sync mapping.NavStack.popGuardAsync + tryPop(): an "are you sure?"
dialog or save-before-leave call for programmatic pops, race-safe (if the
stack changes while the guard is deciding, nothing is popped).AsyncRedirect.attach(stack) wires redirect + refreshListenable in
one call — forgetting the second half used to make gating silently hang.
detach() undoes it; dispose() auto-detaches. Also hardened: disposing
mid-check no longer crashes, and the decision cache is bounded
(cacheSize) and keyed by real element-wise equality instead of a raw
hash.links: set, the entire typed stack (and for tabs: every tab's
stack + the active tab) survives process death — serialized through the
same URL table, unregistered keys skipped, corrupt snapshots discarded.
restoreWith: accepts a NavKeyCodec for destinations without URLs. A
cold-start deep link still wins over the snapshot unless the snapshot is
just the deeper history of the same location. (RestorableBackStack /
RestorableMultiNavStack remain for Router-free setups.)NavEntries.on<T>(page: …) — a destination can carry its own dialog,
sheet, or transition, with decorators still applied: no global
pageBuilder switch and no mixin on domain keys. Displays and delegates
accept the registry directly (entries: as the alternative to builder:).NavStackObserver<K> — typed onScreen/onPush/onPop in your keys.NavPath — typed URL reading/building sugar for hand-written codecs.pushAll, popToRoot, removeWhere.BackStackApp.initialLink — cold-start link, awaited and applied
through the same mapping.pushOrMoveToTop now returns whether the stack changed — making it the
double-tap-proof push (a second tap is a no-op, not a duplicate screen).replaceTop(key, result: …) completes the replaced screen's
pushForResult awaiter with a value.pushForResult's result type is checked at the pop(result) call site in
debug, instead of failing later as a cast error inside the await.NavDisplay page memoization survives parent rebuilds with the documented
entries.call tear-off (function equality instead of identity), and
onRemoved still fires after a builder swap.popToRoot is one commit and respects popGuard on the current top; tab
re-select uses the same semantics (one notification, protected forms stay).NavLinks/link decoding never throws — including trailing slashes and
URLs whose percent-escapes aren't even valid UTF-8.Overlay (tooltips/menus in a bottom bar work).BackStack.of/MultiBackStack.of/parentOf throw real FlutterErrors in
all build modes, with a "did you forget the type argument?" hint;
maybeParentOf stops walking once found; custom pages that drop the
supplied pageKey are caught by a debug assert (they would silently desync
system back).docs: warn that app_links AppLinks() must be created early or the col…
docs: warn that app_links AppLinks() must be created early or the col…
A feature drop aimed at everyday UX — all within the same few-concepts, zero-dependency model (nothing here changes how you own the stack).
AsyncRedirect — loop-proof async auth gating. redirect stays synchronous
(that's what makes it loop-proof), so async gates that need to await — a
permission call, a session refresh, a "does this deep-linked doc exist" check —
now have a first-class home. Assign gate.call to redirect and the gate to
refreshListenable: navigating to a gated destination holds it while the check
runs (gate.resolving drives a loading overlay), then either allows it or
redirects. Decisions are cached per destination so a check runs once; call
gate.invalidate() after login/logout. Built entirely on the existing sync
primitives — the core is unchanged.TransitionPage. Adds sharedAxisHorizontal
/ sharedAxisVertical / sharedAxisScaled (X/Y/Z) and fadeThrough alongside
the existing fade/slideUp/scale. Hand-rolled, dependency-free, and the shared-axis
/ fade-through motions animate the outgoing screen too. Pick the motion by the
relationship between screens (peer step, into-a-hierarchy, unrelated switch).doc/MIGRATING_FROM_GO_ROUTER.md) — a mapping
table plus before/after for routes, navigation calls, typed arguments, redirects
(incl. async), shell/tab routes, deep links and error handling, with notes on
migrating gradually.example/lib/guarded.dart (async gating with a loading overlay) and
example/lib/motion.dart (per-destination Material motion).BackStackApp.linkStream: feed a plugin's runtime Stream (custom scheme, Firebase Dynamic Links, warm app_links) through the same onLink, with the same
Router only surfaces the
launch URL and standard app links it resolves itself. Links that arrive while
the app runs — a custom scheme (myapp://…), a Firebase Dynamic Link, a warm
app_links link — come from a native plugin as a Stream<Uri>. BackStackApp
now takes an optional linkStream: hand it that stream and every emission
runs through the same onLink, with the same never-throws fallback as a
platform link. back_stack owns the subscription lifecycle and cancels it on
dispose; you bring the Uris from whatever plugin you use — no new dependency.NavStackRouterDelegate.handleLink(uri) / MultiNavStackRouterDelegate.handleLink(uri) —
the imperative sibling of setNewRoutePath, for driving your own router: apply
a runtime link to the stack with the identical decode-or-fallback hardening.
(setNewRoutePath now delegates to it, so platform and runtime links share one
code path.)Fix NavListDetail duplicate-GlobalKey crash for a lone detail on a wide screen
A correctness + documentation pass. No API changes — every fix is behavior that matches what the docs already promised.
NavListDetail no longer crashes when a lone detail is on top on a
wide screen. Previously, if the only (or first) entry was itself a detail
type, it was mounted in both panes under the same per-entry GlobalKey and
threw a duplicate-key error. It now shows as the list pane with an empty detail
pane. Regression-tested.NavEntryDecorator.onRemoved is now symmetric across the breakpoint in
NavListDetail. onRemoved fires only for entries that were actually rendered
(as a wide pane or a narrow page), so a wide-only middle entry that was never
shown no longer gets a spurious onRemoved with no matching decorate.MultiNavStack.handleBack consumes back even when a popGuard vetoes
the pop. With history in the active tab, back is now treated as handled (so the
app stays open) instead of surfacing the vetoed pop's false to the host
PopScope — which would have closed the app.redirect/replaceAll that resolves to no destinations is caught by an assert
in debug and, in release, declined (the current stack stays) instead of leaving
the Navigator with an empty pages list.example/lib/tabs.dart (MultiNavStack bottom nav with
per-tab history) and example/lib/results.dart (pushForResult — await a value
from a pushed screen), filling the two biggest example gaps.pushOrMoveToTop/moveToTop) and the live
BackStackInspector; corrects the BackStack.of<AppKey> type argument, the
exhaustiveness claim (now that the hero leads with NavEntries), and the example
README's Pokédex description. pushForResult's Object?-cast caveat and the
NavEntry.id / entries docs are clarified.Release 0.2.6: moveToTop/pushOrMoveToTop, combineRedirects, BackStack…
Release 0.2.6: moveToTop/pushOrMoveToTop, combineRedirects, BackStack…
Convenience additions inspired by other routers — all built as plain list ops / pure functions / one widget, so the core model is unchanged (no codegen, no dependencies).
NavStack.moveToTop(test) / pushOrMoveToTop(key) — reuse a screen that's
already open instead of stacking a duplicate (singleTop/clearTop).combineRedirects([...]) — compose several redirect rules (auth,
onboarding, flags) into one redirect, each a pure proposed → RedirectStep
(ContinueRedirect / RedirectTo(stack, stop:)). Same once-per-change,
loop-proof semantics as a single redirect.BackStackInspector<K> — a zero-dep in-app widget that lists the live stack
(current entry marked), for debugging without any DevTools plumbing.Not added, on purpose: a string query-parameter bag (typed key fields already carry a destination's arguments — a stringly-typed side channel would work against that) and a route-file code generator (back_stack stays codegen-free).
Release 0.2.5: BackStackApp (one-function deep links) + BackStack.par…
Release 0.2.5: BackStackApp (one-function deep links) + BackStack.par…
BackStackApp<K> — deep links in one function. A single widget bundles the
MaterialApp.router + NavStackRouterDelegate + parser wiring; you supply only
onLink: (Uri) => [destinations]. Optional toLink projects the stack back onto
the web address bar, onLinkFallback handles bad links, and restorationScopeId
is set by default. Zero new dependencies — it drives Flutter's own Router.BackStack.parentOf<K>(context) (and maybeParentOf) — reach the stack one
level up from a nested child when parent and child share the same key type, so a
child screen can BackStack.parentOf<AppKey>(context).pop() the outer flow.
(Distinct key subtypes + BackStack.of<ParentKey> remain the more type-safe
default.)example/lib/multi_file/ now uses BackStackApp — the simplest example also
demonstrates deep links (/product/7).Release 0.2.4: README hero uses NavEntries (no switch); add simple mu…
Release 0.2.4: README hero uses NavEntries (no switch); add simple mu…
switch —
it leads with NavEntries (..on<Home>(...), builder: entries.call) over a
non-sealed AppKey, so it compiles as shown and mirrors a multi-file app. (The
previous snippet declared a sealed base with no const constructor and
inferred NavStack<Home>, so it didn't compile.)example/lib/multi_file/ — the simplest multi-file layout: app_key.dart
home_feature.dart, product_feature.dart) each with
its own destination, screen and register* call, collected in main.dart. No
switch, no central list.Fix: a pop(result) that is vetoed by guard (or collapsed to a no-op by redirect) no longer completes the destination's pushForResult future or returns
pop(result) that is vetoed by guard (or collapsed to a no-op by
redirect) no longer completes the destination's pushForResult future or
returns true. Previously the awaiter received the result while the screen was
still on top — now the result is delivered, and pop returns true, only when
the entry actually leaves the stack. (popGuard was already safe.)NavEntryDecorator.onRemoved notes that it runs during the display's
build (defer any rebuild-triggering work with addPostFrameCallback);
NavEntries documents that it matches by exact runtime type, not subtype.example/lib/modular_demo.dart (also on GitHub): three feature modules
register their own NavEntries into one map, and a single NavEntryDecorator
wraps every screen and logs teardown on onRemoved.The main example (example/lib/main.dart, the one shown on the package page) now demonstrates NavEntries (as the builder) and NavEntryDecorator (a scre
example/lib/main.dart, the one shown on the package page)
now demonstrates NavEntries (as the builder) and NavEntryDecorator (a
screen_view log + teardown hook) — so both APIs are visible on pub.dev's
Example tab. No library code changed.Add NavEntries — a registrable destination-type → screen map (..on (...)), Compose Nav3's entryProvider. Pass builder: entries.call to NavDisplay. Com
NavEntries<K> — a registrable destination-type → screen map
(..on<Home>(...)), Compose Nav3's entryProvider. Pass builder: entries.call
to NavDisplay. Composes across feature files/modules instead of one big
switch. The switch stays the default (compile-time exhaustive); this is the
modular option.NavEntryDecorator<K> — Nav3's NavEntryDecorator. decorate wraps every
screen (providers/DI scopes/tracing; first decorator is outermost) and
onRemoved fires when an entry leaves the stack (or the display is disposed) so
you can tear down a Bloc/controller/scope tied to a destination. Wire via the new
decorators: on NavDisplay — also forwarded by MultiNavDisplay,
NavSceneHost, NavListDetail, NavStackRouterDelegate and
MultiNavStackRouterDelegate.Add NavStackCodec.of(encode:, decode:, fallback:) — build a deep-link codec inline from two functions instead of subclassing NavStackCodec.
NavStackCodec.of(encode:, decode:, fallback:) — build a deep-link codec
inline from two functions instead of subclassing NavStackCodec.NavStackCodec.fallbackFor(uri) — the stack shown when a link is malformed
or unknown (defaults to decoding /; supply fallback: or override to route
to a NotFound screen).NavStackRouterDelegate now decodes without
ever throwing — a decode that throws or returns empty falls back instead of
crashing the app, so codecs can parse optimistically.pushForResult no longer hangs (and leaks its awaiter) when the push
is blocked by guard or collapsed to a no-op by redirect — the future now
resolves null, like every other way a screen can leave the stack.MultiNavStackRouterDelegate + MultiNavStackCodec bring URL
sync, deep links and OS back to a MultiNavStack (per-tab history) — the
multi-tab equivalents of NavStackRouterDelegate / NavStackCodec.RestorableMultiNavStack persists every tab's stack
and the active tab across process death. Both restorable widgets now survive a
corrupt/incompatible snapshot by keeping the freshly created stack instead of
crashing on cold start.MultiNavDisplay gains lazy (build a tab only once first selected; default
false, unchanged behavior) and observers. observers is also now forwarded
by NavSceneHost, NavListDetail and NavStackRouterDelegate — a clean
NavigatorObserver seam for screen_view analytics.NavListDetail now preserves pane State across the breakpoint. Rotating
or resizing between the two-pane and stacked layouts keeps each screen's scroll
position and controllers — every entry gets a stable GlobalKey, so Flutter
reparents the live screen instead of rebuilding it.MultiBackStack.of(context) (and MultiNavStackScope) — reach the
MultiNavStack host from any tab screen to switch tabs / read the active
index, without passing the host down by hand.Docs: the demo GIF now uses an absolute URL so it renders on the pub.dev package page (a relative path inside a raw tag wasn't rewritten).
<img> tag wasn't rewritten).Fix: Hero (shared-element) transitions now animate inside a nested NavDisplay — e.g. under NavListDetail or MaterialApp(home:). Each display drives it
Hero (shared-element) transitions now animate inside a nested
NavDisplay — e.g. under NavListDetail or MaterialApp(home:). Each display
drives its own HeroController via a HeroControllerScope, so a sprite
flies between screens instead of snapping. Previously only the root/Router
display inherited one. Regression-tested.Adaptive layout: NavListDetail (one stack → two-pane on wide, animated stack on narrow) and the general NavSceneHost + NavSceneStrategy engine (listDe
NavListDetail (one stack → two-pane on wide, animated stack
on narrow) and the general NavSceneHost + NavSceneStrategy engine
(listDetailScene, supportingPaneScene) — Compose Nav3 "scenes" over one list.MultiNavStack + MultiNavDisplay give a bottom-nav persistent
per-tab back stack (IndexedStack), with innermost-first system back.TransitionPage (.fade/.slideUp/.scale/.none),
DialogPage, SheetPage, and a per-key NavPage mixin so a destination
declares its own transition.ConfirmPopScope: async confirm-before-leave covering the Android system back.RestorableBackStack + NavKeyCodec: full-stack restoration across process
death without a URL.NavDisplay.nested flag routes the system back gesture into a nested stack.benchmark/).popGuard (sync pop veto) and refreshListenable (re-run redirect on auth
change) on NavStack.NavStack: the back stack you own. push / pop / replaceTop / replaceAll /
popUntil / edit. BackStack.of(context) reaches it from a screen (auto-provided by NavDisplay).redirect (pure transform, applied once — loop-proof) and guard
(veto).NavDisplay: renders the stack via the Pages API; system / predictive /
hardware back sync into the list automatically (onDidRemovePage).NavKey: type-safe destinations as plain Dart objects. EquatableNavKey mixin
gives them value equality so a URL re-decode reuses the live screen.replaceAll/edit reconcile so
survivors keep their State and only changed screens rebuild.pushForResult<T>() -> Future<T?>, completed by
pop([result]) or null on any other removal / dispose (never hangs/leaks).NavStackRouterDelegate + NavStackRouteInformationParser
NavStackCodec (Uri <-> List<NavKey>): web URL sync, deep links (you
choose layer-vs-replace), browser/OS/predictive back, and state restoration.leak_tracker_flutter_testing. 42 tests + benchmarks, analyze clean
under very_good_analysis (strict casts/inference/raw-types).doc/PHILOSOPHY.md maps Flutter's model + the nav leak/caveat catalog.Your coding agent can read these notes before it upgrades. Set up the MCP server →