Tabs with independent navigation stacks in Flutter with app_state package

Alexey Inkin
Flutter Senior
Published in
3 min readApr 3, 2022

--

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.

--

--

Alexey Inkin
Flutter Senior

Google Developer Expert in Flutter. PHP, SQL, TS, Java, C++, professionally since 2003. Open for consulting & dev with my team. Telegram channel: @ainkin_com