Cómo navegar a un destino

La navegación a un destino se realiza con un NavController, un objeto que administra la navegación de una app dentro de un elemento NavHost. Cada NavHost tiene su propio NavController correspondiente. NavController ofrece algunas maneras de navegar a un destino, que se describen en mayor detalle en las siguientes secciones.

Si deseas recuperar el objeto NavController de un fragmento, una actividad o una vista, usa uno de los siguientes métodos:

Kotlin:

Java:

Después de recuperar un objeto NavController, puedes llamar a una de las sobrecargas de navigate() para navegar entre los destinos. Cada sobrecarga ofrece compatibilidad con varios casos de navegación, como se describe en las siguientes secciones.

Cómo usar Safe Args para navegar con seguridad de tipo

Para navegar entre destinos, te recomendamos que uses el complemento de Gradle Safe Args, que genera clases simples de objeto y compilador que permiten navegar con seguridad de tipo entre destinos. Se recomienda el uso de Safe Args para navegar y pasar datos entre destinos.

Para agregar Safe Args a tu proyecto, incluye la siguiente classpath en tu archivo build.gradle de nivel superior:

Groovy

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

Kotlin

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

También debes aplicar uno de los dos complementos disponibles.

Para generar código de lenguaje Java adecuado para Java o módulos combinados de Java y Kotlin, agrega esta línea al archivo build.gradle de tu app o módulo:

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

Como alternativa, para generar el código de Kotlin adecuado para módulos solo de Kotlin, agrega lo siguiente:

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

Tienes que tener el objeto android.useAndroidX=true en tu archivo gradle.properties, según se indica en Cómo migrar a AndroidX.

Después de que habilites Safe Args, tu código generado contendrá clases y métodos para cada acción que hayas definido, además de clases que correspondan a cada destino de envío y recepción.

Safe Args genera una clase para cada destino en el que se origina una acción. Al nombre de la clase de destino de origen se le agrega "Directions". Por ejemplo, si el destino de origen tiene el nombre SpecifyAmountFragment, la clase generada se llamará SpecifyAmountFragmentDirections.

La clase generada contiene un método estático para cada acción definida en el destino de origen, que toma cualquier parámetro de acción definido como argumento y muestra un objeto NavDirections que puedes pasar directamente a navigate().

Ejemplo de Safe Args

Como ejemplo, imagina que tienes un gráfico de navegación con una sola acción que conecta dos destinos: SpecifyAmountFragment y ConfirmationFragment. El objeto ConfirmationFragment toma un solo parámetro float que proporcionas como parte de la acción.

Safe Args genera una clase SpecifyAmountFragmentDirections con un solo método, actionSpecifyAmountFragmentToConfirmationFragment() y una clase interna llamada ActionSpecifyAmountFragmentToConfirmationFragment. La clase interna deriva de NavDirections y almacena el ID de acción asociado y el parámetro float. Luego, el objeto NavDirections que se muestra se puede pasar directamente a navigate(), como se ve en el siguiente ejemplo:

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);
}

A fin de obtener más información para pasar datos entre destinos con Safe Args, consulta Cómo usar Safe Args para pasar datos con seguridad de tipo.

Cómo navegar con ID

El objeto navigate(int) toma el ID de recurso de una acción o un destino. En el siguiente fragmento de código, se muestra cómo navegar al elemento 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);
    }
});

Para los botones, también puedes usar el método de conveniencia createNavigateOnClickListener() de la clase Navigation a los efectos de navegar a un destino, como se muestra en el ejemplo a continuación:

Kotlin

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

Java

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

Para controlar otros componentes de IU comunes, como la barra de la app superior y la navegación inferior, consulta Cómo actualizar componentes de IU con NavigationUI.

Cuando defines una acción en el gráfico de navegación, Navigation genera una clase NavAction correspondiente, que contiene las configuraciones definidas para esa acción, incluidas las siguientes:

  • Destino: es el ID de recurso del destino objetivo.
  • Argumentos predeterminados: es un android.os.Bundle que contiene valores predeterminados para el destino objetivo, si se lo suministró.
  • Opciones de navegación: son las opciones de Navigation, representadas como NavOptions. Esta clase contiene toda la configuración especial para la transición hacia el destino objetivo y desde él, lo que incluye la configuración del recurso de animación, el comportamiento emergente y si el destino debería lanzarse en un solo modo superior.

Veamos un ejemplo de un gráfico con dos pantallas y una acción para navegar de una a la otra:

<?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>

Cuando aumenta el gráfico de navegación, se analizan esas acciones y se generan los objetos NavAction correspondientes con las configuraciones definidas en el gráfico. Por ejemplo, se define action_b_to_a como la navegación desde el destino b hacia el destino a. La acción incluye animaciones, además del comportamiento popTo, que quita todos los destinos de la pila de actividades. Todas esas configuraciones se capturan como NavOptions y se adjuntan al NavAction.

Para seguir esta NavAction, usa NavController.navigate() y pasa el ID de la acción, como se muestra en el siguiente ejemplo:

Kotlin

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

Java

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

Cómo navegar con DeepLinkRequest

Puedes usar navigate(NavDeepLinkRequest) para navegar directamente a un destino de vínculo directo implícito, como se muestra en el siguiente ejemplo:

Kotlin

val request = NavDeepLinkRequest.Builder
    .fromUri("android-app://androidx.navigation.app/profile".toUri())
    .build()
findNavController().navigate(request)

Java

NavDeepLinkRequest request = NavDeepLinkRequest.Builder
    .fromUri(Uri.parse("android-app://androidx.navigation.app/profile"))
    .build()
NavHostFragment.findNavController(this).navigate(request)

Además de Uri, NavDeepLinkRequest también admite vínculos directos con acciones y tipos de MIME. Para agregar una acción a la solicitud, usa fromAction() o setAction(). Para agregar un tipo de MIME a una solicitud, usa fromMimeType() o setMimeType().

A los efectos de que una NavDeepLinkRequest coincida de forma adecuada con un destino de vínculo directo implícito, el URI, la acción y el tipo de MIME deben coincidir con el NavDeepLink en el destino. Los URI deben coincidir con el patrón, las acciones deben tener una concordancia exacta, y los tipos de MIME deben estar relacionados (p. ej., "image/jpg" coincide con "image/*").

A diferencia de la navegación mediante ID de acción o destino, puedes navegar a cualquier vínculo directo en tu gráfico, ya sea que el destino esté visible o no. Puedes navegar a un destino en el gráfico actual o a uno en un gráfico completamente diferente.

Cuando navegas con NavDeepLinkRequest, la pila de actividades no se restablece. Este comportamiento difiere de otros métodos de navegación de vínculo directo, en los que se reemplaza la pila de actividades durante la navegación. Los objetos popUpTo y popUpToInclusive todavía quitan destinos de la pila de actividades como si hubieses navegado mediante un ID.

Navigation y la pila de actividades

Android mantiene una pila de actividades que incluye los destinos que visitaste. Cuando el usuario abre tu app, se coloca el primer destino de la app en la pila. Cada llamada al método navigate() ubica otro destino sobre la pila. Cuando presionas Up o Back, se llama a los métodos NavController.navigateUp() y NavController.popBackStack(), respectivamente, para quitar (o hacer que aparezca como emergente) el destino superior de la pila.

NavController.popBackStack() muestra un valor booleano que indica si otro destino apareció con éxito. El caso más común en el que se muestra false es cuando resaltas de forma manual el destino de inicio de tu gráfico.

Cuando el método muestra false, el objeto NavController.getCurrentDestination() muestra null. Eres responsable de navegar a un destino nuevo o de controlar el elemento emergente. Para ello, llama a finish() en tu actividad, como se muestra en el siguiente ejemplo:

Kotlin

...

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

Java

...

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

popUpTo y popUpToInclusive

Cuando navegas a través de una acción, puedes elegir resaltar destinos adicionales de la pila de actividades. Por ejemplo, si tu app tiene un flujo de acceso inicial, una vez que un usuario accede, debes resaltar todos los destinos relacionados con el acceso en la pila de actividades para que el botón Atrás no vuelva a dirigir a los usuarios al flujo de acceso.

Para mostrar los destinos cuando navegas de un destino al otro, agrega un atributo app:popUpTo al elemento <action> asociado. El atributo app:popUpTo le indica a la biblioteca de Navigation que resalte algunos destinos de la pila de actividades como parte de la llamada a navigate(). El valor de atributo es el ID del destino más reciente que debe permanecer en la pila.

Además, puedes incluir app:popUpToInclusive="true" para indicar que el destino especificado en el atributo app:popUpTo se debería quitar también de la pila de actividades.

Ejemplo de popUpTo: lógica circular

Supongamos que tu app tiene tres destinos (A, B y C) junto con las acciones que conducen de a A a B, de B a C y de C nuevamente a A. En la Figura 1, se muestra el gráfico de navegación correspondiente:

Figura 1: Un gráfico de navegación circular con tres destinos: A, B y C.

Con cada acción de navegación, se agrega un destino a la pila de actividades. Si navegas varias veces por este flujo, tu pila de actividades contendrá varios conjuntos de cada destino (A, B, C, A, B, C, A y así sucesivamente). A fin de evitar esta repetición, puedes especificar los atributos app:popUpTo y app:popUpToInclusive en la acción que te dirige del destino C al destino A, como se muestra en el siguiente ejemplo:

<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>

Después de llegar al destino C, la pila de actividades contiene una instancia de cada destino (A, B, C). Cuando navegas de vuelta al destino A, también popUpTo A, lo que significa que quitamos B y C de la pila durante la navegación. También usamos app:popUpToInclusive="true" para destacar la primera A de la pila y la borramos efectivamente. Ten en cuenta que si no usas app:popUpToInclusive, tu pila de actividades contendrá dos instancias del destino A.