Navigation and Routing

Unless you’re building a single-page app, you’ll need to learn some APIs so you can navigate between screens.

And in this lesson, I’ll give you an overview of the most common navigation techniques you can use in Flutter.

We’ll start by reviewing the imperative navigation APIs that were first introduced in Flutter (also known as Navigator 1.0). 👇

Basic Navigation with Push/Pop

As long as you don’t need to do anything too complex, Flutter provides a simple API for navigating to a new screen and back:

In very simple terms, navigation is performed by:

  • Getting a Navigator object for the current context (e.g. Navigator.of(context)).
  • Calling push() to add a new route to the navigation stack. This is most commonly done with a MaterialPageRoute that builds the widget/page you want to show.
  • Calling pop() to return to the previous route once done.

When using the basic push() and pop() methods, it is also very easy to pass arguments to the new route and return a value when it is dismissed.

Basic Navigation with Named Routes

But when you have many screens in your app, it is more appropriate to define a set of named routes so you can centralize all your routing logic in the same place:

As the article above shows, this is done by defining your application’s routes inside your MaterialApp widget (or, alternatively, using onGenerateRoute).

Passing Arguments to Named Routes

Once you’re comfortable with named routes, the next thing you’ll want to do is pass arguments to them. And this is all explained in detail here:

Note that if you want to pass two or more arguments, you can store them in a new class (more boilerplate) or a map (not type-safe), neither of which is ideal.

To overcome some of the inherent issues with passing arguments to named routes, packages such as auto_route have been created. These packages leverage code generation to provide a more type-safe and user-friendly navigation API.

Whether you like code generation or not, the original Flutter Navigator APIs had some limitations, and new Navigator 2.0 APIs were introduced to overcome them.

But what are the limitations of Navigator 1.0?

  • Arbitrary changes to the navigation stack are hard because push() and pop() only apply to the topmost route.
  • Deep linking from push notifications is hard as it requires changing the entire navigation stack in a way that is not practical using push() and pop()
  • Restoring the app navigation state on app restart is also hard for the same reasons.

Navigator 2.0 solves these issues by introducing a declarative API that is backwards compatible with the imperative API of Navigator 1.0.

You can read all about Navigator 2.0 and how it works here:

However, people soon realized that the new Navigator 2.0 APIs were very hard to use.

Other Navigation & Routing Packages

To overcome some of the complexity of Navigator 2.0, the community has created various packages that offer a simpler API and make declarative navigation easy.

As of today, the three most popular packages are:

Out of them, my personal favourite is go_router, as it has a great API and is officially maintained by the Flutter team.

However, it is not without faults. The original documentation by Chris Sells hasn’t been updated since version 3.0, and a good replacement is still lacking.

To address this, I’ve created some articles to cover some specific use cases with GoRouter:

GoRouter also plays nice with deep links, which are a way to send users to a specific page within your app. To learn more about deep links, read:

Undoubtedly, GoRouter is a big project, and I hope the Flutter team will continue to improve it.

As always, the best tool for the job is the one that satisfies all your requirements. So feel free to explore the packages above and choose the one you like the most.

Bottom line

  • use Navigator 1.0 if your app only needs to perform simple navigation with push() and pop() (including named routes).
  • use Navigator 2.0 if you need to handle deep links, perform state restoration, or make arbitrary changes to the navigation stack.
  • use a good 3rd party package if you find Navigator 2.0 too complex, but you still need a declarative API.

Daily Challenge - Detail Navigation with Hero widget

Create a simple app with simple main and detail screens, and get some practice working with the Hero widget.

The main screen should hold a GridView showing the numbers from 0 to 9. When an item is selected, the app should transition to the detail screen and show the corresponding number in the middle of the screen.

Feel free to implement this with the Flutter navigation APIs or any 3rd party navigation package you choose.

See you in the next lesson, where we will discuss interactivity and handling user input.

Questions? Let's chat