Skip to content

Intermediate · Lists, navigation and forms · topic 25 of 50 · 30 min

Routing with go_router

Explanation

For apps with many screens, deep links and the web, use a router package. go_router, published by the Flutter team, defines routes with URL paths like /product/:id, reads path parameters from state.pathParameters, and plugs into MaterialApp.router. context.go replaces the current location; context.push adds a screen on top. Redirects handle login checks, and ShellRoute keeps a shared layout such as a bottom bar.

Why a router?

Navigator.push works well inside small apps. Larger apps need more: a URL for every screen (so the web version's address bar and browser back button work), deep links (opening a specific product from a notification or a shared link), and central rules such as "if not logged in, go to /login". A router package handles these. go_router is a popular choice maintained by the Flutter team (publisher flutter.dev).

bash
flutter pub add go_router
Adds the package to pubspec.yaml.

Defining routes

dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'product/:id',                       // full path: /product/42
          builder: (context, state) {
            final id = int.parse(state.pathParameters['id']!);
            return ProductScreen(productId: id);
          },
        ),
      ],
    ),
    GoRoute(path: '/cart', builder: (context, state) => const CartScreen()),
    GoRoute(path: '/login', builder: (context, state) => const LoginScreen()),
  ],
);

void main() => runApp(MaterialApp.router(routerConfig: router));
Each GoRoute maps a path to a screen. :id is a path parameter.

Routes can be nested: the product route above is a child of /, so its full path is /product/42, and opening it directly (from a deep link) builds Home underneath it, so the back button leads somewhere sensible. Keep the route list in one file so the whole app's map is easy to read.

Pass ids in the path (/product/42) rather than whole objects. A path survives a browser refresh, can be shared as a link, and lets the screen load fresh data. go_router also has a state.extra field for passing objects, but it is lost when the web page reloads.

Moving around

CallEffect
context.go('/product/42')Go to that location, rebuilding the stack to match the route tree (like typing a URL)
context.push('/cart')Push a screen on top of the current one; Back returns here
context.pop()Go back
context.go('/product/42?tab=reviews')Query parameters, read with state.uri.queryParameters

Redirects for login

dart
final router = GoRouter(
  redirect: (context, state) {
    final loggedIn = authService.isLoggedIn;            // your own auth state
    final goingToLogin = state.matchedLocation == '/login';
    if (!loggedIn && !goingToLogin) return '/login';
    if (loggedIn && goingToLogin) return '/';
    return null;                                        // no redirect
  },
  routes: [ /* ... */ ],
);
One place guards every screen. Use refreshListenable to re-run redirects when auth state changes.

ShellRoute for a shared bottom bar

Many apps keep a bottom navigation bar visible while switching tabs. ShellRoute (and StatefulShellRoute, which keeps each tab's own stack) wraps several routes in a shared layout, so the bar stays on screen while the content changes. Check the go_router documentation for the current API, as it evolves.

Think of it like this: go_router is like a building directory at the entrance of a large office: every room has an address (/product/42), the security guard (redirect) checks your badge before letting you go upstairs, and the lift lobby (ShellRoute) stays the same whichever floor you visit.

Common mistakes

  • Using MaterialApp(home: ...) instead of MaterialApp.router(routerConfig: router): the router is ignored.
  • Mixing context.go and context.push without understanding them: go rebuilds the stack, push adds on top.
  • Using int.parse on a path parameter without handling bad input from a deep link; show a "not found" screen instead of crashing.
  • Putting large objects in state.extra and expecting them to survive a web refresh: they do not. Pass ids in the path.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Routing with go_router · 1/6

go_router

For bigger apps, deep links and the web, use a router. go_router is published by the Flutter team.

Prefer a video by a teacher? Find free videos on “flutter go_router tutorial path parameters redirect shellroute” on YouTube ↗

In real life: Deep link from a WhatsApp message

An online store shares product links like https://shop.example.in/product/1234 on WhatsApp. With go_router and app links configured, tapping the link opens the app directly on ProductScreen(productId: 1234), or the web version on the same page. A redirect sends users who are not logged in to /login first and then back to the product.

dart
GoRoute(
  path: '/product/:id',
  builder: (context, state) {
    final id = int.tryParse(state.pathParameters['id'] ?? '');
    return id == null ? const NotFoundScreen() : ProductScreen(productId: id);
  },
)
tryParse protects against broken links.

Practice (0/3 done)

Task 1 · Question

With GoRoute(path: '/order/:orderId'), how do you read the id for /order/77?

Task 2 · Question

Which call keeps the current screen and lets Back return to it?

Task 3 · Practice

Convert a small app to go_router: routes for /, /product/:id and /login; MaterialApp.router; a redirect that sends logged-out users to /login (use a simple global bool or ValueNotifier for now); and a NotFound screen for invalid ids. Run it on the web and check that the browser URL and back button work.

Quick quiz

Question 1

1.Why use a router package like go_router?

Question 2

2.Which MaterialApp constructor works with go_router?

Question 3

3.What should a redirect return when no redirect is needed?

Question 4

4.Who publishes the go_router package?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Why use a router package like go_router?

Answer: For URL-based routes, deep links and central redirects

Especially for web and larger apps.

Which MaterialApp constructor works with go_router?

Answer: MaterialApp.router()

Pass routerConfig: router.

What should a redirect return when no redirect is needed?

Answer: null

null means stay on the requested location.

Who publishes the go_router package?

Answer: The Flutter team (flutter.dev)

It is a first-party package.

Which route handles GET /products?sort=price?Related

Answer: GET /products

The query string is not part of routing. The handler of GET /products reads it.

From Routing: sending each request to the right code

For the route /notes/:id and the URL /notes/42, what does useParams() return?Related

Answer: { id: '42' }

useParams returns an object of named params, and the values are always strings. Use Number(id) when you need a number.

From Pages and client-side routing

Which is the recommended way to pass a product id to a details screen with Navigator.push?Related

Answer: The details screen's constructor

Constructor arguments are explicit and type-safe.

From Navigator basics

Why is a frontend redirect to /login not real protection?Related

Answer: Anyone can call the API directly without using your pages

Tools like curl or a changed app can call the API directly, so only the backend check actually protects data.

From Protecting pages and API routes

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Lists, navigation and forms” before its exam: Forms and validation.
  2. Module 6 exam: Lists, navigation and forms · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, State management. 50 topics left in the course before the final exam.

← Previous: Navigator basics