Concevoir votre graphique de navigation

Le composant de navigation utilise un graphique de navigation pour gérer la navigation dans votre appli. Le graphique de navigation est une structure de données qui contient chaque destination de votre appli ainsi que les connexions qui s'établissent entre elles.

Types de destinations

Il existe trois principaux types de destinations : destination hébergée, destination de boîte de dialogue et destination d'activité. Le tableau ci-dessous décrit ces trois types de destinations ainsi que leurs objectifs.

Type

Description

Cas d'utilisation

Destination hébergée

Occupe l'intégralité de l'hôte de navigation. Autrement dit, la taille d'une destination hébergée est identique à celle de l'hôte de navigation, et les destinations précédentes ne sont pas visibles.

Écran principal et page d'informations

Destination de boîte de dialogue

Présente les composants d'interface utilisateur superposés. Cette interface utilisateur n'est pas liée à l'emplacement de l'hôte de navigation ni à sa taille. Les destinations précédentes sont visibles sous la destination.

Alertes, sélections, formulaires.

Destination d'activité

Représente des pages ou des fonctionnalités uniques au sein de l'appli.

Sert de point de sortie au graphique de navigation qui lance une nouvelle activité Android gérée indépendamment du composant Navigation.

Dans Modern Android Development, une appli se compose d'une seule activité. Lorsqu'elles interagissent avec des activités tierces ou dans le cadre du processus de migration, les destinations des activités sont ainsi mieux utilisées.

Ce document contient des exemples de destinations hébergées, qui sont les destinations les plus courantes et les plus fondamentales. Pour en savoir plus sur les autres destinations, consultez les guides suivants :

Frameworks

Bien que le même workflow général s'applique dans tous les cas, la manière exacte de créer un hôte et un graphique de navigation dépend du framework d'interface utilisateur que vous utilisez.

  • Compose : utilisez le composable NavHost. Ajoutez-y un NavGraph à l'aide du DSL Kotlin. Il existe deux manières de créer le graphique :
    • Dans NavHost : construisez le graphique de navigation directement lors de l'ajout de NavHost.
    • Par programmation : utilisez la méthode NavController.createGraph() pour créer un NavGraph et le transmettre directement à NavHost.
  • Fragments:lorsque vous utilisez des fragments avec le framework d'UI Vues, utilisez un NavHostFragment comme hôte. Il existe plusieurs façons de créer un graphique de navigation :
    • De façon programmatique:utilisez le DSL Kotlin pour créer un NavGraph et l'appliquer directement à NavHostFragment.
      • La fonction createGraph() utilisée avec le DSL Kotlin pour les deux fragments ainsi que Compose est la même.
    • XML : écrivez votre hôte de navigation ainsi que votre graphique de navigation directement en XML.
    • Éditeur Android Studio : utilisez l'éditeur IUG dans Android Studio pour créer et ajuster votre graphique en tant que fichier de ressources XML.

Compose

Dans Compose, utilisez un objet ou une classe sérialisable pour définir un parcours. Un itinéraire décrit comment accéder à une destination et contient toutes les informations requises pour y parvenir.

Utilisez l'annotation @Serializable pour créer automatiquement les méthodes de sérialisation et de désérialisation nécessaires pour vos types de parcours. Cette annotation est fournie par le plug-in de sérialisation Kotlin. Suivez ces instructions pour ajouter ce plug-in.

Une fois que vous avez défini vos itinéraires, utilisez le composable NavHost pour créer votre graphique de navigation. Prenons l'exemple suivant :

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // Add more destinations similarly.
}
  1. Un objet sérialisable représente chacun des deux itinéraires, Profile et FriendsList.
  2. L'appel du composable NavHost transmet un NavController et un itinéraire pour la destination de départ.
  3. Le lambda transmis à NavHost appelle finalement NavController.createGraph() et renvoie un NavGraph.
  4. Chaque itinéraire est fourni en tant qu'argument de type à NavGraphBuilder.composable<T>(), qui ajoute la destination à l'NavGraph généré.
  5. Le lambda transmis à composable est ce que NavHost affiche pour cette destination.

Comprendre le lambda

Pour mieux comprendre le lambda qui crée le NavGraph, n'oubliez pas que pour créer le même graphique que dans l'extrait précédent, vous pouvez créer le NavGraph séparément à l'aide de NavController.createGraph() et le transmettre directement à NavHost :

val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)

Transmettre des arguments

Si vous devez transmettre des données à une destination, définissez l'itinéraire avec une classe qui comporte des paramètres. Par exemple, le chemin Profile est une classe de données avec un paramètre name.

@Serializable
data class Profile(val name: String)

Chaque fois que vous devez transmettre des arguments à cette destination, vous créez une instance de votre classe d'itinéraire, en transmettant les arguments au constructeur de la classe.

Pour les arguments facultatifs, créez des champs nullables avec une valeur par défaut.

@Serializable
data class Profile(val nickname: String? = null)

Obtenir une instance d'itinéraire

Vous pouvez obtenir l'instance de route avec NavBackStackEntry.toRoute() ou SavedStateHandle.toRoute(). Lorsque vous créez une destination à l'aide de composable(), NavBackStackEntry est disponible en tant que paramètre.

@Serializable
data class Profile(val name: String)

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(name = profile.name) }
}

Notez les points suivants dans cet extrait:

  • L'itinéraire Profile spécifie la destination de départ dans le graphique de navigation, avec "John Smith" comme argument pour name.
  • La destination elle-même est le bloc composable<Profile>{}.
  • Le composable ProfileScreen prend la valeur de profile.name pour son propre argument name.
  • Par conséquent, la valeur "John Smith" est transmise à ProfileScreen.

Exemple minimal

Voici un exemple complet de cas où NavController et NavHost fonctionnent ensemble:

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(
    profile: Profile
    onNavigateToFriendsList: () -> Unit,
  ) {
  Text("Profile for ${profile.name}")
  Button(onClick = { onNavigateToFriendsList() }) {
    Text("Go to Friends List")
  }
}

// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
  Text("Friends List")
  Button(onClick = { onNavigateToProfile() }) {
    Text("Go to Profile")
  }
}

// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
  val navController = rememberNavController()
  NavHost(navController, startDestination = Profile(name = "John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(
            profile = profile,
            onNavigateToFriendsList = {
                navController.navigate(route = FriendsList)
            }
        )
    }
    composable<FriendsList> {
      FriendsListScreen(
        onNavigateToProfile = {
          navController.navigate(
            route = Profile(name = "Aisha Devi")
          )
        }
      )
    }
  }
}

Comme le montre l'extrait, plutôt que de transmettre le NavController à vos composables, exposez un événement à NavHost. Autrement dit, vos composables doivent comporter un paramètre de type () -> Unit pour lequel NavHost transmet un lambda qui appelle NavController.navigate().

Fragments

Comme indiqué dans les sections précédentes, lorsque vous utilisez des fragments, vous avez la possibilité de créer un graphique de navigation par programmation à l'aide du DSL Kotlin, du XML ou de l'éditeur Android Studio.

Les sections suivantes reviennent plus en détail sur ces différentes approches.

Par programmation

Le DSL Kotlin permet de créer un graphique de navigation avec des fragments de façon programmatique. À bien des égards, cette méthode est plus pratique et plus moderne que le recours à un fichier de ressources XML.

Prenons l'exemple suivant, qui implémente un graphique de navigation sur deux écrans.

Il est avant tout nécessaire de créer le NavHostFragment, qui ne doit pas inclure d'élément app:navGraph:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

Transmettez ensuite l'id du NavHostFragment à NavController.findNavController. Cela a pour effet d'associer NavController au NavHostFragment.

Ensuite, l'appel envoyé à NavController.createGraph() associe le graphique au NavController et, par conséquent, au NavHostFragment:

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Retrieve the NavController.
val navController = findNavController(R.id.nav_host_fragment)

// Add the graph to the NavController with `createGraph()`.
navController.graph = navController.createGraph(
    startDestination = Profile(name = "John Smith")
) {
    // Associate each destination with one of the route constants.
    fragment<ProfileFragment, Profile> {
        label = "Profile"
    }

    fragment<FriendsListFragment, FriendsList>() {
        label = "Friends List"
    }

    // Add other fragment destinations similarly.
}

Cette manière d'utiliser le DSL est très semblable au workflow décrit dans la section précédente sur Compose. Par exemple, ici et là, la fonction NavController.createGraph() génère le NavGraph. De même, tandis que NavGraphBuilder.composable() ajoute des destinations de composable au graphique, NavGraphBuilder.fragment() ajoute une destination de fragment.

Pour en savoir plus sur l'utilisation du DSL Kotlin, consultez la section Créer un graphe par programmation à l'aide du langage DSL Kotlin.

XML

Vous pouvez rédiger vous-même le code XML directement. L'exemple suivant est le même que celui présenté à la section précédente (sur deux écrans).

Commencez par créer un NavHostFragment. Ce dernier servira d'hôte de navigation et contiendra le graphique de navigation.

Implémentation minimale d'un NavHostFragment :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/nav_graph" />

</FrameLayout>

Le NavHostFragment contient l'attribut app:navGraph. Utilisez cet attribut pour connecter votre graphique de navigation à l'hôte de navigation. Voici un exemple d'implémentation du graphique :

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/profile">

    <fragment
        android:id="@+id/profile"
        android:name="com.example.ProfileFragment"
        android:label="Profile">

        <!-- Action to navigate from Profile to Friends List. -->
        <action
            android:id="@+id/action_profile_to_friendslist"
            app:destination="@id/friendslist" />
    </fragment>

    <fragment
        android:id="@+id/friendslist"
        android:name="com.example.FriendsListFragment"
        android:label="Friends List" />

    <!-- Add other fragment destinations similarly. -->
</navigation>

Les actions vous permettent de définir les connexions entre les différentes destinations. Dans cet exemple, le fragment profile contient une action qui permet d'accéder à friendslist. Pour en savoir plus, consultez la section Utiliser les actions de navigation et les fragments.

Éditeur

Vous pouvez gérer le graphique de navigation de votre appli à l'aide de l'éditeur de navigation d'Android Studio. Il s'agit essentiellement d'une IUG que vous pouvez utiliser pour créer et modifier votre fichier XML NavigationFragment, comme indiqué dans la section précédente.

Pour en savoir plus, consultez la section Éditeur de navigation.

Graphiques imbriqués

Vous pouvez également utiliser des graphiques imbriqués. Cela implique l'utilisation d'un graphique comme destination de navigation. Pour en savoir plus, consultez la section Graphiques imbriqués.

Complément d'informations

Pour plus de concepts de base concernant la navigation, consultez les guides suivants :