Flutter Navigator 2.0 for Authentication and Bootstrapping — Part 1: Introduction
By: Cagatay Ulusoy
Tags: Bootstrapping, Flutter, Dart, Navigator, Authentication

The Navigator 2.0 API has been one of the most controversial APIs I have seen in my software development career. In this series of articles, I aim to convince the readers to use this API in their projects, because it is more Flutterish, it helps to separate concerns and improves the user experience of Flutter Web applications.

The keywords of this series would be declarative API, navigation stack (history stack), Router widget, Navigator widget, app state, and system events. Considering these keywords, I believe the Navigator 2.0 API is summarized best by Micheal Goderbauer:

“Introduce a declarative API to set the history stack of the Navigator and a new Router widget to configure the Navigator based on app state and system events.”

When it was announced, many Flutter devs, including me, initially found this new way of navigation complicated and hard to use due to the increased number of classes, lack of training materials and long documentation. The Flutter team is well aware of this situation, and they started Navigator 2.0 API Usability Research. Anyone can contribute to this research project and engage in the discussions by adding comments to the Github issue.

I was too close to give up on migrating to the new Navigator API too, but I wanted to get benefit from it for my side project to provide a better Web UX. After going through the source code, reading the Github discussions, and experimenting a lot with a demo project, I would like to share my learnings in this series of articles.

Too much information (taken from specs)

I call the old Navigator API as Imperative Navigator API and the Navigator 2.0 API as Declarative Navigator API . As a mobile developer who has worked with Android’s imperative ways of coding for years, I believe getting used to the declarative APIs requires time and practice.

The declarative paradigm is getting very popular nowadays considering the investment by Apple in SwiftUI and by Google in Flutter, and Jetpack Compose. Although the Android team introduced Jetpack Compose toolkit for building native UI with a declarative paradigm, as of today, the navigation is still handled imperatively.

In my opinion, the Flutter team presented the Navigator 2.0 API with an article that gives too much information to digest at once. I think the article could have been split into smaller parts and covered more common scenarios such as authentication, bootstrapping, deep-link handling, etc. I am sure many blog posts from the Flutter community will help to close this gap. I also hope that this series of articles and the sample apps will be useful for the community.

You can find the source code for the sample apps of this series on my Github page. The implementation details of the sample apps will be explained in the following articles:

Why Navigator 2.0?

Although the Navigator 2.0 API is known to be declarative, it still supports the existing static imperative methods (pop, push, replace, etc.) of the Navigator widget. After reading different articles, I came up with the following reasons to use the new declarative Navigator API:

The new API is more similar to how we build widgets in Flutter. We declare the widget’s subtree and return it within the build method. In this declaration, we set the properties of the subtree depending on the widget state. When we set the widget’s state, the build method is called and the subtree is rebuilt accordingly. Similarly, with Navigator 2.0, we build the entire history stack like a widget subtree according to app state updates and system events. This gives us more flexibility to modify every entry in the history stack, unlike the imperative Navigator API.

Parsing and restoring URLs bring significant value to the app navigation when the Web platform is targeted. The new API introduces a component that is responsible for parsing URLs entered in the Web browser’s address bar and restoring URLs on the address bar according to the application state changes.

Instead of describing how to navigate inside the Widgets or in different parts of the application using static imperative API methods, the declarative API introduces the Router widget which makes decisions in a declarative way based on the app state and operating system events. This enables us to apply the most important software development principle: Separation of Concerns. By doing this we move the navigation logic from Widgets and controller classes to the Router widget and its delegates. Building a navigation stack from a central place gives more flexibility and an easier maintenance experience to the developers.

Sample Apps

In this series of articles, using the Navigator 2.0 API in Flutter projects is demonstrated with sample applications that are built incrementally. In the first three samples, we focus on building a navigation stack as a result of user interaction.

In the last sample, we focus on two things:

Handling the new route requests from the OS for example by entering a Web URL on the Web browser’s address bar.

Updating the Web browser’s address bar according to app state changes for example by clicking a button.

1. User Interaction

The first sample has 3 screens: Home, Color, and Shape.

The Home screen lists the Material Design’s 2014 colors.

Clicking a colored button on the Home screen will navigate the app to the Color screen where buttons with the selected color are shown with the following ShapeBorder classes: BeveledRectangleBorder , RoundedReactangleBorder , ContinousRectangleBorder , StadiumBorder , and the CircleBorder .

classes: , , , , and the . Clicking a shape border type will navigate the app to the Shape screen which shows only a button with selected shape border and color.

2. Authentication

The second sample adds the authentication use case to the first sample.

If the user is not logged in, the app first shows a simple Login screen that has only a button.

Clicking the button first navigates the app to the Splash screen which will be shown until the authentication process is done. A delay is added to mock the authentication process.

Once the process is done, the app navigates to the Home screen. In this sample, Home, Color, and Shape screens have logout floating action button so that when pressed, the user is logged out and the app is navigated back to the Login screen.

3. Bootstrapping

The third sample adds the bootstrapping use case to the second sample. In the previous samples, the list of colors (data) is accessed immediately. However, this is usually not the case in the real world. In many apps, once a user is logged in, a Splash screen is shown while the data is being loaded from the local and remote data sources. In this sample, when the authentication process is done and the user is logged in, a delay is added to mock the process of fetching data from a repository. The app waits until the color list data is ready before navigating to the Home Screen. When the user is logged out, a delay is added to mock the process of clearing the user data.

4. Web

The fourth sample is for improving the navigation for the Web platform by handling the URLs properly. In this sample, we will navigate to the Color and Shape screens by typing the URL to the address bar of the Web browser and update the URL on the address bar according to application state changes.

Conclusion

In this article, we had an introduction to the declarative navigation API and explained the sample apps that we will be building incrementally throughout this series. In the next article, we will explore how to build a navigation stack according to the app state changes with the Navigator 2.0 API.

If you liked this article, please press the clap button, and star the Github repository. I would like to thank to Jon Imanol Durán who reviewed initial versions of all the articles in this series and gave me useful feedback.