Flutter Navigator 2.0 with GoRouter
Learn about the declarative routing mechanism in Flutter using the go_router package
Navigation is a core concept in app development. Navigation refers to the interactions that allow users to navigate across, into, and back out from the different pieces of content within your app.
Table of Contents
- What is GoRouter?
- Now, What is Navigator 2.0?
- Advantages of the Navigator 2.0
- How to implement the new Navigator technique?
- Getting Started with GoRouter
- Get the Dependencies
- Repository and Screens
- Initial setup for GoRouter
- Error handling
- Navigation
- Sub routes
- Parameters and Query Parameters
- Web history and neglect tracking
What is GoRouter?
GoRouter is a declarative router for Flutter apps based on Navigation 2.0 to reduce complexity, regardless of the platform the app is running (mobile, web, or desktop), handle deep and dynamic linking, and data-driven routes while still providing an easy-to-use developer experience.
Now, What is Navigator 2.0?
The Navigator widget in Flutter is an imperative routing mechanism (Navigator 1.0), and Navigator 2.0 is the standard reference to declarative navigation and does not replace Navigator 1.0, as they can be used together. Navigator 2.0 offers a new declarative API design, Pages, for the existing Navigator widget. It also introduces a new Router widget.
- Ah, now what is imperative and declarative?
- Why bother about Navigator 2.0 when we already have a routing mechanism that works well and is easy?
This time, let’s start from the very depth. 📈
Imperative Programming is a programming paradigm that describes how a program will be executed. Developers are more concerned with the process of obtaining a solution step by step.
Whereas, Declarative Programming is a programming paradigm that expresses the logic of a computation without describing its control flow.
For example, Imperative Programming is like asking your friend how to solve a problem step by step. And Declarative Programming is like asking your friend to solve that problem, here you don’t care how he fixes the problem, you focus on the end result.
In Flutter, small applications are typically well served by simply using the Navigator API via the MaterialApp constructor’s MaterialApp.routes property.
But, more complex applications are generally served better by the Router API, which is accessible via the MaterialApp.router constructor. This takes a little more work upfront to describe how to parse deep links for your application and how to map the application state to the set of active pages, but it is more expressive in the long run.
Advantages of the Navigator 2.0
In Navigator 1.0, you can only add a page to the top of the navigation stack and remove the topmost route. The declarative mechanism, Navigator 2.0, on the other hand, gives you complete control over the navigation stack.
When the Web platform is targeted, parsing and restoring URLs add significant value to app navigation. The new API includes a component in charge of parsing URLs entered in the Web browser’s address bar and restoring URLs in the address bar as the application state changes.
How to implement the new Navigator technique?
Navigator 2.0 introduces Pages, a new declarative API design for the existing Navigator widget. It also adds a new Router widget. Customized RouterDelegate
and RouteInformationParser
classes are required for this new routing system. Both of these classes require a significant amount of effort to implement and will still leave you puzzled.
Some developers come up with their packages to make things a little bit easier and are made on top of the new declarative navigation mechanism - AutoRoute, Beamer, VRouter. All of these three have their strengths and weaknesses. For example, AutoRoute generally requires code generation. Beamer appeared exciting, but it’s a little bit ambiguous. Flutter developers were puzzled by VRouter’s similarly named APIs, which were used in different contexts.
There comes the go_router package with its intuitive and easy-to-use navigation approach. In this tutorial, we are going to see how to implement Navigator 2.0 using GoRouter and how it provides much more granular control for your app’s navigation.
Getting Started with GoRouter
In this tutorial, we are going to build a very simple application using GoRouter. The app will have three screens - LandingScreen which will be the initial screen, ColorExploreScreen which grids out a number of colored containers, and ColorShadeScreen which is responsible to list out different shades of a specific color based on an alpha value.
Get the Dependencies
In this project we require two dependencies:
- flutter_bloc: To work with a repository class. For this tutorial, making a repository class and creating a provider to manage it, wasn’t necessarily required. We could have done it in a different manner too. But good practices are good practices. Also, this makes our work a little bit easier, so we’ll make use of it.
- go_router: To reduce the complexity of the declarative routing mechanism.
Repository and Screens
Let’s create a repository class to hold a number of different colors, and also declare some helper methods. Check out the code below.
And create an instance of the repository using RepositoryProvider, as an ancestor widget of the MaterialApp.
There are three screens. Let’s discuss their routes or paths.
/
→ will represent the LandingScreen()
widget.
/colors
→ will represent the ColorExploreScreen()
widget.
/colors/:index
→ will represent the ColorShadeScreen(index: index)
widget with a specific color at index
.
The last route may also contain some query parameters
/colors/:idx?alpha={alpha}
→ will represent the ColorShadeScreen(index: index, alpha: alpha)
widget.
Now our job is to connect everything in such a way that whenever some interaction happens we call the necessary routes with the necessary parameters. And these parameters must be extracted by the widgets to show the proper result.
Initial setup for GoRouter
The go_router package has two main classes that you need to use - GoRouter and GoRoute. Creating a GoRouter constructor will give you a RouterDelegate
, RouteInformationParser
, and a RouteInformationProvider
.
For routes, GoRouter uses a GoRoute that contains a path
responsible for determining the URL, a builder
that returns a page and a redirect
handler that redirects to another route.
Error handling
GoRouter by default includes default error screens for both MaterialApp and CupertinoApp in addition to a default error screen if you’re not using either. You can set the errorBuilder
parameter of the GoRouter to replace the default error screen.
Navigation
In order to navigate from one screen to another, there are a few ways of doing it. We can navigate using GoRouter or using the Dart extension method or using the Link widget.
Sub routes
Every top-level route will generate a single-page navigation stack in GoRouter. And sub-routes are used to create a navigation stack. Now check the below snippet carefully.
In the first case, /
and /colors
both are top-level routes. Whereas, in the second case, /colors
is a sub-route of /
. And there are two ways we can navigate to the /colors
page from the home page. Either using context.go('/colors')
or context.push('/colors')
. So what is the difference between go
and push
?
Let’s take another example to understand the difference carefully.
See the /profile
and /dashboard
are the sub-routes of /user
route. So if you are on the /user
route it has started creating a navigation stack. When you navigate from /user
to /profile
route using context.go(‘/profile’)
or context.push(‘/profile’)
, the profile route will be added on top of the users route as the profile is a sub-route of the user route. A similar thing happens when you navigate from /user
to /dashboard
route.
Go and Push works differently when you try to navigate from /profile
to /dashboard
route or vice versa. Let's say you are on the /profile
route. So the navigation stack is made up with /user
and /profile
. Now if you do context.go(‘/dashboard’)
, it will jump to the target route /dashboard
by removing the /profile
route as /dashboard
is not a sub-route of /profile
.
But if you do context.push(‘/dashboard’)
from the /profile
route, it will push the target route into the already available navigation stack.
Parameters and Query Parameters
Both these terms are closely related but they are not the same at all, Parameters are set for the route, Query Parameters are values resembling variable assignment that provide extra information on what is being required for the route and it will always start with a ‘?’ on the URL, inherently they are both string values.
For example, from the route: creatures/:name
- if we were to make a request to
/creatures/alces
, then alces is the name parameter. - if we were to make a request to
/creatures/alces?familyName=Moose
, then alces is the name parameter and Moose is the familyName query parameter.
Web history and neglect tracking
Every top-level route will create a navigation stack of one page. Sub-routes create an entire stack of pages so that it becomes easy to navigate to the previous or to the next page using arrow buttons in the browser. But sometimes, you may want to neglect to stack up pages.
For example, every time you change the alpha value it creates another page in our project. So if you want to go back to the explore screen via the back button, it will go through a number of screens. So let’s neglect to stack up pages when altering the alpha values.
If you want to know more and more in detail, check out the official documentation here at gorouter.dev.
The full source code for this application can be found here 👇
Conclusion
We now have a really good idea about Navigator 2.0 and how to implement using the go_router package. We have also experimented with its declarative routing, navigations, parameters, query parameters, and sub-routing.
Let’s celebrate then! 🎉
If you have any queries or suggestions or questions, dive into the comments sections. Alternatively, you can reach out to me using Instagram, Twitter, LinkedIn, or Email.
Please remember to applaud if this article was insightful and taught you something amazing. You know you can clap 50 times 🚀using that clap button. Give it a shot.😅
That’s it. Have a great day ahead!