Tabs with independent navigation stacks in Flutter with app_state package
This is a part of the series on app_state
package. The beginning is here.
In the previous part, we set up navigation and web support with URLs. Now let’s do multiple tabs with independent navigation stacks in each of them. In addition to the book list, we will have another tab:
When we switch to it and back, the stack in the first tab is preserved for us.
Take a look at the example project found here. This is the structure with new files highlighted:
Let’s walk through what has changed from the previous part.
PageStacksBloc
The project has changed its heart. Instead of PageStackBloc
we now use PageStacksBloc
. It contains multiple page stacks with each having a string key. It also may consider one of them current.
Each stack has its own bottom page that is always there. Each stack uses the same page factory which will potentially allow any page to show on any stack. This is the key to Instagram-like navigation where a profile may show on the Likes tab, or on the Explore tab, or any other one. But we are not going to play with this.
Also note that we changed the back button dispatcher class to work with this bloc.
PageStacksRouterDelegate
The router delegate has changed. Previously, we were using PageStackRouterDelegate
that took care of building the Navigator
widget and updating it on some page events. Now we use the new class MaterialPageStacksRouterDelegate
(note the “s”). It still takes care of updating on events, but it has no idea how we want to present the stacks. Tabs are common, but we may also want to run split-screen, so no common solution here. So it must be given a home screen.
HomeScreen
In the home screen, we listen to events in PageStacksBloc
to rebuild. We may create any arrangement of the stack, but we will go with tabs with bottom navigation buttons:
Here we are using KeyedStack
and KeyedBottomNavigationBar
widgets. These are convenient alternatives to IndexedStack
and BottomNavigationBar
provided by keyed_collection_widgets package. They abstract the indexes so we do not have to convert indexes to stack keys and vice versa. There is however a minor inconvenience that we use TabEnum
for tabs and String
for stack keys. But this conversion is much more straightforward than with int
.
Push pages
We can push a page to the current page stack like this:
This will also help to mimic Instagram. Call this in any reusable widget, like a profile picture, and the new screen will show in that current tab.
PageStacksRouteInformationParser
We used to extend PageStackRouteInformationParser
to parse URLs into a stack configuration. Now we extend PageStacksRouteInformationParser
(note the “s”).
The difference is that the latter recovers state for all stacks.
Provide default stack to PagePath objects
When URL is typed in, it is converted to PagePath
object which is used to create stacks of pages. With single stack, each path was populating that one stack. With multiple stacks, each path must know what tab should be active if it was the entry point to the app. Otherwise an exception is thrown.
For this, override defaultStackKey
getter in each PagePath
:
And we are good to go. You may run the app to see how tabs switch.
In Android, the back button always closes the current screen and does not lead to the previously selected tab. However, in browser, the back button will take you to a previously selected tab as this is how browsers are supposed to work. Traditionally their back-forward navigation has to do with history and not with hierarchy.
In the next tutorial, we will add a modal dialog and wait for its result. This has significant perks with app_state
.