Creating a BottomNavigation Multi-Stack using child Fragments with Simple-Stack

Gabor Varadi
4 min readJan 25, 2021

It’s been a recurring question, regardless of navigation framework of choice: how is it possible to create a screen with a bottom navigation view, that would host a navigation history stack per each tab?

Each framework has its own unique ways. For example, Jetpack Navigation provides NavigationExtensions as a sample, there’s FragNav, but how would you do it with Simple-Stack?

How bottom navigation with child fragments normally works

If you intend to create a BottomNavigationView that is hosted by a Fragment (and that Fragment hosts each tab as a child fragment), there’s a bit of work needed to be done. To keep the fragments alive while switching between them, they must all be added. However, to make sure that the fragments are in the correct lifecycle (stopped while not showing), it makes sense to detach them (and attach the one that is meant to be showing).

This example is also available here.

Please note that there is nothing specific to Simple-Stack in this snippet, this is how a fragment can manage its child fragments and keep only one of them attached, while the rest are detached.

In fact, the NavigationAdvancedSample works the same way.

We will use this knowledge later in the article.

What Simple-Stack normally does

When you use Simple-Stack, you typically install a Navigator using Navigator.install(). This allows a single global Backstack instance to be accessible through the Activity’s context (and thus making it available to all views within the Activity), and provides automatic lifecycle management to ensure that navigation is handled correctly.

Simple-Stack basic fragment setup

This is important to know, because if we want to create a Backstack for each Fragment within our BottomNavigationView, we must manage the lifecycle (and keep the Backstack instance alive across configuration changes) ourselves.

Thankfully, this is actually quite simple to do! After all, fragments give us all the callbacks we need for the lifecycle management, while ScopedServices can help us keep stuff alive across config changes.

Keeping the Backstack alive for each Fragment

To keep the backstack alive, I’ll introduce something called a FragmentBackstackHost. It’s really just responsible for retaining the fragment across configuration changes (and also to intercept back presses for the current selected tab).

FragmentStackHost

Managing the lifecycle of nested Backstacks

Now we need to manage the lifecycle of this Backstack instance from a Fragment:

FragmentStackHostFragment

Registering the stack hosts as a scoped service

Now in order for the backstack fragment to see the stack hosts, we must provide them as a scoped service, so we will register it on our root screen:

RootScreen

Setting up the host fragments (and their child fragments)

Remember the snippet at the beginning, which allows us to select a fragment with a bottom navigation view, and show (attach) 1 fragment, while hiding (detaching) the rest?

That’s exactly what we need to do, just for these fragments that host a backstack each.

RootFragment

Navigating within a child fragment in a child stack

To navigate within a given child stack, all we need to do is use the child stack that was added with the help of the FragmentStackHost.

This might seem a bit tricky, but after all, we are getting a service registered to the top-most parent fragment’s scope, and can identify the stacks with their provided names.

And with that, we’re actually done!

Conclusion

With that, we have created a Fragment, that hosts 3 child Fragments, and each of these 3 child Fragments hosts a Backstack of their own, that survives configuration changes and process death.

Was it tricky? Well, it requires knowledge of the Fragment lifecycle to set up, and knowing about FragmentTransaction.add, FragmentTransaction.attach, FragmentTransaction.detach. However, this is also the same mechanism we’ve been using for years in ViewPager’s FragmentPagerAdapter, so surely, it’s not too alien? 😅

Otherwise, for a problem that’s been unresolved for years, it didn’t seem that complicated to implement— with the added ability from Simple-Stack — that allowed Fragments to be capable of easily keeping things alive across configuration changes, and also be able to intercept back with an intuitive API.

The full sample code is available here, and the discussion thread is available here on /r/android_devs.

--

--

Gabor Varadi

Android dev. Zhuinden, or EpicPandaForce @ SO. Extension function fan #Kotlin, dislikes multiple Activities/Fragment backstack.