A Useful Navigation Extension

Photo of Mariusz Karwowski

Mariusz Karwowski

Updated Jan 5, 2023 • 12 min read
navigation

Recently while cruising through the internet I came across the NavigationAdvancedSample created by Google.

As I was using a navigation component in my current project, I wanted to check it out, and it turned out to be a great decision. Google provided us with a very nice extension function. This function allows us to attach multiple navigation graphs to the BottomNavigationView. Let’s jump into implementation.

Here’s a link to the extension function that will be used in this example.

Creating menu and navigation graphs

“navigation_menu.xml”:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/home"
        android:contentDescription="@string/navigation_menu_home"
        android:icon="@drawable/ic_home"
        android:title="@string/navigation_menu_home" />
    <item
        android:id="@+id/friends"
        android:contentDescription="@string/navigation_menu_friends"
        android:icon="@drawable/ic_friends"
        android:title="@string/navigation_menu_friends" />
    <item
        android:id="@+id/messeges"
        android:contentDescription="@string/navigation_menu_messeges"
        android:icon="@drawable/ic_messeges"
        android:title="@string/navigation_menu_messeges" />
</menu>

While creating a menu that will be attached to our BottomNavigationView keep in mind that the IDs for menu items will be the same as the IDs of our navigation graphs. Everything else can be named anything you desire.

“home.xml”

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/home"
    app:startDestination="@+id/homeFragment">
 
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.mariusz.karwowski.sample.home.HomeFragment"
        android:label="HOME"
        tools:layout="@layout/fragment_home">
        <action
            android:id="@+id/homeToPlayer"
            app:destination="@+id/playerFragment"/>
    </fragment>
 
    <fragment
        android:id="@+id/playerFragment"
        android:name="com.mariusz.karwowski.sample.home.PlayerFragment"
        android:label="PLAYER"
        tools:layout="@layout/fragment_player">
        <argument
                android:name="movieUrl"
                app:argType="string" />
    </fragment>
</navigation>

We created a sample navigation graph for our home path. As mentioned before, we named our XML file “home.xml” and the navigation graph’s ID is “home”, which will allow the navigation component to easily chain menu items with the proper navigation graph.

The headings of the remaining graphs should look like this:

“friends.xml”

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/friends"
    app:startDestination="@+id/friendsFragment">
        ...
</navigation>


“messages.xml”

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/messeges"
    app:startDestination="@+id/messegesFragment">
        ...
</navigation>

We have our menu and we have our navigation graphs - we now need a BottomNavigationView.

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black"
        app:itemBackground="@color/transparent"
        app:itemIconTint="@drawable/ic_home_selector"
        app:itemTextColor="@drawable/ic_home_selector"
        app:labelVisibilityMode="labeled"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/navHostFragment"
        app:layout_insetEdge="bottom"
        app:menu="@menu/navigation_menu" />

You can notice a couple differences compared to Google’s introduction to the navigation component : there is no android:name="androidx.navigation.fragment.NavHostFragment" or app:defaultNavHost="true" app:navGraph="@navigation/nav_graph", because the function linked above will handle this for us. All we have to do is to link the menu created before.

Final setup in activity

There are quite a few things that we have to do in the activity that will be hosting the BottomNavigationView:

Declare NavController LiveData that will be used for back presses:

private var currentNavController: LiveData<NavController>? = null

Override onSupportNavigateUp using LiveData:

    override fun onSupportNavigateUp() =
            currentNavController?.value?.navigateUp() ?: false

Declare a list of our navigation graphs:

val navGraphIds = listOf(R.navigation.home, R.navigation.friends, R.navigation.messeges)

We can now use the provided extension function to set up the BottomNavigationView:

    val controller = bottomNavigationView.setupWithNavController(
            navGraphIds = navGraphIds,
            fragmentManager = supportFragmentManager,
            containerId = R.id.navHostFragment,
            intent = intent
    
    currentNavController = controller

The function parameters are: the list of navigation graphs, the fragment manager, and the ID of the FrameLayout that will be used as a container for the fragments and intent from activity. Don’t forget to assign the generated LiveData to our property.

What I like to do is remove the optional function that will pop all fragments on reselection of the menu item:

setupItemReselected(graphIdToTagMap, fragmentManager)

You can find this in the 135th line of the provided extension function.

Tips

Here are some useful tips for working with multiple navigation graphs:

One destination in multiple navigation graphs

If you want to use one fragment in more than one navigation graph, all you have to do is to create a new navigation graph for this fragment:
“player.xml”
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/player"
    app:startDestination="@+id/playerFragment">
    <fragment
        android:id="@+id/playerFragment"
        android:name="com.mariusz.karwowski.sample.home.PlayerFragment"
        android:label="PLAYER"
        tools:layout="@layout/fragment_player">
    </fragment>
</navigation>

And now you have to include this navigation graph in all graphs which need to have this fragment in their navigation paths, but remember to pass the navigation graph IDs as destinations instead of fragment IDs.

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/home"
    app:startDestination="@+id/homeFragment">
 
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.mariusz.karwowski.sample.home.HomeFragment"
        android:label="HOME"
        tools:layout="@layout/fragment_home">
        <action
            android:id="@+id/homeToPlayer"
            app:destination="@+id/player"/>
    </fragment>
<include app:graph="@navigation/player" /> </navigation>

Arguments for starting destinations

If your newly created fragment still has to have arguments and you want to pass them with directions, you can’t add arguments to fragments which are starting destinations of the graph; instead, you need to add arguments to actions, so in the above the home fragment with all its new actions will look like this

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.mariusz.karwowski.sample.home.HomeFragment"
        android:label="HOME"
        tools:layout="@layout/fragment_home">
        <action 
            android:id="@+id/homeToPlayer"
            app:destination="@+id/player">
            <argument
                android:name="movieUrl"
                app:argType="string" />
        </action>
    </fragment>

In addition, you will be required to fetch all these arguments by yourself from the PlayerFragment.

Summary

The sample provided by Google allows us to easily handle complex fragment management which would otherwise have to be implemented programmatically inside menu option selection handling. We can see the application flow presented in nice graph previews. We can create deep links to our fragments and the whole navigation graph should be recreated, so we do not need to create fragment stacks by ourselves. What’s more, our destinations can have our custom classes as arguments - just remember to extend Serializable or Parcelable. Overall, I would suggest you create your own sample project that uses Navigation Components so you can see for yourself how easy it is.

Photo of Mariusz Karwowski

More posts by this author

Mariusz Karwowski

Senior Android Developer at Netguru
Efficient software development  Build faster, deliver more  Start now!

Read more on our Blog

Check out the knowledge base collected and distilled by experienced professionals.

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business