Introduction

back_stack

You own the back stack. Navigation is a List you push and pop — type-safe, observable, no route graph.

back_stack brings Jetpack Compose Navigation 3's "you own the back stack" model to Flutter. Destinations are plain Dart types, checked by the compiler, and the UI just renders the list.

// Destinations are typed objects — arguments are real, checked fields.
abstract class AppKey extends NavKey { const AppKey(); }
class Home    extends AppKey { const Home(); }
class Product extends AppKey { const Product(this.id); final int id; }
 
// One line per screen.
final entries = NavEntries<AppKey>()
  ..on<Home>((context, key) => const HomeScreen())
  ..on<Product>((context, key) => ProductScreen(id: key.id));
 
// The back stack is a list you own.
final stack = NavStack<AppKey>.of(const Home());
 
void main() => runApp(BackStackApp<AppKey>(stack: stack, entries: entries));

That's a complete app. Navigate by changing the list — from anywhere:

stack.push(const Product(42));    // forward
stack.pop();                      // back
stack.replaceAll([const Home()]); // reset a flow (e.g. after login)

System back, predictive back, and the hardware button already flow into the list. Everything else is one parameter on BackStackApp — the wiring is internal:

You wantYou pass
Deep links, web URLs, shareable linkslinks: — one NavLinks table
Restoration across process deathalready on with links: — see Restoration
Async link resolution ("does this doc exist?")onLinkAsync:
Links from a native plugin (app_links)initialLink: / linkStream:
Persistent chrome around the navigatorshell:
Bottom tabs with per-tab historythe sibling widget, BackStackTabsApp

And because the stack is plain data, testing is stack.push(...) then expect(stack.keys, [...]) — no harness, no mocks.

Next