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:
- Flutter Navigator 2.0: 101 for mobile dev: Where to start if you have no idea what navigator 2.0 is.
- Flutter Navigator 2.0 for mobile dev: Nested navigators basics: How to nest the navigators, and the fix needed to avoid that they ruin your animations
- Flutter Navigator 2.0 for mobile dev: Transitions: How to implement Hero or Page route transitions with navigator 2.0
- Flutter Navigator 2.0 for mobile dev: Bloc state management integration: How to use navigator 2.0 and the bloc library
- Flutter Navigator 2.0 for web dev: Url handling: The basics of URL handling.
- Flutter web: URL handling made simple with simple_url_handler: How to use the simple_url_handler package to manage the URL in flutter web easily.
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 settingsredirectFrom
is set when the user tries to accessprofile
orsettings
while not being allowed to. It hosts aInAppPages
which is eitherProfile
orSettings
isAuthenticated
which is self-explanatory. The interesting part is that it is a setter method. Indeed when authenticating (i.e., settingisAuthenticated
to true) we check ifredirectedFrom
has a value and if so, changeselectedBottomNavigationBarIndex
accordingly.
AuthenticationRouter
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.
AuthenticationRouterDelegate
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.
NestedRouter
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 theProfileWidget
/settings
when the user is on theSettingsWidget
/login
when the user is on theAuthenticationWidget
/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.