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 se pueden reutilizar. También proporcionan un nivel de encapsulamiento: los destinos fuera del gráfico anidado no tienen acceso directo a ninguno de los destinos dentro del gráfico anidado. En cambio, deben navigate()
en el propio gráfico anidado, donde la lógica interna puede cambiar sin afectar al resto del gráfico.
Ejemplo
El gráfico de navegación de nivel superior de tu app debe comenzar con el destino inicial que el usuario ve cuando inicia la app y debe incluir los destinos que ve cuando se desplaza por ella.
Toma como ejemplo el gráfico de navegación de nivel superior de la figura 1 y, luego, imagina que quieres que el usuario vea las pantallas title_screen y register solo cuando la app se inicia por primera vez. A continuación, se almacena la información del usuario y, en inicios posteriores de la app, deberías dirigir al usuario directamente a la pantalla match.
Como práctica recomendada, establece la pantalla match como destino de inicio del gráfico de navegación de nivel superior y mueve el título y las pantallas de registro a un gráfico anidado, como se muestra en la figura 1:
Cuando se inicie la pantalla match, 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()
al igual 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.
Considera cómo el siguiente fragmento implementa el gráfico de la Figura 2 con Compose:
NavHost(navController, startDestination = "title_screen") {
composable("title_screen") {
TitleScreen(
onPlayClicked = { navController.navigate("register") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("register") {
RegisterScreen(
onSignUpComplete = { navController.navigate("gameInProgress") }
)
}
navigation(startDestination = "match", route = "gameInProgress") {
composable("match") {
MatchScreen(
onStartGame = { navController.navigate("in_game") }
)
}
composable("in_game") {
InGameScreen(
onGameWin = { navController.navigate("results_winner") },
onGameLose = { navController.navigate("game_over") }
)
}
composable("results_winner") {
ResultsWinnerScreen(
onNextMatchClicked = {
navController.navigate("match") {
popUpTo("match") { inclusive = true }
}
},
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("game_over") {
GameOverScreen(
onTryAgainClicked = {
navController.navigate("match") {
popUpTo("match") { inclusive = true }
}
}
)
}
}
}
Para navegar directamente a un destino anidado, usa route
como lo harías con cualquier otro destino. Esto se debe a que las rutas son un concepto global al que puede navegar cualquier pantalla:
navController.navigate("match")
Funciones de extensión
Puedes agregar destinos a un gráfico con una función de extensión en NavGraphBuilder
. Puedes usar estas funciones de extensión junto con los
métodos de extensión navigation
, composable
y dialog
compilados previamente.
Por ejemplo, puedes usar una función de extensión para agregar el gráfico anidado que se mostró en la sección anterior:
fun NavGraphBuilder.addNestedGraph(navController: NavController) {
navigation(startDestination = "match", route = "gameInProgress") {
composable("match") {
MatchScreen(
onStartGame = { navController.navigate("in_game") }
)
}
composable("in_game") {
InGameScreen(
onGameWin = { navController.navigate("results_winner") },
onGameLose = { navController.navigate("game_over") }
)
}
composable("results_winner") {
ResultsWinnerScreen(
onNextMatchClicked = { navController.navigate("match") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("game_over") {
GameOverScreen(
onTryAgainClicked = { navController.navigate("match") }
)
}
}
}
Luego, puedes llamar a esta función en la expresión lambda que pasas a NavHost
, en lugar de llamar a la navegación intercalada. En el siguiente ejemplo se demuestra esto:
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "title_screen") {
composable("title_screen") {
TitleScreen(
onPlayClicked = { navController.navigate("register") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("register") {
RegisterScreen(
onSignUpComplete = { navController.navigate("gameInProgress") }
)
}
// Add the nested graph using the extension function
addNestedGraph(navController)
}
}
XML
Cuando usas XML, puedes usar el editor de Navigation para crear tu gráfico anidado. Para hacerlo, sigue estos pasos:
- 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.
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 :
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 utiliza para hacer referencia al gráfico anidado de tu código.
Haz doble clic en el gráfico anidado para mostrar sus destinos.
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 atributostartDestination
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>
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);
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 forma de modularizar la estructura de tu gráfico es incluir un gráfico dentro de otro mediante un elemento <include>
en el gráfico de navegación superior. Esto permite que el gráfico incluido se defina en un módulo o proyecto separado, lo que maximiza la 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 acerca de la navegación, consulta los siguientes recursos adicionales.