Navigation patterns for flutter are hard. And this is a scenario I stumbled upon and couldn’t find an agreed upon pattern to handle, so I made up one.
The scenario is this: You want to navigate to some “Views” of the app, and keep track of the current “View” for usage on some widget outside that view.
In my case, it was the
BottomNavigatonVar that needs to constantly have its
currentIndex parameter updated to the correct menu item we are viewing.
To solve this, we have 3 issues to tackle:
- Set a central place for the
- Update the
currentViewstate whenever navigating to a new “View”.
- Update the
currentViewstate whenever user hit “back” (Which is not active navigation, as the widget will not rebuild).
For a central place for the state, I chose to use BLoC provider, that will wrap my entire application.
As changing the
currentView state will probably happen all around the app, we need something better than simple
StatefulWidget . I chose BLoC, but really,
RXDart or whatever is your favorite state management will work.
Updating state when navigating to new “View”
In my opinion the routing functions are the best place to update the
currentView state. That way, you guarantee to have it happen when a routing is actively occurring.
Updating the state in other places, for example, when the “View” widget is building, is prone to bugs as the widget might rebuild even without new routing occurring.
Updating state when user goes back (navigation pop)
We need to “go back” a state whenever the user hits “back”. For that we will use the
WillPopScope widget wrapping our app. Using
onWillPop parameter to update the state accordingly.
Data structure for the history
We want to keep track of the history as a whole, for pushing new routes and being able to pop and go back to old routes state.
That calls for a stack. Unsurprisingly, if you will look at
Navigator implementation you’ll see they use
_historystack (implemented through
List) internally exactly for this purpose.
Our implementation will be similar, but limited to our scope. Instead of logging all route changes, we will log only those we care about and in format easy for us.
Show me the code
The bloc class
Several things to address here:
- The event enum is an enum that list the possible routes and additional special event
go_backthat speaks for itself.
- Having this enum separated from the routes code itself is an advantage, as it allows more flexibility for whatever usage you need. For example, having multiple app routes map to a single “View” enum.
- The event handling is pretty basic for readability. But this is the place to add code that will reject adding what is already the current route to the history, if that’s something you need.
Routing to a new route
Within the routing, we want to issue a bloc event for the appropriate view. that will look like this:
Say you are using
fluro for navigation in your app, that means having this as the first step of the handlerFunc. Like this:
Initializing the bloc, routing back handling
We need to initialize the bloc state, and coincidentally here I also take care of the route pop case.
This should wrap the part of the app that is relevant for this routing. It can be on top of the entire application if your case necessitates that.
Pretty basic, but there are some tricks here that are nice to have in one place — using
onWillPop , replicate a local history stack (I spent some time thinking
Navigation reveals something like that, and that would have been a better way. But they don’t,
_history is private) etc’ etc’.
Hope it has been useful for you.