Tutorial

Navigating Between Pages

pushNamed and pop first, then results, named locations, replacing and resetting the stack.

The two you already used

  • pushNamed(location) adds a page. The argument is a location (a path), not a route name.
  • pop([result]) removes the top page immediately. maybePop() asks first (the page's PopScope, then the route's onExit) — use it for back buttons.

There is no push(Route): every page must be in the route table so the URL can always describe the stack.

Three more verbs, one at a time

VerbIn this step
await pushNamed<int>(...) + pop(stars) The detail page opens /products/:id/reviews and waits; the reviews page calls pop(4) ; the Future completes with 4 .
namedLocation(name, pathParameters:) Builds /products/2 from the route named product , so paths are not glued together by hand.
pushReplacementNamed(location) “Next product” swaps the top page. Back still goes to the list, not to the previous product.
pushNamedAndRemoveUntil(location, (_) => false) “Back to the list” rebuilds the whole stack from a URL — the same call you will use after sign-in and for deep links.

The complete file

The route :id gets a name and a child reviews; the detail page gets three buttons; a ReviewsPage is added.

dart lib/main.dart
import 'package:material_ui/material_ui.dart';
import 'package:xue_hua_adaptive_sliding_layout/xue_hua_adaptive_sliding_layout.dart';

class Product {
  const Product(this.id, this.name, this.price);
  final int id;
  final String name;
  final double price;
}

const products = [
  Product(1, 'Coffee mug', 12),
  Product(2, 'Notebook', 8),
  Product(3, 'Desk lamp', 39),
];

Product? findProduct(String? id) {
  final value = int.tryParse(id ?? '');
  for (final product in products) {
    if (product.id == value) return product;
  }
  return null;
}

final router = AdaptiveRouter(
  initialLocation: '/products',
  routes: [
    AdaptiveRoute(
      path: '/products',
      builder: (context, state) => const ProductsPage(),
      routes: [
        // Child paths are relative: this matches /products/1, /products/2 ...
        AdaptiveRoute(
          path: ':id',
          name: 'product', // lets you build locations with namedLocation
          builder: (context, state) => ProductDetailPage(id: state.pathParameters['id']),
          routes: [
            AdaptiveRoute(
              path: 'reviews', // /products/:id/reviews
              builder: (context, state) => const ReviewsPage(),
            ),
          ],
        ),
      ],
    ),
  ],
);

void main() => runApp(const ShopApp());

class ShopApp extends StatelessWidget {
  const ShopApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(title: 'Shop', routerConfig: router);
  }
}

class ProductsPage extends StatelessWidget {
  const ProductsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Products')),
      body: ListView(
        children: [
          for (final product in products)
            ListTile(
              title: Text(product.name),
              subtitle: Text('\$${product.price}'),
              onTap: () => AdaptiveRouter.of(context).pushNamed('/products/${product.id}'),
            ),
        ],
      ),
    );
  }
}

class ProductDetailPage extends StatefulWidget {
  const ProductDetailPage({super.key, required this.id});

  final String? id;

  @override
  State<ProductDetailPage> createState() => _ProductDetailPageState();
}

class _ProductDetailPageState extends State<ProductDetailPage> {
  int? _stars;

  @override
  Widget build(BuildContext context) {
    final product = findProduct(widget.id);
    if (product == null) {
      return Scaffold(
        appBar: AppBar(),
        body: Center(child: Text('No product ${widget.id}')),
      );
    }
    final router = AdaptiveRouter.of(context);
    final next = products[product.id % products.length]; // wraps around
    return Scaffold(
      appBar: AppBar(
        leading: BackButton(onPressed: () => router.maybePop()),
        title: Text(product.name),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          Text('\$${product.price}', style: Theme.of(context).textTheme.headlineSmall),
          const SizedBox(height: 16),
          // pushNamed returns a Future that completes with the value passed to pop.
          FilledButton(
            onPressed: () async {
              final stars = await router.pushNamed<int>('/products/${product.id}/reviews');
              if (stars != null && mounted) setState(() => _stars = stars);
            },
            child: Text(_stars == null ? 'Rate this product' : 'Your rating: $_stars stars'),
          ),
          // pushReplacementNamed swaps the top page: back still returns to the list.
          OutlinedButton(
            onPressed: () => router.pushReplacementNamed(
              router.namedLocation('product', pathParameters: {'id': '${next.id}'}),
            ),
            child: Text('Next: ${next.name}'),
          ),
          // pushNamedAndRemoveUntil with (_) => false rebuilds the whole stack from a URL.
          TextButton(
            onPressed: () => router.pushNamedAndRemoveUntil('/products', (_) => false),
            child: const Text('Back to the list'),
          ),
        ],
      ),
    );
  }
}

class ReviewsPage extends StatelessWidget {
  const ReviewsPage({super.key});

  @override
  Widget build(BuildContext context) {
    final router = AdaptiveRouter.of(context);
    return Scaffold(
      appBar: AppBar(
        leading: BackButton(onPressed: () => router.maybePop()),
        title: const Text('Rate'),
      ),
      body: ListView(
        children: [
          for (var stars = 5; stars >= 1; stars--)
            ListTile(
              title: Text('$stars stars'),
              // pop(result) completes the pushNamed Future on the previous page.
              onTap: () => router.pop(stars),
            ),
        ],
      ),
    );
  }
}

What you will see

  • Detail → Rate this product → pick “4 stars”: you are back on the detail and the button reads “Your rating: 4 stars”.
  • Next: Notebook replaces the page: the URL changes to /#/products/2, and back returns to the list.
  • Back to the list clears everything above /products.

Next: Adding tabs.