Getting started

Getting started

Install

dependencies:
  back_stack: ^0.6.0

Hello world

Three pieces: typed destinations, a screen per destination, and the stack. BackStackApp renders it — that's the whole app:

import 'package:back_stack/back_stack.dart';
import 'package:flutter/material.dart';
 
// Destinations — plain Dart types. Arguments are real 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; }
 
final entries = NavEntries<AppKey>()
  ..on<Home>((context, key) => const HomeScreen())
  ..on<Product>((context, key) => ProductScreen(id: key.id));
 
final stack = NavStack<AppKey>.of(const Home());
 
void main() => runApp(BackStackApp<AppKey>(stack: stack, entries: entries));

System back, the Android predictive-back gesture, and the hardware button already flow into the stack — nothing to wire.

Navigate by changing the list

The stack is a List you own. There's no go vs push — one operation, mutate the list:

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

Navigate from a screen

Every screen can reach the stack with BackStack.of<AppKey>(context) — pass the key type so the lookup finds your stack:

onTap: () => BackStack.of<AppKey>(context).push(const Product(42)),

of doesn't rebuild on change (right for event handlers). Pass listen: true when a widget should rebuild when the stack changes.

Without BackStackApp

BackStackApp is convenience, not magic. NavDisplay is a plain widget that renders a stack — host it inside your own MaterialApp, or anywhere in an existing app:

MaterialApp(home: NavDisplay<AppKey>(stack: stack, entries: entries));

You then wire the Router yourself if you want URLs — see Deep links → Full control. Whoever creates a stack disposes it (stack.dispose()); BackStackApp leaves ownership with you.

Where to next