Navigate to a destination

Navigating to a destination is done using a NavController, an object that manages app navigation within a NavHost. Each NavHost has its own corresponding NavController. NavController provides a few different ways to navigate to a destination, which are further described in the sections below.

To retrieve the NavController for a fragment, activity, or view, use one of the following methods:

Kotlin:

Java:

After you've retrieved a NavController, you can call one of the overloads of navigate() to navigate between destinations. Each overload provides support for various navigation scenarios, as described in the following sections.

Use Safe Args to navigate with type-safety

The recommended way to navigate between destinations is to use the Safe Args Gradle plugin. This plugin generates simple object and builder classes that enable type-safe navigation between destinations. Safe Args is recommended both for navigating as well as passing data between destinations.

To add Safe Args to your project, include the following classpath in your top level build.gradle file:

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.1.0"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

You must also apply one of two available plugins.

To generate Java language code suitable for Java or mixed Java and Kotlin modules, add this line to your app or module's build.gradle file:

apply plugin: "androidx.navigation.safeargs"

Alternatively, to generate Kotlin code suitable for Kotlin-only modules add:

apply plugin: "androidx.navigation.safeargs.kotlin"

You must have android.useAndroidX=true in your gradle.properties file as per Migrating to AndroidX.

After enabling Safe Args, your generated code contains classes and methods for each action you've defined as well as classes that correspond to each sending and receiving destination.

Safe Args generates a class for each destination where an action originates. The generated class name adds "Directions" to the originating destination class name. For example, if the originating destination is named SpecifyAmountFragment, the generated class is named SpecifyAmountFragmentDirections.

The generated class contains a static method for each action defined in the originating destination. This method takes any defined action parameters as arguments and returns a NavDirections object that you can pass directly to navigate().

Safe Args example

As an example, assume we have a navigation graph with a single action that connects two destinations, SpecifyAmountFragment and ConfirmationFragment. The ConfirmationFragment takes a single float parameter that you provide as part of the action.

Safe Args generates a SpecifyAmountFragmentDirections class with a single method, actionSpecifyAmountFragmentToConfirmationFragment(), and an inner class called ActionSpecifyAmountFragmentToConfirmationFragment. The inner class is derived from NavDirections and stores the associated action ID and float parameter. The returned NavDirections object can then be passed directly to navigate(), as shown in the following example:

Kotlin

override fun onClick(v: View) {
    val amount: Float = ...
    val action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment(amount)
    v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
    float amount = ...;
    action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment(amount);
    Navigation.findNavController(view).navigate(action);
}

For more information on passing data between destinations with Safe Args, see Use Safe Args to pass data with type safety.

Navigate using ID

navigate(int) takes the resource ID of either an action or a destination. The following code snippet shows how to navigate to the ViewTransactionsFragment:

Kotlin

viewTransactionsButton.setOnClickListener { view ->
   view.findNavController().navigate(R.id.viewTransactionsAction)
}

Java

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

For buttons, you can also use the Navigation class’s createNavigateOnClickListener() convenience method to navigate to a destination, as shown in the following example:

Kotlin

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))

Java

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

To handle other common UI components, such as the top app bar and bottom navigation, see Update UI components with NavigationUI.

When you define an action in the navigation graph, Navigation generates a corresponding NavAction class, which contains the configurations defined for that action, including the following:

  • Destination: The resource ID of the target destination.
  • Default arguments: An android.os.Bundle containing default values for the target destination, if supplied.
  • Navigation options: Navigation options, represented as NavOptions. This class contains all of the special configuration for transitioning to and back from the target destination, including animation resource configuration, pop behavior, and whether the destination should be launched in single top mode.

Let’s take a look at an example graph consisting of two screens along with an action to navigate from one to the other:

<?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/a">

    <fragment android:id="@+id/a"
              android:name="com.example.myapplication.FragmentA"
              android:label="a"
              tools:layout="@layout/a">
        <action android:id="@+id/action_a_to_b"
                app:destination="@id/b"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
    </fragment>

    <fragment android:id="@+id/b"
              android:name="com.example.myapplication.FragmentB"
              android:label="b"
              tools:layout="@layout/b">
        <action android:id="@+id/action_b_to_a"
                app:destination="@id/a"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim"
                app:popUpTo="@+id/a"
                app:popUpToInclusive="true"/>
    </fragment>
</navigation>

When the navigation graph is inflated, these actions are parsed, and corresponding NavAction objects are generated with the configurations defined in the graph. For example, action_b_to_a is defined as navigating from destination b to destination a. The action includes animations along with popTo behavior that removes all destinations from the backstack. All of these settings are captured as NavOptions and are attached to the NavAction.

To follow this NavAction, use NavController.navigate(), passing the ID of the action, as shown in the following example:

Kotlin

findNavController().navigate(R.id.action_b_to_a)

Java

Navigation.findNavController(this).navigate(R.id.action_b_to_a);

Navigate using URI

You can use navigate(Uri) to navigate directly to an implicit deep link destination, as shown in the following example:

Kotlin

val navController = findNavController()
val deeplink = Uri.parse("android-app://androidx.navigation.app/profile")
findNavController().navigate(deeplink)

Java

Uri deeplink = Uri.parse("android-app://androidx.navigation.app/profile");
view.findNavController().navigate(deeplink);

Unlike navigation using action or destination IDs, you can navigate to any URI in your graph, regardless of whether the destination is visible. You can navigate to a destination on the current graph or a destination on a completely different graph.

When navigating using URI, the back stack is not reset. This is unlike other deep link navigation, where the back stack is replaced when navigating. popUpTo and popUpToInclusive, however, still remove destinations from the back stack just as though you had navigated using an ID.

Navigation and the back stack

Android maintains a back stack that contains the destinations you've visited. The first destination of your app is placed on the stack when the user opens the app. Each call to the navigate() method puts another destination on top of the stack. Tapping Up or Back calls the NavController.navigateUp() and NavController.popBackStack() methods, respectively, to remove (or pop) the top destination off of the stack.

NavController.popBackStack() returns a boolean indicating whether it successfully popped back to another destination. The most common case when this returns false is when you manually pop the start destination of your graph.

When the method returns false, NavController.getCurrentDestination() returns null. You are responsible for either navigating to a new destination or handling the pop by calling finish() on your Activity, as shown in the following example:

Kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

Java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

popUpTo and popUpToInclusive

When navigating using an action, you can optionally pop additional destinations off of the back stack. For example, if your app has an initial login flow, once a user has logged in, you should pop all of the login-related destinations off of the back stack so that the Back button doesn't take users back into the login flow.

To pop destinations when navigating from one destination to another, add an app:popUpTo attribute to the associated <action> element. app:popUpTo tells the Navigation library to pop some destinations off of the back stack as part of the call to navigate(). The attribute value is the ID of the most recent destination that should remain on the stack.

You can also include app:popUpToInclusive="true" to indicate that the destination specified in app:popUpTo should also be removed from the back stack.

popUpTo example: circular logic

Let's say that your app has three destinations—A, B, and C—along with actions that lead from A to B, B to C, and C back to A. The corresponding navigation graph is shown in figure 1:

Figure 1. A circular navigation graph with three destinations: A, B, and C.

With each navigation action, a destination is added to the back stack. If you were to navigate repeatedly through this flow, your back stack would then contain multiple sets of each destination (A, B, C, A, B, C, A, and so on). To avoid this repetition, you can specify app:popUpTo and app:popUpToInclusive in the action that takes you from destination C to destination A, as shown in the following example:

<fragment
    android:id="@+id/c"
    android:name="com.example.myapplication.C"
    android:label="fragment_c"
    tools:layout="@layout/fragment_c">

    <action
        android:id="@+id/action_c_to_a"
        app:destination="@id/a"
        app:popUpTo="@+id/a"
        app:popUpToInclusive="true"/>
</fragment>

After reaching destination C, the back stack contains one instance of each destination (A, B, C). When navigating back to destination A, we also popUpTo A, which means that we remove B and C from the stack while navigating. With app:popUpToInclusive="true", we also pop that first A off of the stack, effectively clearing it. Notice here that if you don't use app:popUpToInclusive, your back stack would contain two instances of destination A.