The Navigation component uses a navigation graph to manage your app's navigation. The navigation graph is a data structure that contains each destination within your app and the connections between them.
Destination types
There are three general types of destinations: hosted, dialog, and activity. The following table outlines these three destination types and their purposes.
Type |
Description |
Use cases |
---|---|---|
Hosted |
Fills the entire navigation host. That is, the size of a hosted destination is the same as the size of the navigation host and previous destinations are not visible. |
Main and detail screens. |
Dialog |
Presents overlay UI components. This UI is not tied to the location of the navigation host or its size. Previous destinations are visible underneath the destination. |
Alerts, selections, forms. |
Activity |
Represents unique screens or features within the app. |
Serve as an exit point to the navigation graph that starts a new Android activity that is managed separately from the Navigation component. In modern Android development, an app consists of a single activity. Activity destinations are therefore best used when interacting with third party activities or as part of the migration process. |
This document contains examples of hosted destinations, which are the most common and fundamental destinations. See the following guides for information on the other destinations:
Frameworks
Although the same general workflow applies in every case, how exactly you create a navigation host and graph depends on the UI framework you use.
- Compose: Use the
NavHost
composable. Add aNavGraph
to it using the Kotlin DSL. You can create the graph in two ways:- As part of the NavHost: Construct the navigation graph directly as
part of adding the
NavHost
. - Programmatically: Use the
NavController.createGraph()
method to create aNavGraph
and pass it to theNavHost
directly.
- As part of the NavHost: Construct the navigation graph directly as
part of adding the
- Fragments: When using fragments with the views UI framework, use a
NavHostFragment
as the host. There are several ways to create a navigation graph:- Programmatically: Use the Kotlin DSL to create a
NavGraph
and directly apply it on theNavHostFragment
.- The
createGraph()
function used with the Kotlin DSL for both fragments and Compose is the same.
- The
- XML: Write your navigation host and graph directly in XML.
- Android Studio editor: Use the GUI editor in Android Studio to create and adjust your graph as an XML resource file.
- Programmatically: Use the Kotlin DSL to create a
Compose
In Compose, use a serializable object or class to define a route. A route
describes how to get to a destination, and contains all the information that the
destination requires. Once you have defined your routes, use the NavHost
composable to create your navigation graph. Consider the following example:
@Serializable
object Profile
@Serializable
object FriendsList
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Profile) {
composable<Profile> { ProfileScreen( /* ... */ ) }
composable<FriendsList> { FriendsListScreen( /* ... */ ) }
// Add more destinations similarly.
}
- A serializable object represents each of the two routes,
Profile
andFriendsList
. - The call to the
NavHost
composable passes aNavController
and a route for the start destination. - The lambda passed to the
NavHost
ultimately callsNavController.createGraph()
and returns aNavGraph
. - Each route is supplied as a type argument to
NavGraphBuilder.composable<T>()
which adds the destination to the resultingNavGraph
. - The lambda passed to
composable
is what theNavHost
displays for that destination.
Understand the lambda
To better understand the lambda that creates the NavGraph
, consider that to
build the same graph as in the preceding snippet, you could create the
NavGraph
separately using NavController.createGraph()
and pass it to
the NavHost
directly:
val navGraph by remember(navController) {
navController.createGraph(startDestination = Profile)) {
composable<Profile> { ProfileScreen( /* ... */ ) }
composable<FriendsList> { FriendsListScreen( /* ... */ ) }
}
}
NavHost(navController, navGraph)
Pass arguments
If you need to pass data to a destination, define the route with a class that
has parameters. For example, the Profile
route is a data class with a name
parameter.
@Serializable
data class Profile(val name: String)
Whenever you need to pass arguments to that destination, you create an instance of your route class, passing the arguments to the class constructor.
For optional arguments, create nullable fields with a default value.
@Serializable
data class Profile(val nickname: String? = null)
Obtain route instance
You can obtain the route instance with NavBackStackEntry.toRoute()
or
SavedStateHandle.toRoute()
. When you create a destination using
composable()
, the NavBackStackEntry
is available as a parameter.
@Serializable
data class Profile(val name: String)
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(name = profile.name) }
}
Note the following in this snippet:
- The
Profile
route specifies the starting destination in the navigation graph, with"John Smith"
as the argument forname
. - The destination itself is the
composable<Profile>{}
block. - The
ProfileScreen
composable takes the value ofprofile.name
for its ownname
argument. - As such, the value
"John Smith"
passes through toProfileScreen
.
Minimal example
A complete example of a NavController
and NavHost
working together:
@Serializable
data class Profile(val name: String)
@Serializable
object FriendsList
// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(
profile: Profile
onNavigateToFriendsList: () -> Unit,
) {
Text("Profile for ${profile.name}")
Button(onClick = { onNavigateToFriendsList() }) {
Text("Go to Friends List")
}
}
// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
Text("Friends List")
Button(onClick = { onNavigateToProfile() }) {
Text("Go to Profile")
}
}
// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = Profile(name = "John Smith")) {
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(
profile = profile,
onNavigateToFriendsList = {
navController.navigate(route = FriendsList)
}
)
}
composable<FriendsList> {
FriendsListScreen(
onNavigateToProfile = {
navController.navigate(
route = Profile(name = "Aisha Devi")
)
}
)
}
}
}
As the snippet demonstrates, instead of passing the NavController
to your
composables, expose an event to the NavHost
. That is, your composables should
have a parameter of type () -> Unit
for which the NavHost
passes a lambda
that calls NavController.navigate()
.
Fragments
As outlined in the preceding sections, when using fragments you have the option to create a navigation graph programmatically using the Kotlin DSL, XML, or the Android Studio editor.
The following sections detail these different approaches.
Programmatically
The Kotlin DSL provides a programmatic way of creating a navigation graph with fragments. In many ways this is neater and more modern than using an XML resource file.
Consider the following example, which implements a two-screen navigation graph.
First it is necessary to create the NavHostFragment
, which must not include
an app:navGraph
element:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Next, pass the id
of the NavHostFragment
to
NavController.findNavController
. This associates the NavController with
the NavHostFragment
.
Subsequently, the call to NavController.createGraph()
links the graph to
the NavController
and consequently also to the NavHostFragment
:
@Serializable
data class Profile(val name: String)
@Serializable
object FriendsList
// Retrieve the NavController.
val navController = findNavController(R.id.nav_host_fragment)
// Add the graph to the NavController with `createGraph()`.
navController.graph = navController.createGraph(
startDestination = Profile(name = "John Smith")
) {
// Associate each destination with one of the route constants.
fragment<ProfileFragment, Profile> {
label = "Profile"
}
fragment<FriendsListFragment, FriendsList>() {
label = "Friends List"
}
// Add other fragment destinations similarly.
}
Using the DSL in this way is very similar to the workflow outlined in the
preceding section on Compose. For example, both there and here, the
NavController.createGraph()
function generates the NavGraph
. Likewise, while
NavGraphBuilder.composable()
adds composable destinations to the graph, here
NavGraphBuilder.fragment()
adds a fragment destination.
For more information on how to use the Kotlin DSL, see Build a graph with the NavGraphBuilder DSL.
XML
You can directly write the XML yourself. The following example mirrors and is equivalent to the two-screen example from the preceding section.
First, create a NavHostFragment
. This serves as the navigation host which
contains the actual navigation graph.
A minimal implementation of a NavHostFragment
:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/nav_graph" />
</FrameLayout>
The NavHostFragment
contains the attribute app:navGraph
. Use this attribute
to connect your navigation graph to the navigation host. The following is an
example of how you might implement the graph:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/profile">
<fragment
android:id="@+id/profile"
android:name="com.example.ProfileFragment"
android:label="Profile">
<!-- Action to navigate from Profile to Friends List. -->
<action
android:id="@+id/action_profile_to_friendslist"
app:destination="@id/friendslist" />
</fragment>
<fragment
android:id="@+id/friendslist"
android:name="com.example.FriendsListFragment"
android:label="Friends List" />
<!-- Add other fragment destinations similarly. -->
</navigation>
You use actions to define the connections between different destinations. In
this example, the profile
fragment contains an action that navigates to
friendslist
. For more information, see Use Navigation actions and
fragments.
Editor
You can manage your app's navigation graph using the Navigation Editor in
Android Studio. This is essentially a GUI you can use to create and edit your
NavigationFragment
XML, as seen in the preceding section.
For more information, see Navigation editor.
Nested graphs
You can also use nested graphs. This involves using a graph as a navigation destination. For more information, see Nested graphs.
Further Reading
For more core navigation concepts, see the following guides:
- Overview: Make sure to read the general overview of the Navigation component.
- Activity destinations: Examples of how to implement destinations that take the user to activities.
- Dialog destinations: Examples of how to create destinations that take the user to a dialog.
- Navigate to a destination: A detailed guide that covers how to navigate from one destination to another.
- Nested graphs: An in-depth guide on how to nest one navigation graph within another.