Flutter Navigator 2.0 for Authentication and Bootstrapping — Part 3: Authentication

In the second part of this series, we explored the Navigator 2.0 API, particularly the Router widget, the Page class, and the RouterDelegate class. The first sample app handles the navigation between HomePage , ColorPage , and the ShapePage . In this second sample, we add the authentication use case and build the navigation stack according to the authentication state changes. In addition to the _selectedColorCode and the _selectedShape properties, we add _loggedIn property which holds the authentication state in the RouterDelegate .

The AuthRepository

In this project, we don’t use an authentication service such as Firebase Auth. AuthRepository class is used to mock the login and logout processes with 2 seconds delay, and saves the authentication state in the local cache using the shared preferences library. When the app is launched next time, it remembers the last authentication state and shows the HomePage or LoginPage accordingly.

The App

When the app is launched, the Router widget is instantiated and located at the top of the app widget tree as in the previous sample. In this sample, we also inject AuthRepository to the app through AutViewModel class using the Provider State Management pattern.

The RouterDelegate

In this sample app, we define three Pages lists: _splashStack, _loggedInStack , and _loggedOutStack . Depending on the app stateRouterDelegate will return a Navigator widget with one of these pages list.

When the app is launched for the first time:

  • Inside the setter method of the loggedIn state notifyListeners() method is called and the Router widget is notified.
  • The Router widget asks the RouterDelegate to build a new Navigator widget with a new navigation stack based on the new app state.


The onLogin callback which is defined in the RouterDelegate is passed to the LoginPage and from there to the LoginScreen. The state management inside the LoginScreen widget is handled with AutViewModel class which has access to the AuthRepository (Note: Check Provider State Management pattern if you are not familiar).

When the user presses the login button inside the LoginScreen, the following steps happen:

  • AuthViewModel.login() method is called

Now let’s see what happens in the RouterDelegate when the onLogin callback is invoked:

  • TheloggedIn state is set true

The _loggedInStack has a list of pages that are in the same order with the same logic as in the previous sample app. The only difference is that onLogout callback method is injected into each Page as a constructor parameter.


In this sample app, a Floating Action Button (FAB) is added to HomeScreen ,ColorScreen , and to ShapeScreen . The onLogout callback is passed down to the FAB widget through these screens.

When the user presses the floating action button, the following steps happen:

  • AuthViewModel.logout() method is called

Now let’s see what happens in the RouterDelegate when the onLogout callback is invoked:

  • TheloggedIn state is set to false .


In this sample app, the RouterDelegate is informed about the authentication state changes via callback methods that are injected into the widgets. I chose to use callback methods due to demonstration purposes and easier explanation but I would not recommend this as the number of these callback methods is subject to increase over time during application development. As a result, it will be overkill to inject each one of them into the widgets through constructor parameters. We need to be smart and apply the best practices with a clean architecture.

A better approach to react to the app state changes in the RouterDelegate would be subscribing to the state changes via streams. For example:

  • When the RouterDelegate is initialized, we subscribe to the authentication state changes via a Stream.

We can also inject the use case classes to the RouterDelegate and ViewModel classes of the widgets to interact with the repositories using dependency injection libraries. Although clean architecture is out of this topic’s scope, I would strongly recommend further reading if the reader is not familiar with the topic.


In this article, we discussed how to build a navigation stack in response to the app state changes caused by authentication state updates. You can find the source code on the Github page. The project includes multiple main.dart files. The easiest way of running the sample app is right-clicking on the main_002_02.dart file and selecting the Run 'main_002_02.dart'.

In the next article, we will add the bootstrapping use case to this sample app. Special thanks to Jon Imanol Durán who reviewed all the articles in this series and gave me useful feedback. If you liked this article, please press the clap button, and star the Github repository.

Flutter and Android Mobile App Developer