Gráficos aninhados

Fluxos de login, assistentes ou outros subfluxos no seu app geralmente são mais bem representados como gráficos de navegação aninhados. Aninhando fluxos de subnavegação autocontidos dessa forma, fica mais fácil de compreender e gerenciar o fluxo principal da interface do seu app.

Além disso, os gráficos aninhados são reutilizáveis. Eles também oferecem um nível de encapsulamento: destinos fora do gráfico aninhado não têm acesso direto a nenhum dos destinos dentro do gráfico aninhado. Em vez disso, eles precisam navigate() até o gráfico aninhado, onde a lógica interna pode mudar sem afetar o resto do gráfico.

Exemplo

O gráfico de navegação de nível superior do seu app precisa começar com o destino inicial que o usuário encontra ao iniciar o app e incluir os destinos que ele vê quando se movimenta pelo app.

Figura 1. Um gráfico de navegação de nível superior.

Usando o gráfico de navegação de nível superior da Figura 1 como exemplo, suponha que você queira exigir que o usuário veja as telas title_screen e register somente quando o app for iniciado pela primeira vez. Em seguida, as informações do usuário são armazenadas e, em lançamentos subsequentes do app, você precisa levá-las diretamente para a tela match.

Como prática recomendada, defina a tela match como o destino inicial do gráfico de navegação de nível superior e mova as telas de título e registro para um gráfico aninhado, conforme mostrado na Figura 1:

Figura 2. O gráfico de navegação de nível superior agora contém um gráfico aninhado.

Quando a tela de correspondência for iniciada, verifique se há um usuário registrado. Se o usuário não estiver registrado, navegue até a tela de registro.

Para saber mais sobre cenários de navegação condicional, consulte Navegação condicional.

Compose

Para criar um gráfico de navegação aninhado usando o Compose, use a função NavGraphBuilder.navigation(). Use navigation() da mesma forma que as funções NavGraphBuilder.composable() e NavGraphBuilder.dialog() ao adicionar destinos a um gráfico.

A principal diferença é que navigation cria um gráfico aninhado em vez de um novo destino. Em seguida, chame composable() e dialog() na lambda de navigation() para adicionar destinos ao gráfico aninhado.

Considere como o snippet abaixo implementa o gráfico na Figura 2 usando o 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 diretamente até um destino aninhado, use um tipo de rota como faria para qualquer outro destino. Isso ocorre porque as rotas são um conceito global usado para identificar destinos que podem ser acessados por qualquer tela:

navController.navigate(route = Match)
.

XML

Ao usar XML, você pode usar o Navigation Editor para criar seu gráfico aninhado. Para isso, siga estas etapas:

  1. No Navigation Editor, toque e pressione a tecla Shift, e clique nos destinos que você quer incluir no gráfico aninhado.
  2. Clique com o botão direito do mouse para abrir o menu de contexto e selecione Move to Nested Graph > New Graph. Os destinos são colocados em um gráfico aninhado. A Figura 2 mostra um gráfico aninhado no Navigation Editor:

    Figura 2. Gráfico aninhado no Navigation Editor
  3. Clique no gráfico aninhado. Os atributos abaixo aparecem no painel Attributes:

    • Type contém o "Nested Graph".
    • ID contém um ID atribuído pelo sistema ao gráfico aninhado. Esse ID é usado para fazer referência ao gráfico aninhado do seu código.
  4. Clique duas vezes no gráfico aninhado para exibir os destinos.

  5. Clique na guia Text para alternar para a visualização XML. Um gráfico de navegação aninhado foi adicionado ao gráfico. Este gráfico de navegação tem os próprios elementos navigation, com o ID e um atributo startDestination que aponta para o primeiro destino no gráfico aninhado:

    <?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. No seu código, transmita o ID do recurso da ação que conecta o gráfico raiz ao gráfico aninhado:

Kotlin

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

Java

Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
  1. De volta à guia Design, retorne ao gráfico raiz clicando em Root.

Fazer referência a outros gráficos de navegação com "include"

Outra maneira de modularizar a estrutura do seu gráfico é incluir um gráfico dentro de outro usando um elemento <include> no gráfico de navegação pai. Isso permite que o gráfico incluído seja definido em um módulo ou projeto separado, o que maximiza a capacidade de reutilização.

O snippet abaixo demonstra como 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>

Outros recursos

Para saber mais sobre a navegação, consulte os recursos a seguir.

Codelabs

Vídeos