Flutter Navigator 2.0: Using go_router

Go beyond Flutter’s Navigator 2.0 and learn how to handle navigation with the go_router package. By Kevin D Moore.

4.5 (15) · 9 Reviews

Download materials
Save for later
Update note: Kevin D. Moore updated this tutorial for go_router. He also wrote the original. Special thanks to Chris Sells, the author of go_router, who helped review this tutorial.

The first version of this tutorial used a navigation system that’s pretty complicated.

Many developers — and even Google — realized the same thing. As a result, some developers wrote their packages to make the process easier. Google came out with a research paper evaluating three of the packages: VRouter, AutoRoute and Beamer.

All three of them have strengths and weaknesses. AutoRoute, for example, requires code generation. Beamer looked exciting, but it’s a bit confusing. VRouter was confusing to users due to similarly named APIs that were used in different contexts.

Another option is an intuitive and easy-to-use package called go_router.

Note: In Flutter, screens and pages are called routes. In this tutorial, you’ll see the screens and pages terms used. They mean the same thing for the most part.

In this tutorial, you’ll build a shopping app brilliantly called Navigation App. Through the process of building this app, you’ll learn:

  • How to implement Flutter Navigator 2.0 with the go_router navigation package.
  • How it can provide much more granular control for your app’s navigation.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

Note: This tutorial uses Android Studio, but Visual Studio Code or IntelliJ IDEA will work fine as well.

The starter app is a set of screens for the shopping app. The UI doesn’t do much, but you’ll use it to navigate between pages. This set of pages — represented as screens — is in the image below:

Shopping App

Sneaking a Peek

The app starts with the Login page, which looks like this:

Login Screen

Run your app and verify it opens this page. The app will stay on this page since the navigation system isn’t yet implemented. Gradually, you’ll add code to navigate between all screens.

The flow will be:

  • Start at the Login screen.
  • From there, the user can log in or go to the Create Account screen.
  • At the Create Account screen, the user can create an account or go back to the Login screen.
  • Upon logging in, the user will be directed to the Home screen.
  • The Home screen will show three screens:
    • Shopping — A list of items. Selecting an item will display a details page.
    • Cart — Displays the current shopping cart.
    • Profile — This screen will show information related to payment, sign in and more.
  • Shopping — A list of items. Selecting an item will display a details page.
  • Cart — Displays the current shopping cart.
  • Profile — This screen will show information related to payment, sign in and more.

Like many apps, this one requires the user to log in first before navigating around. So, you’ll want to prevent the user from going to the home screen until they have logged in.

You could check at every point that shows a page if the user has logged in and take them to the login page if they haven’t. Or, you can use some of the nice features of these routing packages to check the login state before showing a screen. Some packages call this feature guards. They guard against using a page unless the user is authorized. go_router uses the redirect callback for this purpose.

Introducing go_router

Google introduced a new Flutter routing system that requires customized RouterDelegate and RouterInformationParser classes. Both of these classes take a lot of work to implement and still leave you scratching your head. Many developers decided there was an easier way to handle routing. Some decided to use their system, and others plugged into Google’s router system.

The go_router package uses Google’s router system but makes it easy to use. There are two main classes you need to use:

  • GoRouter
  • GoRoute

Creating a GoRouter gives you a RouterDelegate and a RouterInformationParser for free. By creating this class, you can provide an initial route and the routes you need for each screen. There is even a section called redirect that allows you to use logic to decide which route to use.

This package works on all the main platforms: Android, iOS, Mac, Windows, Linux and the Web.

Examining GoRouter Components

The GoRouter class is made up of:

  • Routes
  • Error handler
  • Redirect handler

For routes, GoRouter uses a GoRoute. This class contains a path — like a URL, an optional name that you can use instead of paths and either a page builder that returns a page or a redirect handler that redirects to another route. GoRoutes can even have sub-routes. This is where you would put pages that the parent route would call.

For example, the profile page has four pages that it launches, so it uses a sub-route for those pages. Sub-routes are a way to create a stack of pages so that the back button is shown and you can go back to the parent page. This can be as deep as you like.

You can start creating routes with the path but using names is much easier. If you were to hard-code the names in your app, you’d see login if you are using names or /login if you are using paths. If you wanted to use a deeper path, like the details page, it would look something like /main/shop/details/item. It’s easier to use names so you can use details with a parameter.

To start implementing GoRouter, open pubspec.yaml and add the package:

  go_router: ^2.2.8

Now, click the Pub get link on the top right, or from the command line type:
flutter pub get.

Implementing Router

Create a new folder in the lib directory named router. Next, create a new dart file named routes.dart. Add the following imports. This includes all the screens and the go_router package:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../ui/create_account.dart';
import '../ui/error_page.dart';
import '../ui/home_screen.dart';
import '../ui/more_info.dart';
import '../ui/payment.dart';
import '../ui/personal_info.dart';
import '../ui/signin_info.dart';
import '../constants.dart';
import '../login_state.dart';
import '../ui/login.dart';
import '../ui/details.dart';

Now, create the MyRouter class:

class MyRouter {
  // 1
  final LoginState loginState;

  // 2
  late final router = GoRouter(
    // 3
    refreshListenable: loginState,
    // 4
    debugLogDiagnostics: true,
    // 5
    urlPathStrategy: UrlPathStrategy.path,

    // 6
    routes: [
      // TODO: Add Routes
    // TODO: Add Error Handler
    // TODO Add Redirect


Here’s what’s happening in the code above:

  1. LoginState stores the user’s logged in state.
  2. You create a variable that holds a GoRouter instance.
  3. Then, you set the router to listen for changes to the loginState.
  4. Show debugging logs.
  5. Choose the path url strategy (can use hash ‘#’).
  6. Define all of the routes you’ll use.

There are several properties you can set for GoRouter. Here, you want the router to listen for changes in the login state. If the user logs out, you want the Login screen to appear. If they log in, the Home screen should appear. The debugLogDiagnostics flag is useful to see what path you’re using and to debug any problems with your routes.

Note: Make sure you remove the debugLogDiagnostics flag before shipping your app.

This defines the basics for using GoRouter. Now, you’ll need three things:

  1. A list of all the routes to your screens.
  2. Error Handler. If a route comes in (maybe from a deep link), handle an invalid route.
  3. Any redirect logic needed for redirecting to different pages based on any current state (optional).