Cómo diseñar tu gráfico de navegación

El componente Navigation utiliza un gráfico de navegación para administrar la navegación de tu app. El gráfico de navegación es una estructura de datos que contiene cada destino dentro de tu app y las conexiones entre ellos.

Tipos de destinos

Existen tres tipos generales de destinos: alojados, de diálogo y de actividad. En la siguiente tabla, se describen estos tres tipos de destinos y sus propósitos.

Tipo

Descripción

Casos de uso

Alojado

Llena todo el host de navegación. Es decir, el tamaño de un destino alojado es el mismo que el tamaño del host de navegación, y los destinos anteriores no son visibles.

Pantallas principales y de detalles

De diálogo

Presenta componentes superpuestos de la IU. Esta IU no está vinculada a la ubicación del host de navegación ni a su tamaño. Los destinos anteriores se pueden ver debajo del destino.

Alertas, selecciones y formularios

De actividad

Representa pantallas o funciones únicas dentro de la app.

Sirve como punto de salida al gráfico de navegación que inicia una nueva actividad de Android que se administra de forma independiente del componente Navigation.

En Modern Android Development, una app consta de una sola actividad. Por lo tanto, es mejor usar los destinos de actividad cuando se interactúa con actividades externas o como parte del proceso de migración.

En este documento, se incluyen ejemplos de destinos alojados, que son los destinos más comunes y fundamentales. Consulta las siguientes guías para obtener información sobre los otros destinos:

Frameworks

Aunque en todos los casos se aplica el mismo flujo de trabajo general, la forma exacta de crear un host y un gráfico de navegación depende del framework de IU que uses.

  • Compose: usa el elemento componible NavHost. Agrega un NavGraph con el DSL de Kotlin. Puedes crear el gráfico de dos maneras:
    • Como parte del NavHost: construye el gráfico de navegación directamente como parte de agregar el NavHost.
    • De manera programática: usa el método NavController.createGraph() para crear un NavGraph y pasarlo directamente al NavHost.
  • Fragmentos: Cuando uses fragmentos con el framework de IU de vistas, usa un NavHostFragment como host. Existen varias formas de crear un gráfico de navegación:
    • De manera programática: usa el DSL de Kotlin para crear un NavGraph y aplicarlo directamente en el NavHostFragment.
      • La función createGraph() que se usa con el DSL de Kotlin para fragmentos y Compose es la misma.
    • XML: escribe el host y el gráfico de navegación directamente en XML.
    • Editor de Android Studio: usa el editor de GUI de Android Studio para crear y ajustar tu gráfico como un archivo de recursos XML.

Compose

En Compose, usa un objeto o una clase serializable para definir una ruta. Una ruta describe cómo llegar a un destino y contiene toda la información que este requiere.

Usa la anotación @Serializable para crear automáticamente los métodos de serialización y deserialización necesarios para tus tipos de ruta. El complemento de serialización de Kotlin proporciona esta anotación. Sigue estas instrucciones para agregar este complemento.

Una vez que hayas definido tus rutas, usa el elemento componible NavHost para crear tu gráfico de navegación. Consulta el siguiente ejemplo:

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // Add more destinations similarly.
}
  1. Un objeto serializable representa cada una de las dos rutas, Profile y FriendsList.
  2. La llamada al elemento componible NavHost pasa un NavController y una ruta para el destino de inicio.
  3. La expresión lambda pasada al NavHost finalmente llama a NavController.createGraph() y muestra un NavGraph.
  4. Cada ruta se proporciona como argumento de tipo a NavGraphBuilder.composable<T>(), que agrega el destino al NavGraph resultante.
  5. La lambda que se pasa a composable es lo que NavHost muestra para ese destino.

Comprende la lambda

Para comprender mejor la expresión lambda que crea el NavGraph, considera que, para compilar el mismo gráfico que en el fragmento anterior, puedes crear el NavGraph por separado con NavController.createGraph() y pasarlo al NavHost directamente:

val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)

Pasa argumentos

Si necesitas pasar datos a un destino, define la ruta con una clase que tenga parámetros. Por ejemplo, la ruta Profile es una clase de datos con un parámetro name.

@Serializable
data class Profile(val name: String)

Cada vez que necesites pasar argumentos a ese destino, creas una instancia de tu clase de ruta y pasas los argumentos al constructor de la clase.

Para los argumentos opcionales, crea campos anulables con un valor predeterminado.

@Serializable
data class Profile(val nickname: String? = null)

Cómo obtener una instancia de ruta

Puedes obtener la instancia de ruta con NavBackStackEntry.toRoute() o SavedStateHandle.toRoute(). Cuando creas un destino con composable(), NavBackStackEntry está disponible como parámetro.

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

Ten en cuenta lo siguiente en este fragmento:

  • La ruta Profile especifica el destino de inicio en el gráfico de navegación, con "John Smith" como argumento para name.
  • El destino en sí es el bloque composable<Profile>{}.
  • El elemento componible ProfileScreen toma el valor de profile.name para su propio argumento name.
  • Por lo tanto, el valor "John Smith" pasa a ProfileScreen.

Ejemplo mínimo

Un ejemplo completo de NavController y NavHost que funcionan juntos:

@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")
          )
        }
      )
    }
  }
}

Como se muestra en el fragmento, en lugar de pasar el NavController a los elementos componibles, expone un evento al NavHost. Es decir, los elementos componibles deben tener un parámetro de tipo () -> Unit para el cual el NavHost pase una expresión lambda que llame a NavController.navigate().

Fragmentos

Como se describe en las secciones anteriores, cuando usas fragmentos, tienes la opción de crear un gráfico de navegación de manera programática con el DSL de Kotlin, XML o el editor de Android Studio.

En las siguientes secciones, se detallan estos diferentes enfoques.

De manera programática

El DSL de Kotlin proporciona una manera programática de crear un gráfico de navegación con fragmentos. En muchos sentidos, esto es más prolijo y moderno que usar un archivo de recursos XML.

Ten en cuenta el siguiente ejemplo, que implementa un gráfico de navegación de dos pantallas.

En primer lugar, es necesario crear el NavHostFragment, que no debe incluir un elemento app:navGraph:

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

A continuación, pasa el id del NavHostFragment a NavController.findNavController. De esta manera, se asocia el NavController con el NavHostFragment.

Luego, la llamada a NavController.createGraph() vincula el gráfico al NavController y, por lo tanto, también al 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.
}

Usar el DSL de esta manera es muy similar al flujo de trabajo descrito en la sección anterior sobre Compose. Por ejemplo, tanto allí como aquí, la función NavController.createGraph() genera el NavGraph. Del mismo modo, mientras que NavGraphBuilder.composable() agrega destinos componibles al gráfico, aquí NavGraphBuilder.fragment() agrega un destino de fragmento.

Para obtener más información sobre cómo usar el DSL de Kotlin, consulta Cómo compilar un gráfico con el DSL de NavGraphBuilder.

XML

Puedes escribir directamente el XML por tu cuenta. El siguiente ejemplo replica el ejemplo de dos pantallas de la sección anterior y es equivalente a él.

Primero, crea un NavHostFragment. Esto sirve como el host de navegación que contiene el gráfico de navegación real.

Implementación mínima de un 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>

El NavHostFragment contiene el atributo app:navGraph. Usa este atributo para conectar tu gráfico de navegación al host de navegación. A continuación, se muestra un ejemplo de cómo puedes implementar el gráfico:

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

Usas acciones para definir las conexiones entre los diferentes destinos. En este ejemplo, el fragmento profile contiene una acción que navega a friendslist. Para obtener más información, consulta Cómo usar acciones y fragmentos de Navigation.

Editor

Puedes administrar el gráfico de navegación de tu app con el Editor de Navigation en Android Studio. En esencia, es una GUI que puedes usar para crear y editar tu XML de NavigationFragment, como se muestra en la sección anterior.

Para obtener más información, consulta Editor de Navigation.

Gráficos anidados

También puedes usar gráficos anidados. Eso implica usar un gráfico como destino de navegación. Para obtener más información, consulta Gráficos anidados.

Lecturas adicionales

Para conocer más conceptos básicos de navegación, consulta las siguientes guías: