Créer une navigation adaptative

La plupart des applications disposent de quelques destinations de haut niveau accessibles via l'interface utilisateur de navigation principale de l'application. Dans les fenêtres de format compact, telles qu'un écran de téléphone standard, les destinations sont généralement affichées dans une barre de navigation en bas de la fenêtre. Dans une fenêtre agrandie, telle qu'une application en plein écran sur une tablette, un rail de navigation à côté de l'application est généralement un meilleur choix, car les commandes de navigation sont plus faciles à accéder tout en maintenant les côtés gauche et droit de l'appareil.

NavigationSuiteScaffold simplifie le basculement entre les interfaces de navigation en affichant le composable approprié d'UI de navigation en fonction de WindowSizeClass. Cela inclut la modification dynamique de l'interface utilisateur lorsque la taille de la fenêtre d'exécution change. Le comportement par défaut consiste à afficher l'un des composants d'interface utilisateur suivants:

  • Barre de navigation si la largeur ou la hauteur sont compactes ou si l'appareil est en position à plat
  • Rail de navigation pour tout le reste
Figure 1. NavigationSuiteScaffold affiche une barre de navigation dans les fenêtres compactes.
Figure 2 : NavigationSuiteScaffold affiche un rail de navigation dans une fenêtre développée.

Ajouter des dépendances

NavigationSuiteScaffold fait partie de la bibliothèque de la suite de navigation adaptative Material3. Ajoutez une dépendance pour la bibliothèque dans le fichier build.gradle de votre application ou de votre module:

Kotlin


implementation("androidx.compose.material3:material3-adaptive-navigation-suite")

Groovy


implementation 'androidx.compose.material3:material3-adaptive-navigation-suite'

Créer une structure en échafaudage

Les deux parties principales de NavigationSuiteScaffold sont les éléments de la suite de navigation et le contenu de la destination sélectionnée. Vous pouvez définir directement les éléments de la suite de navigation dans un composable, mais il est courant de les définir ailleurs, par exemple dans une énumération:

enum class AppDestinations(
    @StringRes val label: Int,
    val icon: ImageVector,
    @StringRes val contentDescription: Int
) {
    HOME(R.string.home, Icons.Default.Home, R.string.home),
    FAVORITES(R.string.favorites, Icons.Default.Favorite, R.string.favorites),
    SHOPPING(R.string.shopping, Icons.Default.ShoppingCart, R.string.shopping),
    PROFILE(R.string.profile, Icons.Default.AccountBox, R.string.profile),
}

Pour utiliser NavigationSuiteScaffold, vous devez suivre la destination actuelle à l'aide de rememberSaveable:

var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }

Dans l'exemple suivant, le paramètre navigationSuiteItems (de type NavigationSuiteScope) utilise sa fonction item pour définir l'UI de navigation pour une destination individuelle. L'UI de destination est utilisée dans les barres, les rails et les panneaux de navigation. Pour créer des éléments de navigation, vous passez en boucle sur votre AppDestinations (défini dans l'extrait précédent):

NavigationSuiteScaffold(
    navigationSuiteItems = {
        AppDestinations.entries.forEach {
            item(
                icon = {
                    Icon(
                        it.icon,
                        contentDescription = stringResource(it.contentDescription)
                    )
                },
                label = { Text(stringResource(it.label)) },
                selected = it == currentDestination,
                onClick = { currentDestination = it }
            )
        }
    }
) {
    // TODO: Destination content.
}

Dans le lambda du contenu de destination, utilisez la valeur currentDestination pour choisir l'UI à afficher. Si vous utilisez une bibliothèque de navigation dans votre application, utilisez-la ici pour afficher la destination appropriée. Une instruction "when" peut suffire:

NavigationSuiteScaffold(
    navigationSuiteItems = { /*...*/ }
) {
    // Destination content.
    when (currentDestination) {
        AppDestinations.HOME -> HomeDestination()
        AppDestinations.FAVORITES -> FavoritesDestination()
        AppDestinations.SHOPPING -> ShoppingDestination()
        AppDestinations.PROFILE -> ProfileDestination()
    }
}

Modifier les couleurs

NavigationSuiteScaffold crée un Surface sur la totalité de la zone occupée par l'échafaudage, généralement toute la fenêtre. En outre, l'échafaudage dessine l'interface utilisateur de navigation particulière, telle qu'un élément NavigationBar. La surface et l'interface utilisateur de navigation utilisent les valeurs spécifiées dans le thème de votre application, mais vous pouvez remplacer les valeurs du thème.

Le paramètre containerColor spécifie la couleur de la surface. La couleur par défaut est la couleur d'arrière-plan de votre jeu de couleurs. Le paramètre contentColor spécifie la couleur du contenu sur cette surface. La valeur par défaut est la couleur "on" (activée) de tout élément spécifié pour containerColor. Par exemple, si containerColor utilise la couleur background, contentColor utilise la couleur onBackground. Pour en savoir plus sur le fonctionnement du système de couleurs, consultez la section Thématisation Material Design 3 dans Compose. Lorsque vous remplacez ces valeurs, utilisez des valeurs définies dans votre thème afin que votre application soit compatible avec les modes d'affichage sombre et clair:

NavigationSuiteScaffold(
    navigationSuiteItems = { /* ... */ },
    containerColor = MaterialTheme.colorScheme.primary,
    contentColor = MaterialTheme.colorScheme.onPrimary,
) {
    // Content...
}

L'UI de navigation est affichée devant la surface NavigationSuiteScaffold. Les valeurs par défaut des couleurs de l'interface utilisateur sont fournies par NavigationSuiteDefaults.colors(), mais vous pouvez également les remplacer. Par exemple, si vous souhaitez que l'arrière-plan de la barre de navigation soit transparent, mais que les autres valeurs soient les valeurs par défaut, remplacez navigationBarContainerColor:

NavigationSuiteScaffold(
    navigationSuiteItems = { /* ... */ },
    navigationSuiteColors = NavigationSuiteDefaults.colors(
        navigationBarContainerColor = Color.Transparent,
    )
) {
    // Content...
}

Enfin, vous pouvez personnaliser chaque élément de l'interface utilisateur de navigation. Lorsque vous appelez la fonction item, vous pouvez transmettre une instance de NavigationSuiteItemColors. La classe spécifie les couleurs des éléments d'une barre de navigation, d'un rail de navigation et d'un panneau de navigation. Cela signifie que vous pouvez avoir des couleurs identiques pour chaque type d'UI de navigation ou que vous pouvez varier les couleurs en fonction de vos besoins. Définissez les couleurs au niveau NavigationSuiteScaffold afin d'utiliser la même instance d'objet pour tous les éléments et appelez la fonction NavigationSuiteDefaults.itemColors() pour ne remplacer que celles que vous souhaitez modifier:

val myNavigationSuiteItemColors = NavigationSuiteDefaults.itemColors(
    navigationBarItemColors = NavigationBarItemDefaults.colors(
        indicatorColor = MaterialTheme.colorScheme.primaryContainer,
        selectedIconColor = MaterialTheme.colorScheme.onPrimaryContainer
    ),
)

NavigationSuiteScaffold(
    navigationSuiteItems = {
        AppDestinations.entries.forEach {
            item(
                icon = {
                    Icon(
                        it.icon,
                        contentDescription = stringResource(it.contentDescription)
                    )
                },
                label = { Text(stringResource(it.label)) },
                selected = it == currentDestination,
                onClick = { currentDestination = it },
                colors = myNavigationSuiteItemColors,
            )
        }
    },
) {
    // Content...
}

Personnaliser les types de navigation

Le comportement par défaut de NavigationSuiteScaffold modifie l'interface utilisateur de navigation en fonction des classes de taille de fenêtre. Toutefois, vous pouvez ignorer ce comportement. Par exemple, si votre application affiche un seul grand volet de contenu pour un flux, elle peut utiliser un panneau de navigation permanent pour les fenêtres étendues, mais toujours revenir au comportement par défaut pour les classes de taille de fenêtre compacte et moyenne:

val adaptiveInfo = currentWindowAdaptiveInfo()
val customNavSuiteType = with(adaptiveInfo) {
    if (windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(adaptiveInfo)
    }
}

NavigationSuiteScaffold(
    navigationSuiteItems = { /* ... */ },
    layoutType = customNavSuiteType,
) {
    // Content...
}

Ressources supplémentaires

Consultez les conseils sur Material Design:

Consultez les composants de la bibliothèque androidx.compose.material3 suivants: