Gráficos anidados

Por lo general, los flujos de acceso, los asistentes y otros subflujos de tu app se representan mejor como gráficos de navegación anidados. Si anidas flujos de subnavegación autónomos de esta manera, el flujo principal de la IU de tu app será más fácil de comprender y administrar.

Además, los gráficos anidados son reutilizables. También proporcionan un nivel de encapsulación, es decir, que los destinos fuera del gráfico anidado no tienen acceso directo a ninguno de los destinos dentro del gráfico anidado. En su lugar, deberían tener un elemento navigate() que los dirija al propio gráfico anidado, donde la lógica interna puede cambiar sin afectar el resto del gráfico.

Ejemplo

El gráfico de navegación de nivel superior de tu app debería comenzar con el destino inicial que el usuario ve cuando la inicia y debería incluir los destinos que ve cuando se desplaza por ella.

Figura 1: Un gráfico de navegación de nivel superior

Toma como ejemplo el gráfico de navegación de nivel superior de la figura 1 y imagina que quieres que el usuario vea las pantallas title_screen y register solo cuando se inicie la app por primera vez. A continuación, se almacena la información del usuario y, en los inicios posteriores de la app, deberías llevarlos directamente a la pantalla match.

Como práctica recomendada, establece la pantalla match como destino de inicio de un gráfico de navegación de nivel superior y mueve las pantallas de título y registro a un gráfico anidado, como se muestra en la figura 1:

Figura 2: Ahora, el gráfico de navegación de nivel superior contiene un gráfico anidado.

Cuando se inicie la pantalla de coincidencia, verifica si hay un usuario registrado. Si el usuario no está registrado, redirecciónalo a la pantalla de registro.

Para obtener más información sobre los casos de navegación condicional, consulta Navegación condicional.

Redactar

Para crear un gráfico de navegación anidado con Compose, usa la función NavGraphBuilder.navigation(). Usa navigation() de la misma manera que las funciones NavGraphBuilder.composable() y NavGraphBuilder.dialog() cuando agregues destinos a un gráfico.

La diferencia principal es que navigation crea un gráfico anidado en lugar de un destino nuevo. Luego, llama a composable() y dialog() dentro de la lambda de navigation() para agregar destinos al gráfico anidado.

Ten en cuenta cómo, en el siguiente fragmento, se implementa el gráfico de la figura 2 con Compose:

// Routes
@Serializable object Title
@Serializable object Register

// Route for nested graph
@Serializable object Game

// Routes inside nested graph
@Serializable object Match
@Serializable object InGame
@Serializable object ResultsWinner
@Serializable object GameOver

NavHost(navController, startDestination = Title) {
   composable<Title> {
       TitleScreen(
           onPlayClicked = { navController.navigate(route = Register) },
           onLeaderboardsClicked = { /* Navigate to leaderboards */ }
       )
   }
   composable<Register> {
       RegisterScreen(
           onSignUpComplete = { navController.navigate(route = Game) }
       )
   }
   navigation<Game>(startDestination = Match) {
       composable<Match> {
           MatchScreen(
               onStartGame = { navController.navigate(route = InGame) }
           )
       }
       composable<InGame> {
           InGameScreen(
               onGameWin = { navController.navigate(route = ResultsWinner) },
               onGameLose = { navController.navigate(route = GameOver) }
           )
       }
       composable<ResultsWinner> {
           ResultsWinnerScreen(
               onNextMatchClicked = {
                   navController.navigate(route = Match) {
                       popUpTo(route = Match) { inclusive = true }
                   }
               },
               onLeaderboardsClicked = { /* Navigate to leaderboards */ }
           )
       }
       composable<GameOver> {
           GameOverScreen(
               onTryAgainClicked = {
                   navController.navigate(route = Match) {
                       popUpTo(route = Match) { inclusive = true }
                   }
               }
           )
       }
   }
}

Para navegar directamente a un destino anidado, usa un tipo de ruta como lo harías con cualquier otro destino. Esto se debe a que las rutas son un concepto global que se usa para identificar los destinos a los que puede navegar cualquier pantalla:

navController.navigate(route = Match)
.

XML

Cuando usas XML, puedes usar el Editor de Navigation para crear tu gráfico anidado. Para hacerlo, sigue estos pasos:

  1. En el Editor de Navigation, mantén presionada la tecla Mayúsculas y haz clic en los destinos que deseas incluir en el gráfico anidado.
  2. Haz clic con el botón derecho para abrir el menú contextual y selecciona Move to Nested Graph > New Graph. Los destinos se encuentran dentro de un gráfico anidado. En la figura 2, se muestra un gráfico anidado en el editor de Navigation :

    Figura 2: Gráfico anidado en el Editor de Navigation
  3. Haz clic en el gráfico anidado. Los siguientes atributos se muestran en el panel Attributes:

    • Type, que contiene "Nested Graph" (gráfico anidado).
    • ID, que contiene un ID asignado por el sistema para el gráfico anidado. Este ID se usa para hacer referencia al gráfico anidado de tu código.
  4. Haz doble clic en el gráfico anidado para mostrar sus destinos.

  5. Haz clic en la pestaña Text para alternar a la vista XML. Se agregó un gráfico de navegación anidado al gráfico. Este gráfico de navegación tiene sus propios elementos navigation, junto con su propio ID y un atributo startDestination que apunta al primer destino del gráfico anidado:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
       xmlns:tools="http://schemas.android.com/tools"
       xmlns:android="http://schemas.android.com/apk/res/android"
       app:startDestination="@id/mainFragment">
       <fragment
           android:id="@+id/mainFragment"
           android:name="com.example.cashdog.cashdog.MainFragment"
           android:label="fragment_main"
           tools:layout="@layout/fragment_main" >
           <action
               android:id="@+id/action_mainFragment_to_sendMoneyGraph"
               app:destination="@id/sendMoneyGraph" />
           <action
               android:id="@+id/action_mainFragment_to_viewBalanceFragment"
               app:destination="@id/viewBalanceFragment" />
       </fragment>
       <fragment
           android:id="@+id/viewBalanceFragment"
           android:name="com.example.cashdog.cashdog.ViewBalanceFragment"
           android:label="fragment_view_balance"
           tools:layout="@layout/fragment_view_balance" />
       <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">
           <fragment
               android:id="@+id/chooseRecipient"
               android:name="com.example.cashdog.cashdog.ChooseRecipient"
               android:label="fragment_choose_recipient"
               tools:layout="@layout/fragment_choose_recipient">
               <action
                   android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"
                   app:destination="@id/chooseAmountFragment" />
           </fragment>
           <fragment
               android:id="@+id/chooseAmountFragment"
               android:name="com.example.cashdog.cashdog.ChooseAmountFragment"
               android:label="fragment_choose_amount"
               tools:layout="@layout/fragment_choose_amount" />
       </navigation>
    </navigation>
    
  6. En tu código, pasa el ID de recurso de la acción que conecta el gráfico raíz al gráfico anidado:

    Kotlin

    view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
    

    Java

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    
  7. De vuelta en la pestaña Design, haz clic en Root para regresar al gráfico raíz.

Cómo hacer referencia a otros gráficos de navegación con include

Otra manera de modularizar la estructura de tu gráfico consiste en incluir un gráfico dentro de otro con un elemento <include> en el gráfico de navegación superior. De esta manera, el gráfico incluido se puede definir en un módulo o proyecto por separado, lo que maximiza la capacidad de reutilización.

En el siguiente fragmento, se muestra cómo puedes usar <include>:

<!-- (root) nav_graph.xml -->
<?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/fragment">

    <strong><include app:graph="@navigation/included_graph" /></strong>

    <fragment
        android:id="@+id/fragment"
        android:name="com.example.myapplication.BlankFragment"
        android:label="Fragment in Root Graph"
        tools:layout="@layout/fragment_blank">
        <strong><action
            android:id="@+id/action_fragment_to_second_graph"
            app:destination="@id/second_graph" /></strong>
    </fragment>

    ...
</navigation>
<!-- included_graph.xml -->
<?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"
    <strong>android:id="@+id/second_graph"</strong>
    app:startDestination="@id/includedStart">

    <fragment
        android:id="@+id/includedStart"
        android:name="com.example.myapplication.IncludedStart"
        android:label="fragment_included_start"
        tools:layout="@layout/fragment_included_start" />
</navigation>

Recursos adicionales

Para obtener más información sobre la navegación, consulta los siguientes recursos adicionales.

Ejemplos

Codelabs

Videos