Graphiques imbriqués

Les flux de connexion, les assistants ou d'autres sous-flux dans votre application sont généralement représentés par des graphiques de navigation imbriqués. En imbriquant ainsi les flux de sous-navigation autonomes, le flux principal de l'UI de votre application est plus facile à comprendre et à gérer.

De plus, les graphiques imbriqués sont réutilisables. Ils fournissent également un niveau d'encapsulation. En effet, les destinations situées en dehors du graphique imbriqué n'ont pas un accès direct aux destinations du graphique imbriqué. À la place, elles doivent naviguer (navigate()) jusqu'au graphique imbriqué lui-même, où la logique interne peut changer sans affecter le reste du graphique.

Exemple

Le graphique de navigation de premier niveau de votre application doit commencer par la destination initiale que l'utilisateur voit lorsqu'il lance l'application et doit inclure les destinations qu'il voit lorsqu'il se déplace dans votre application.

Figure 1. Un graphique de navigation de premier niveau

En utilisant le graphique de navigation de premier niveau de la figure 1 comme exemple, imaginons que vous souhaitez que l'utilisateur ne voie les écrans title_screen (écran titre) et register (inscription) que lorsque l'application est lancée pour la première fois. Par la suite, les informations utilisateur sont stockées et, lors des lancements suivants de l'application, l'utilisateur est amené directement à l'écran match (correspondance).

En guise de bonne pratique, il est recommandé de définir l'écran match (correspondance) comme destination de départ du graphique de navigation de premier niveau, puis de déplacer l'écran titre et l'écran d'inscription dans un graphique imbriqué, comme illustré dans la figure 2 :

Figure 2 : Le graphique de navigation de premier niveau contient désormais un graphique imbriqué.

Lorsque l'écran de correspondance se lance, vérifiez si un utilisateur est enregistré. Si l'utilisateur n'est pas enregistré, redirigez-le vers l'écran d'enregistrement.

Pour en savoir plus sur les scénarios de navigation conditionnelle, consultez la page Navigation conditionnelle.

Compose

Pour créer un graphique de navigation imbriqué à l'aide de Compose, utilisez la fonction NavGraphBuilder.navigation(). Vous utilisez navigation() tout comme les fonctions NavGraphBuilder.composable() et NavGraphBuilder.dialog() lorsque vous ajoutez des destinations à un graphique.

La principale différence est que navigation crée un graphique imbriqué plutôt qu'une nouvelle destination. Vous appelez ensuite composable et dialog dans le lambda de navigation pour ajouter des destinations au graphique imbriqué.

Examinons comment l'extrait suivant implémente le graphique de la figure 2 à l'aide de 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 }
                    }
                }
            )
        }
    }
}

Pour accéder directement à une destination imbriquée, utilisez route comme vous le feriez pour toute autre destination. En effet, les routes sont un concept global auquel n'importe quel écran peut naviguer :

navController.navigate("match")

Fonctions d'extension

Vous pouvez ajouter des destinations à un graphique à l'aide d'une fonction d'extension sur NavGraphBuilder. Vous pouvez utiliser ces fonctions d'extension avec les méthodes d'extension navigation, composable et dialog prédéfinies.

Par exemple, vous pouvez utiliser une fonction d'extension pour ajouter le graphique imbriqué présenté dans la section précédente :

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

Vous pouvez ensuite appeler cette fonction dans le lambda que vous transmettez à NavHost au lieu d'appeler la navigation intégrée. En voici un bon exemple :

@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

Lorsque vous utilisez le format XML, vous pouvez utiliser l'éditeur de navigation pour créer votre graphique imbriqué. Pour cela, procédez comme suit :

  1. Dans l'éditeur de navigation, appuyez de manière prolongée sur la touche Maj, puis cliquez sur les destinations que vous souhaitez inclure dans le graphique imbriqué.
  2. Effectuez un clic droit pour ouvrir le menu contextuel, puis sélectionnez Move to Nested Graph > New Graph (Déplacer vers un graphique imbriqué > Nouveau graphique). Les destinations sont incluses dans un graphique imbriqué. La figure 2 montre un graphique imbriqué dans l'éditeur de navigation :

    Figure 2. Graphique imbriqué dans l'éditeur de navigation
  3. Cliquez sur le graphique imbriqué. Les attributs suivants apparaissent dans le panneau Attributes (Attributs) :

    • Type, qui contient "Nested Graph" (Graphique imbriqué)
    • ID, qui contient un ID attribué par le système pour le graphique imbriqué (cet ID permet de référencer le graphique imbriqué de votre code)
  4. Double-cliquez sur le graphique imbriqué pour afficher ses destinations.

  5. Cliquez sur l'onglet Text (Texte) pour passer à la vue XML. Un graphique de navigation imbriqué y a été ajouté. Ce graphique de navigation possède ses propres éléments navigation, ainsi que son propre ID et un attribut startDestination qui pointe vers la première destination du graphique imbriqué :

    <?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. Dans votre code, transmettez l'ID de ressource de l'action qui connecte le graphique racine au graphique imbriqué :

    kotlin

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

    java

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    
  7. Revenez dans l'onglet Design (Conception), puis accédez de nouveau au graphique racine en cliquant sur Root (Racine).

Référencer d'autres graphiques de navigation avec un élément "include"

Vous pouvez également modulariser la structure de votre graphique en incluant un graphique dans un autre à l'aide d'un élément <include> dans le graphique de navigation parent. Le graphique inclus peut ainsi être défini dans un module ou un projet distinct, ce qui optimise sa réutilisation.

L'extrait suivant montre comment utiliser <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>

Ressources supplémentaires

Pour en savoir plus sur la navigation, consultez les ressources supplémentaires suivantes.

Exemples

Ateliers de programmation

Vidéos