Back
Flutter Navigation Overview and Best Practices
With Flutter, there are two methods to implement navigation; both have advantages and disadvantages, but one might be better suited than the other for your application.
Navigation is one of the most fundamental components of any app and is critical to ensuring that your app is providing a good user experience. With Flutter, there are two methods to implement navigation: the `Navigator` widget and the `Router` widget. Both these approaches have advantages and disadvantages, and one might be better suited than the other for your application.
This article will first explain why Flutter navigation is important, and then show you how to implement navigation in your Flutter apps using the Navigator widget and the Router widget.
What’s Flutter Navigation?
Let’s say that your app looks like this:
This home page has a button that leads to a login page. Now, you need to code this button so that it displays the login page upon clicking. This can be accomplished using Flutter navigation.
Through the navigation stack, which is explained fully later in the article, Flutter navigation provides the ability to show different pages to users depending on their actions, for example, clicking a button leads to the display of a different page.
Why Getting to Grips with Flutter Navigation Is Important
Understanding Flutter navigation is important and critical for a good user experience for a few reasons:
An app consists of multiple pages presenting different pieces of information. If users can’t navigate to and from those pages, they won’t be able to access all the content, rendering the app useless.
When users struggle to navigate through your app, the result is bad user experience. If you don’t understand Flutter navigation properly, you won’t be able to implement a navigation system that works effectively for your app, which ties back to optimizing the user experience.
A strong understanding of the navigation system will help you deal with technical issues that come up in developing the app. Copy-pasting code won’t help during troubleshooting.
How to Implement Flutter Navigation
Continuing with the example described above, this section will demonstrate how you can start implementing Flutter navigation.
The home page of the app that’s used an example in this article starts off with the following code:
In this example, you have one main widget called MyApp
and another widget called MyHomePage
, containing your app bar, text, and button. MyApp
references MyHomePage
for the content of the page.
With the basic code for the home page in place, you now need to work on your button so that it displays another screen upon clicking. For that, let’s explore the two navigation methods.
The Navigator Widget
As the name suggests, the Navigator
widget is the most popular option in Flutter for navigation. The two main functions you need to know for Navigator
are Navigator.push()
and Navigator.pop()
.
However, before you dive into using these functions, you need to understand a fundamental concept, the navigation stack.
You can think of the navigation stack as a pile containing the pages in your app. The page at the top of the bundle will be displayed to the user, while the others remain out of sight.
When you push
a page, it goes to the top of the navigation stack, and that’s what the user sees. Using pop
, you remove the page at the top of the navigation stack, displaying the page underneath it to the user. So you can either add a page on the stack for navigation between two pages or remove one from the stack.
By understanding the role of the navigation stack, you’ll be able to effectively use the functions like push
and pop
as a subset of the Navigator
widget. Let’s continue with the use case and implementation now.
The Navigator Widget Use Case
You should use the Navigator
widget when:
You don’t need to preserve the state of the underlying pages on the navigation stack;
Nor do you need to store the browsing history of the user; and
You just want to pass simple data to other pages.
This widget provides a straightforward and clean way to navigate between pages on a mobile app where the user cannot directly access the page URLs. You’ll be able to add navigation without writing too much code or logic.
Navigator.push()
You can add pages to the call stack with the Navigator.push()
function. For example, if you want to go from the home page to the login page, you should use the push()
function as follows:
For the navigation to work, you only need to edit the ElevatedButton()
, because that’s how the user will hop from one page to another.
Note: For reference, here’s an unannotated version of the same screenshot.
In the onPressed()
property of the button, you added a Navigator.push()
function where we have passed the context
and the MaterialPageRoute
widget.
The MaterialPageRoute
helps create a route object that can be pushed onto the navigation stack. You define LoginPage()
as the destination, which is a widget in your login.dart
file for the login page.
As you can see, this process is relatively straightforward. Now, let’s look at how you navigate back to other pages on the navigation stack.
Navigator.pop()
This simple function is used to go back to the previous route on the stack. On your login page, you add a simple button using the following code:
You can add the functionality to go back to the home page upon clicking the button with the following modifications to the button code:
A short line inside the onPressed()
function is all that’s needed to add this functionality.
The Router Widget
Flutter Navigator 2.0 introduced the Router
widget to take a more declarative route approach. Its main aim is to help sync the browser URL to the app pages.
The Router Widget Use Cases
The Router
widget is what will likely come in handy if you’re using Flutter for web app development.
At first, the widget can be challenging to master because it has many essential nested functions that you need to understand, and it requires many more lines of code even for simple navigation compared to the Navigator
widget.
However, if your app has any type of content and you want to save the history of every single interaction, then you should use the Router
widget. For example, if you’re building a blog and you want to store the URLs of the user’s visited blog posts from their history. Or, let’s say you showcase videos on your site and you want to store the URLs of videos that users have watched on your website from their history.
How Does It Work?
As we mentioned, the Router
widget is complicated. In fact, despite Flutter trying to make routing more straightforward with this widget, it’s probably more complex than it needs to be.
The widget contains several subsections, which can be explained as follows:
Router: A new widget that dispatches the opening and closing of pages in your Flutter app. It wraps around the
pages
attribute in theNavigator
widget to automatically modify it upon the app’s state changes.Route name provider: This delegate assists the router in actually understanding which routes to show according to the operating system’s request.
Route name parser: Parses the string from
routeNameProvider
into a user-specified data type.Router delegate: This is the main component of the
Router
widget. TherouterDelegate
determines how to rebuild the navigation stack to display the pages.Back button dispatch: Tells the app to rebuild the
Router
to go back after the system back button has been pressed.
In more simple terms , this widget handles navigation in the following way:
A user interacts with the app;
The interaction modifies the state of the app;
The app state notifies the
Router
widget; andThe
Router
widget rebuilds theNavigator
to show the new route.
The implementation of many of the functions is left to the developer. You can read more about Flutter’s complex navigation and routing system.
Navigation Best Practices
Now that you know about implementing navigation, it’s time to learn about some coding standards and best practices.
Named vs. Anonymous Routes
Navigator.push()
and Navigator.pop()
are used for anonymous routes, where you define the widget you want to redirect the user to.
Anonymous routes push and remove elements from the top of the stack, and the state of the underlying objects is left untouched.
Navigator.pushNamed()
is used for named routes where you define a routes
map containing the URL and the widget linked to it.
Third-Party Libraries
A principal rule of software development is to build upon existing solutions rather than writing everything from scratch.
To minimize repetition and work, you can choose from the thousands of Flutter third-party libraries that are available.
Also, most libraries are pre-tested and verified, so you can be confident that the solution you’ll be using will be one of the best ones. Apart from saving time, third-party libraries are also a huge cost saver.
Minimize Complexity
The end user doesn’t care about what technologies and methods you’ve used to build the app. All the user wants is a functional experience. As a developer, it’s your goal to implement the simplest solutions possible instead of more complex ones for marginal improvements. In short, you should use an elaborate system only if the resulting improvement is huge.
For Flutter navigation, using the Router
widget is only recommended if your intended use perfectly matches its targeted use cases. Otherwise, for most purposes, the Navigator
widget will get the job done satisfactorily without introducing a ton of unnecessary complexity.
Conclusion
This article introduced two different methods of implementing navigation in Flutter. You can either use the old, imperative, but more accessible approach of the Navigator
, or use Navigator 2.0’s new, but more complex procedure, like the Router
.
It mostly boils down to your particular use case and preference, and ultimately, ensuring that your chosen method provides a great experience for your users is what matters the most!