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 contidos dessa maneira, fica mais fácil de compreender e gerenciar o fluxo principal da IU 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() no próprio gráfico aninhado, onde a lógica interna pode mudar sem afetar o restante 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 vê 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ê precisará levar esses usuários 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 o título e as telas de 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 a seguir implementa o gráfico na Figura 2 usando o 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 diretamente até um destino aninhado, use o route como faria para qualquer outro destino. Isso ocorre porque as rotas são um conceito global que pode ser acessado em qualquer tela:

navController.navigate("match")

Funções da extensão

Você pode adicionar destinos a um gráfico usando uma função de extensão em NavGraphBuilder. Você pode usar essas funções de extensão com os métodos de extensão pré-criados navigation, composable e dialog.

Por exemplo, é possível usar uma função de extensão para adicionar o gráfico aninhado demonstrado na seção 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") }
            )
        }
    }
}

Você pode chamar essa função na lambda transmitida para NavHost em vez de chamar a navegação inline. O exemplo a seguir demonstra isso:

@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

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

  1. No Navigation Editor, mantenha a tecla Shift pressionada 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 seguintes atributos 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. Esse gráfico de navegação tem os próprios elementos navigation com o próprio 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 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);
    
  7. 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 a seguir 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 navegação, consulte os recursos adicionais a seguir.

Exemplos

Codelabs

Vídeos