Tabbed Master-Detail flow using the Navigation Component Library ➡️📑

☠️⚡This article is outdated and has some issues. Please see my updated article! ☠️⚡

Daniel Wilson
5 min readApr 17, 2019

We had a rather unusual use case for a recent Point of Sale app that will operate in both tablet and phone form factors. The app needs to display 3 master lists at the same hierarchical level. Each list is of similar data objects but filtered. 3 detail screens housing the detail views of the list elements is also required. So we landed on master-detail flow, where each master-detail list is also a swipeable tab. Some of this post was inspired by the great tips from Lara Martin at ProAndroidDev here.

The end result in phone-land

Oft left behind by the Android team, master-detail views for tablets are a rarity now, probably due to low tablet sales. Maybe they will make a hot comeback in foldable form.

Master/Detail New Project sample

Normally a master-detail layout is easy enough. The official somewhat dusty new-project sample is a great starting point. An activity houses an item_list xml include which has a phone and tablet flavor. The phone item_list is just a recycler view. The tablet item_list is a recycler view beside a detail container.

Clicking the phone recycler view adapter opens a new Activity with the list item’s details. Clicking the tablet adapter swaps out the detail container fragment.

The Master-Detail sample from the Android team

Subtle issues with this approach

— The sample code duplicates the detail layouts. We need to manage an ItemDetailActivity for phones and an ItemDetailFragment for tablets even though they are probably the same. I came across this myself: if you want a back stack on phones and no back stack on tablets, the only way to do this without some crazy custom back stack management is to launch a new activity for phone detail views. Imagine you want to navigate to an inner-detail fragment inside the ItemDetailFragment. Well that is going to need to be an activity in either case because it’s the same issue, it needs a back stack independent of the nested-tabbed-master-detail top level 🍰

— There is no Navigation Component library action here, so if we need a smarter navigation in the future, and all of the compile-time niceties it brings, it would be hard to fit it into this flow. The Nav library is about back stack management, and this is somewhat incompatible with master-detail views, and kind of irrelevant for tabs.

— FragmentPagerAdapters need dynamic fragments, which are instantiated based on the selected tab. These are also a little bit incompatible with the Nav library.

Crazy Dan’s solution

What I’ve come up with here is a little bit weird, but this is a green-field project and I would like to use the Nav library for as much of my navigation as possible. Again, check out the tips at ProAndroidDev here if you need something a little more conventional. If it is too crazy please let me know, but maybe it will help you. There are two main goals of this exercise:

  • Keep navigation to Nav graphs as much as possible for compile-time safety, animations and less fragment manager confusion. In my mind (especially with nested fragments), if one fragment is added or replaced in one place via manual fragment transactions, but the rest of the code uses the Nav library it might be a little confusing to a new developer.
  • Keep as much of code as reusable and flexible as possible. If I want 10 tabs next month instead of 3, this should support that. If I want three or five layers of navigation inside the detail views, it should support that as well. Complicated and reusable detail Nav graphs, deep links and synthesizing back stacks should be as easy as possible.

1: Have your launch Activity host the tabs fragment Nav graph.

2: The ItemTabsFragment hosts the ViewPager, and is hence responsible for the the programmatic Fragments which need to go into it. Not only is each Fragment therefore dynamic, it also needs to respond differently based on form factor. So we make either a MasterHostFragment or a MasterDetailHostFragment. The reason I’ve used the word Host here is that this is a bridge between the programmatic world of the Pager Adapter and the statically typed world of the app’s Nav graphs.

3: For phones, jump back into Nav graph land with the following

Note, the NavHostFragment does not specify the navGraph here because it is inflated within the fragment itself.

Now we can navigate to the DetailActivity from our MasterFragment using our friendly NavDirections.

4: For tablets, jump back into the Nav graph with the following

Note, the same thing, the NavHostFragments here do not specify navGraphs because they are inflated within the fragment itself.

5: So now there is one final piece of the puzzle, how to make a DetailFragment from a MasterFragment on tablets (but still launch a DetailActivity on phones)

So the margin for error here is the arguments. We are moving from dynamic arguments to Nav graph arguments so if we want to keep them in sync and forward arguments to new start destinations in Nav-graph-land, we need to inflate the Nav graphs ourselves. On tablets, we launch the detail Nav fragment by swapping out the current one and also forward on any arguments to it at inflation time.

End result:

🧙 Tada! Not a Fragment Transaction in sight. The full code to this sample is available on GitHub. Please let me know if you have any tips for the craziness.

--

--