back_stack 0.2.8
back_stack: ^0.2.8 copied to clipboard
You own the back stack. Navigation is a list you push and pop — type-safe, observable, no route graph.
0.2.8 #
- Async deep links from native. The platform's
Routeronly 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 warmapp_linkslink — come from a native plugin as aStream<Uri>.BackStackAppnow takes an optionallinkStream: hand it that stream and every emission runs through the sameonLink, with the same never-throws fallback as a platform link. back_stack owns the subscription lifecycle and cancels it on dispose; you bring theUris from whatever plugin you use — no new dependency. NavStackRouterDelegate.handleLink(uri)/MultiNavStackRouterDelegate.handleLink(uri)— the imperative sibling ofsetNewRoutePath, for driving your own router: apply a runtime link to the stack with the identical decode-or-fallback hardening. (setNewRoutePathnow delegates to it, so platform and runtime links share one code path.)
0.2.7 #
A correctness + documentation pass. No API changes — every fix is behavior that matches what the docs already promised.
- Fix —
NavListDetailno 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-entryGlobalKeyand threw a duplicate-key error. It now shows as the list pane with an empty detail pane. Regression-tested. - Fix —
NavEntryDecorator.onRemovedis now symmetric across the breakpoint inNavListDetail.onRemovedfires 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 spuriousonRemovedwith no matchingdecorate. - Fix —
MultiNavStack.handleBackconsumes back even when apopGuardvetoes 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'sfalseto the hostPopScope— which would have closed the app. - Hardening — an empty stack is refused rather than crashing in release. A
redirect/replaceAllthat resolves to no destinations is caught by an assert in debug and, in release, declined (the current stack stays) instead of leaving theNavigatorwith an empty pages list. - Examples: adds
example/lib/tabs.dart(MultiNavStackbottom nav with per-tab history) andexample/lib/results.dart(pushForResult— await a value from a pushed screen), filling the two biggest example gaps. - Docs: README gains a table of contents and runnable snippets for results,
per-tab nav, reusing an open screen (
pushOrMoveToTop/moveToTop) and the liveBackStackInspector; corrects theBackStack.of<AppKey>type argument, the exhaustiveness claim (now that the hero leads withNavEntries), and the example README's Pokédex description.pushForResult'sObject?-cast caveat and theNavEntry.id/entriesdocs are clarified.
0.2.6 #
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 oneredirect, each a pureproposed → RedirectStep(ContinueRedirect/RedirectTo(stack, stop:)). Same once-per-change, loop-proof semantics as a singleredirect.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).
0.2.5 #
BackStackApp<K>— deep links in one function. A single widget bundles theMaterialApp.router+NavStackRouterDelegate+ parser wiring; you supply onlyonLink: (Uri) => [destinations]. OptionaltoLinkprojects the stack back onto the web address bar,onLinkFallbackhandles bad links, andrestorationScopeIdis set by default. Zero new dependencies — it drives Flutter's own Router.BackStack.parentOf<K>(context)(andmaybeParentOf) — reach the stack one level up from a nested child when parent and child share the same key type, so a child screen canBackStack.parentOf<AppKey>(context).pop()the outer flow. (Distinct key subtypes +BackStack.of<ParentKey>remain the more type-safe default.)example/lib/multi_file/now usesBackStackApp— the simplest example also demonstrates deep links (/product/7).
0.2.4 #
- Docs: the README hero snippet is corrected and no longer uses a
switch— it leads withNavEntries(..on<Home>(...),builder: entries.call) over a non-sealedAppKey, so it compiles as shown and mirrors a multi-file app. (The previous snippet declared asealedbase with noconstconstructor and inferredNavStack<Home>, so it didn't compile.) - Adds
example/lib/multi_file/— the simplest multi-file layout:app_key.dart- one file per feature (
home_feature.dart,product_feature.dart) each with its own destination, screen andregister*call, collected inmain.dart. Noswitch, no central list.
- one file per feature (
0.2.3 #
- Fix: a
pop(result)that is vetoed byguard(or collapsed to a no-op byredirect) no longer completes the destination'spushForResultfuture or returnstrue. Previously the awaiter received the result while the screen was still on top — now the result is delivered, andpopreturnstrue, only when the entry actually leaves the stack. (popGuardwas already safe.) - Docs:
NavEntryDecorator.onRemovednotes that it runs during the display's build (defer any rebuild-triggering work withaddPostFrameCallback);NavEntriesdocuments that it matches by exact runtime type, not subtype. - Adds
example/lib/modular_demo.dart(also on GitHub): three feature modules register their ownNavEntriesinto one map, and a singleNavEntryDecoratorwraps every screen and logs teardown ononRemoved.
0.2.2 #
- The main example (
example/lib/main.dart, the one shown on the package page) now demonstratesNavEntries(as the builder) andNavEntryDecorator(ascreen_viewlog + teardown hook) — so both APIs are visible on pub.dev's Example tab. No library code changed.
0.2.1 #
- Add
NavEntries<K>— a registrable destination-type → screen map (..on<Home>(...)), Compose Nav3'sentryProvider. Passbuilder: entries.calltoNavDisplay. Composes across feature files/modules instead of one bigswitch. Theswitchstays the default (compile-time exhaustive); this is the modular option. - Add
NavEntryDecorator<K>— Nav3'sNavEntryDecorator.decoratewraps every screen (providers/DI scopes/tracing; first decorator is outermost) andonRemovedfires 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 newdecorators:onNavDisplay— also forwarded byMultiNavDisplay,NavSceneHost,NavListDetail,NavStackRouterDelegateandMultiNavStackRouterDelegate.
0.2.0 #
- Add
NavStackCodec.of(encode:, decode:, fallback:)— build a deep-link codec inline from two functions instead of subclassingNavStackCodec. - Add
NavStackCodec.fallbackFor(uri)— the stack shown when a link is malformed or unknown (defaults to decoding/; supplyfallback:or override to route to a NotFound screen). - Harden the deep-link boundary:
NavStackRouterDelegatenow decodes without ever throwing — adecodethat throws or returns empty falls back instead of crashing the app, so codecs can parse optimistically. - Fix:
pushForResultno longer hangs (and leaks its awaiter) when the push is blocked byguardor collapsed to a no-op byredirect— the future now resolvesnull, like every other way a screen can leave the stack. - Web + tabs:
MultiNavStackRouterDelegate+MultiNavStackCodecbring URL sync, deep links and OS back to aMultiNavStack(per-tab history) — the multi-tab equivalents ofNavStackRouterDelegate/NavStackCodec. - Restoration + tabs:
RestorableMultiNavStackpersists 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. MultiNavDisplaygainslazy(build a tab only once first selected; defaultfalse, unchanged behavior) andobservers.observersis also now forwarded byNavSceneHost,NavListDetailandNavStackRouterDelegate— a cleanNavigatorObserverseam forscreen_viewanalytics.NavListDetailnow preserves paneStateacross the breakpoint. Rotating or resizing between the two-pane and stacked layouts keeps each screen's scroll position and controllers — every entry gets a stableGlobalKey, so Flutter reparents the live screen instead of rebuilding it.- Add
MultiBackStack.of(context)(andMultiNavStackScope) — reach theMultiNavStackhost from any tab screen to switch tabs / read the active index, without passing the host down by hand.
0.1.1 #
- Docs: the demo GIF now uses an absolute URL so it renders on the pub.dev
package page (a relative path inside a raw
<img>tag wasn't rewritten).
0.1.0 #
- Fix:
Hero(shared-element) transitions now animate inside a nestedNavDisplay— e.g. underNavListDetailorMaterialApp(home:). Each display drives its ownHeroControllervia aHeroControllerScope, so a sprite flies between screens instead of snapping. Previously only the root/Router display inherited one. Regression-tested.
0.0.1 #
- Adaptive layout:
NavListDetail(one stack → two-pane on wide, animated stack on narrow) and the generalNavSceneHost+NavSceneStrategyengine (listDetailScene,supportingPaneScene) — Compose Nav3 "scenes" over one list. - Multi-stack:
MultiNavStack+MultiNavDisplaygive a bottom-nav persistent per-tab back stack (IndexedStack), with innermost-first system back. - Custom pages:
TransitionPage(.fade/.slideUp/.scale/.none),DialogPage,SheetPage, and a per-keyNavPagemixin 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.nestedflag routes the system back gesture into a nested stack.- Minimal rebuilds: pages are memoized by entry id, so one push rebuilds exactly
one screen regardless of stack depth (see
benchmark/). popGuard(sync pop veto) andrefreshListenable(re-runredirecton auth change) onNavStack.NavStack: the back stack you own. push / pop / replaceTop / replaceAll / popUntil / edit.BackStack.of(context)reaches it from a screen (auto-provided by NavDisplay).- Auth gating:
redirect(pure transform, applied once — loop-proof) andguard(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.EquatableNavKeymixin gives them value equality so a URL re-decode reuses the live screen.- Identity & State preservation: stable unique id per entry (unique page key);
duplicate destinations are independent;
replaceAll/editreconcile so survivors keep their State and only changed screens rebuild. - Result passing:
pushForResult<T>()->Future<T?>, completed bypop([result])or null on any other removal / dispose (never hangs/leaks). - Router integration:
NavStackRouterDelegate+NavStackRouteInformationParser- a
NavStackCodec(Uri <-> List<NavKey>): web URL sync, deep links (you choose layer-vs-replace), browser/OS/predictive back, and state restoration.
- a
- Leak-safe: leaving the stack disposes the route (proven by test); suite runs
under
leak_tracker_flutter_testing. 42 tests + benchmarks, analyze clean undervery_good_analysis(strict casts/inference/raw-types). - Docs:
doc/PHILOSOPHY.mdmaps Flutter's model + the nav leak/caveat catalog.