A navigation graph can consist of any combination of the following:
- A singular destination, such as a
<fragment>
destination. - A nested graph that encapsulates a set of related destinations.
- An
<include>
element, which allows you to embed another navigation graph file as though it was nested.
This flexibility allows you to combine smaller navigation graphs together to form your app's complete navigation graph, even if those smaller navigation graphs are provided by separate library modules.
For the examples in this topic, each library module is focused around one
feature and provides a single navigation graph that encapsulates all of the
destinations needed to implement that feature. In a production app, you might
have many submodules at a lower level that are implementation details of this
higher-level library module. Each of these library modules are included,
either directly or indirectly, into your app
module. The example
multi-module application used in this doc has the following structure:


Each library module is a self-contained unit with its own navigation graph
and destinations. The app
module depends on each, adding them as
implementation details in its build.gradle
file, as shown:
dependencies {
...
implementation project(":list")
implementation project(":favorites")
implementation project(":settings")
The role of the app
module
The app
module is responsible for providing the complete graph for your
app and adding the NavHost
to your UI. Within the app
module's
navigation graph, you can reference the library graphs by using
<include>
. While
using <include>
is functionally the same as using a nested graph,
<include>
supports graphs from other project modules or from library
projects, as shown in the following example:
<?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/nav_graph"
app:startDestination="@id/list_nav_graph">
<include app:graph="@navigation/list_navigation" />
<include app:graph="@navigation/favorites_navigation" />
<include app:graph="@navigation/settings_navigation" />
</navigation>
Once a library is included in the top-level navigation graph, you can navigate to the library graphs as needed. For example, you could create an action to navigate to the settings graph from a fragment in your navigation graph, as shown:
<?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/nav_graph"
app:startDestination="@id/list_nav_graph">
<include app:graph="@navigation/list_navigation" />
<include app:graph="@navigation/favorites_navigation" />
<include app:graph="@navigation/settings_navigation" />
<fragment
android:id="@+id/random_fragment"
android:name="com.example.android.RandomFragment"
android:label="@string/fragment_random" >
<!-- Launch into Settings Navigation Graph -->
<action
android:id="@+id/action_random_fragment_to_settings_nav_graph"
app:destination="@id/settings_nav_graph" />
</fragment>
</navigation>
When multiple library modules need to reference a common set of
destinations, such as a login graph, you should not include those
common destinations into each library module's navigation graph. Instead,
add those common destinations to your app
module's navigation graph.
Each library module can then navigate across library modules
to navigate to those common destinations.
In the previous example, the action specifies a navigation destination
of @id/settings_nav_graph
. This ID refers to a destination that is
defined within the included graph @navigation/settings_navigation.
Top-level navigation in app module
The Navigation component includes a
NavigationUI
class.
This class contains static methods that manage navigation with the top
app bar, the navigation drawer, and bottom navigation. If your app's
top-level destinations are composed of UI elements provided by library
modules, the app
module is a natural place to put the top-level
navigation and UI elements. Since the app module depends on the
collaborating library modules, all of their destinations are accessible
from code defined within your app module. This means that you can use
NavigationUI
to
tie destinations to menu items
if the ID of the item matches the ID of a destination.
In figure 2, the example app
module defines a
BottomNavigationView
in its main activity. The menu item IDs in the menu match the navigation
graph IDs of the library graphs:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@id/list_nav_graph"
android:icon="@drawable/ic_list"
android:title="List"
app:showAsAction="ifRoom"/>
<item
android:id="@id/favorites_nav_graph"
android:icon="@drawable/ic_favorite"
android:title="Favorites"
app:showAsAction="ifRoom"/>
<item
android:id="@id/settings_nav_graph"
android:icon="@drawable/ic_settings"
android:title="Settings"
app:showAsAction="ifRoom" />
</menu>
To let NavigationUI
handle the
bottom navigation, call
setupWithNavController()
from onCreate()
in your main activity class, as shown in the following
example:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController findViewById<BottomNavigationView>(R.id.bottom_nav) .setupWithNavController(navController) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NavHostFragment navHostFragment = (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); BottomNavigationView bottomNav = findViewById(R.id.bottom_nav); NavigationUI.setupWithNavController(bottomNav, navController); }
With this code in place, NavigationUI
navigates to the appropriate
library graph when the user clicks on a bottom navigation item.
Keep in mind that it's generally bad practice for your app module to have a hard dependency on a specific destination embedded deeply within your library modules' navigation graph. In most cases, you want your app module to know only about the entry point to any embedded or included navigation graphs (this applies outside of library modules too). If you need to link to a destination deep within your library's navigation graph, the preferred way to do this is by using a deep link. Deep linking is also the only way for a library to navigate to a destination in another library's navigation graph.
Navigating across library modules
At compile time, independent library modules cannot see each other, so you can't use IDs to navigate to destinations in other modules. Instead, use a deep link to navigate directly to a destination that is associated with an implicit deep link.
Continuing the previous example, imagine you need to navigate from a button in the list module to a destination nested in the settings module. You can do this by adding a deep link to the destination in the settings navigation graph, as shown:
<?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/settings_nav_graph"
app:startDestination="@id/settings_fragment_one">
...
<fragment
android:id="@+id/settings_fragment_two"
android:name="com.example.google.login.SettingsFragmentTwo"
android:label="@string/settings_fragment_two" >
<deepLink
app:uri="android-app://example.google.app/settings_fragment_two" />
</fragment>
</navigation>
Then add the following code to the button's onClickListener
in the list
fragment:
Kotlin
button.setOnClickListener { val request = NavDeepLinkRequest.Builder .fromUri("android-app://example.google.app/settings_fragment_two".toUri()) .build() findNavController().navigate(request) }
Java
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { NavDeepLinkRequest request = NavDeepLinkRequest.Builder .fromUri(Uri.parse("android-app://example.google.app/settings_fragment_two")) .build(); NavHostFragment.findNavController(this).navigate(request); } });
Unlike navigating using action or destination IDs, you can navigate to any URI in any graph, even across modules.
When navigating using URI, the back stack is not reset. This behavior is unlike explicit deep link navigation, where the back stack is replaced when navigating.