Flutter web: A complete example using simple_url_handler

This article is part of a series aiming at making you a master of Navigator 2.0 while showing you that it is not as hard as people think. The other articles of this series are:

Navigator 2.0 received some negative feedback when it came out. However, today we will see how:

  • Routing is not as hard as it looks.
  • Url handling is made very easy by the package simple_url_handler

The app we will create

App architecture

We will need 3 main widgets and 2 routers.

The app state

The state of the app will be stored in a ChangeNotifier provided by a ChangeNotifierProvider at the top of the app. Here is the app state class:

Three main attributes describe this class:

  • selectedBottomNavigationBarIndex: 0 is the profile, and 1 is the settings
  • redirectFrom is set when the user tries to access profile or settings while not being allowed to. It hosts a InAppPages which is either Profile or Settings
  • isAuthenticated which is self-explanatory. The interesting part is that it is a setter method. Indeed when authenticating (i.e., setting isAuthenticated to true) we check if redirectedFrom has a value and if so, change selectedBottomNavigationBarIndex accordingly.


As always with navigator 2.0, we create a Router widget, which will contain our RouterDelegate (which is basically its child).

We also create a RootBackButtonDispatcher because we cant to handle the AppBar back button.


In the build method, we create a Navigator (to which we give a global key for page animations). The pages are MaterialPages containing either the AuthenticationWidget or the NestedRouterWidget. Note how we declarative decide if we want to show the NestedRouterWidget with the isAuthenticated boolean from our app state.

In the onPop method, we set isAuthenticated to false if we pop on the AuthenticationWidget. This happens when we press the AppBar back button, for example.

The popRoute method is triggered if we want to pop the AuthenticationWidget, in that case, we stop the application gracefully with the move_to_background package.

Note that we use obeservers: [HeroController()] because we need it to avoid breaking hero animations in the nested navigator. Since SimpleUrlHandler will create a navigator, this is a nested navigator, so we add this.


This nested delegate is pretty much the same as the authentication one. Note how easy it is to nest navigator, nothing special: nest them.

We create a BottomNavigationBar on top of our navigator so that it is shared between the two pages, and inside the navigator, we choose which Widget to display with the selectedBottomNavigationBarIndex.

Also, note how we return false in the onPopPage method. This is because we handle the pop in the AuthenticationRouterDelegate. Since we took priority in line 16, a pop event will first be triggered here, and then, if we return false, the next onPopPage event will be triggered (so in our case, the AuthenticationRouterDelegate one).

Url Handling

Concerning the URL it will be handled as follow:

  • /profile when the user is on the ProfileWidget
  • /settings when the user is on the SettingsWidget
  • /login when the user is on the AuthenticationWidget
  • /login#profile when the user tries to access /profile but is not authenticated
  • /login#settings when the user tries to access /settings but is not authenticated

This can be easily achieved thanks to the simple_url_handler package.

The first interesting part is happening line 21–22. Indeed, we add a listener to the app state, which will call SimpleUrlNotifier.of(context).notify any time the app state changes.

Then we must provide urlToAppState and appStateToUrl to the SimpleUrlHandler:

urlToAppState is called whenever the url is typed manually or the forward/backward button is pressed. When it is called, we parse the route location and modify our app state accordingly. Note that if the appState is updated, appStateToUrl is called thanks to what we just saw. So any redirection will update the url. Also, note that if that state can not be changed to a valid one, we SimpleUrlNotifier.of(context).notify() to update the url back to a valid one.

appStateToUrl is called when SimpleUrlNotifier.of(context).notify() is called or when the SimpleUrlHandler is built. Here we use our AppState class to determine what should be the corresponding url.

Try it yourself

You can find the code for this app on github. Play around with the code, and you will soon realize that navigator 2.0 is a beauty.