Flutter Navigation : Maintaining Navigation Stack in each Tab while Navigating through BottomNavigationBar
Flutter is fascinating and there’s no competition when it comes to developing beautiful UIs for the mobile. Although once in a while, we stuck on simple things that make our heads turned around. Like I did when I had to maintain the state of individual tabs while navigating through BottomNavigationBar. You can find a great tutorial on the same here, written and explained very well. Even though the article is absorbing, I found it a little complex(Personal Opinion). Then, I decided to do it another way round. I’m not saying mine is better, it’s just my way of doing things.
We’re going to create a BottomNavigationBar containing four tabs. Each tab will have Listview containing multiple items and each item will navigate to a separate route when tapped. What we have to do is to have maintained the current navigation stack and state of the tab while navigating through the BottomNavigationBar.
Step By Step :
1) Make a BottomNavBar
2) Each BottomNavBar Tab will contain a ListView
3) ListView will have a list of items.
4) Each Item in the ListView will navigate to a separate route
5) Maintain the state of the ListView navigation stack while navigating through BottomNavBar
Enough talk lets just jump into the code and see how it’s done. Before proceeding please make sure you are familiar with these things:
Here’s What we gonna do
Let’s Start
It's all about widgets so let's create our root widget App
containing MaterialApp
with Homepage()
as the home widget.
Now we’ll create a StatefulWidget
called HomePage
which will contain the BottomNavigationBar
and an IndexedStack
so that we can navigate between tabs while maintaining state of each tab. If you don’t know much about IndexedStack
please try looking at the docs once.
IndexedStack is Stack that shows a single child from a list of children while maintaining the state of each children in the stack.
Here we’re using _pageIndex
to keep track of the active index in the IndexedStack
which is being shown to the user. Our IndexedStack
contains four tabs and each tab shows aNavigatorPage
as its child.Hope everything is clear up until now.
So, now let’s look at the NavigatorPage.
Things we’re doing in NavigatorPage
- We’re using
Navigator
andMaterialPageRoute
for navigating (Will explain why) - A ListView containing a list of items.
- Each item when clicked navigate to it’s own
DetailRoute
.
So at last we’ll see DetailRoute implementation.
There’s not much happening here, we’re just showing up an AppBar
with Route number on it and a TextField
in center with TextEditingController
that we’re receiving from NavigatorPage
.
Earlier I mentioned that we’re using Navigator
and MaterialPageRoute
to navigate in NavigatorPage
. You can actually remove both of them, then NavigatorPage will look like this:
But there’s a problem with this approach, see what we got vs what we want.
Did you notice the BottomNavigationBar
in both of them? As you can see if we do not use Navigator
and MaterialPageRoute
we’ll end up having DetailRoute
without BottomNavigationBar
. Because when we don’t use Navigator
then whenever we push any route, the route will be stacked on the default Navigator
that comes with MaterialApp
.
One last thing :
We have completed almost everything that we have discussed at the start of this article. Now there’s only one issue with our code. See this
Whenever we navigate to DetailRoute
and we press the back button, our app closes suddenly. To fix this issue we need to use WillPopScope
and GlobalKey
. Try to do this by yourself, here are some hints for you:
- Go check
WillPopScope
andGlobalKey
in Flutter docs. WillPopScope
is a widget that registers a callback to veto attempts by the user whenever he/she presses back button.GlobalKey
will help you get the BuildContext of currentDetailRoute
.
If you solved this all by yourself then congratulations on your victory. If not then don’t worry you have tried atleast and here’s an applaud from my side for giving it a shot.
Now, let’s come to the solution,
- Put
WillPopScope
as a parent widget toIndexedStack
. - Declare a map containing four
GlobalKeys
insideHomePage
,one for each of the tabs. - Pass an
asynchronous
method toonWillPop
parameter ofWillPopScope
.onWillPop
receives a callback that get’s called whenever user presses back button. - We have to make sure the correct route pops out of the widget tree on back button tap.
- Pass
GlobalKeys
toNavigatorPage
- Use
GlobalKeys
received fromHomePage
as key to theNavigatorPage
.
This is what our HomePage code now looks like after all these changes.
And this will the updated NavigatorPage code.
☀️ Conclusion :
Although as a flutter developer we use Navigator (push & pop) very often but there are few use cases when we actually use Navigator as a widget and this one is the perfect scenario to use Navigator. Also we learned about GlobalKey, MaterialPageRoute, WillPopScope etc. I tried to explain as good as I can, so please pardon me if I couldn’t and let me know where I can improve.
You can view or download the source code for this article from here
Thanks for reading. If you liked this one, you should follow me for the next one. 🙏
Thank to Flutter Community for all the awesome work they are doing on StackOverflow, Github and Pub.
If you have any questions, feel free to post them 👇.
Any feedback is welcome 😃.
If you enjoyed this story, you can support me by 👏 this.