La mise en page avec volet secondaire permet à l'utilisateur de se concentrer sur le contenu principal de l'application tout en affichant des informations complémentaires pertinentes. Par exemple, le volet principal peut afficher des informations sur un film, tandis que le volet secondaire répertorie des films similaires, des films du même réalisateur ou des œuvres mettant en scène les mêmes acteurs.
Pour en savoir plus, consultez les consignes concernant le volet secondaire Material 3.
Implémenter un volet secondaire avec un échafaudage (scaffolding)
NavigableSupportingPaneScaffold est un composable qui simplifie l'implémentation d'une mise en page avec volet secondaire dans Jetpack Compose. Il encapsule
SupportingPaneScaffold et ajoute une navigation intégrée et une gestion prédictive du retour.
Un échafaudage avec volet secondaire accepte jusqu'à trois volets :
- Volet principal : affiche le contenu principal.
- Volet secondaire : fournit un contexte ou des outils supplémentaires liés au volet principal.
- Volet supplémentaire (facultatif) : utilisé pour le contenu supplémentaire si nécessaire.
L'échafaudage s'adapte en fonction de la taille de la fenêtre :
- Dans les grandes fenêtres, les volets principal et secondaire s'affichent côte à côte.
Dans les petites fenêtres, un seul volet est visible à la fois, et il change lorsque l'utilisateur navigue.
Figure 1. Mise en page avec volet secondaire.
Ajouter des dépendances
NavigableSupportingPaneScaffold fait partie de la bibliothèque de mise en page adaptative Material 3.
Ajoutez les trois dépendances associées suivantes au fichier build.gradle de votre application ou module :
Kotlin
implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")
Groovy
implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
adaptatif : blocs de construction de bas niveau tels que
HingeInfoetPostureadaptive-layout : mises en page adaptatives telles que
ListDetailPaneScaffoldetSupportingPaneScaffoldadaptive-navigation : composables pour naviguer dans et entre les volets, ainsi que des mises en page adaptatives qui prennent en charge la navigation par défaut, telles que
NavigableListDetailPaneScaffoldetNavigableSupportingPaneScaffold
Vérifiez que votre projet inclut la version 1.1.0-beta1 ou une version ultérieure de compose-material3-adaptive.
Activer la prévisualisation du geste Retour
Pour activer les animations de prévisualisation du retour dans Android 15 ou une version antérieure, vous devez activer la prise en charge de la prévisualisation du geste Retour. Pour ce faire, ajoutez
android:enableOnBackInvokedCallback="true" à la balise <application> ou
à des balises <activity> individuelles dans votre fichier AndroidManifest.xml.
Une fois que votre application cible Android 16 (niveau d'API 36) ou une version ultérieure, la prévisualisation du retour est activée par défaut.
Créer un navigateur
Dans les petites fenêtres, un seul volet s'affiche à la fois. Utilisez donc un
ThreePaneScaffoldNavigator pour passer d'un volet à l'autre. Créez une instance
du navigateur avec rememberSupportingPaneScaffoldNavigator.
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope()
Transmettre le navigateur à l'échafaudage
L'échafaudage nécessite un ThreePaneScaffoldNavigator qui est une interface
représentant l'état de l'échafaudage, le ThreePaneScaffoldValue et un
PaneScaffoldDirective.
NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { /*...*/ }, supportingPane = { /*...*/ }, )
Les volets principal et secondaire sont des composables contenant votre contenu. Utilisez
AnimatedPane pour appliquer les animations de volet par défaut lors de la navigation. Utilisez
la valeur de l'échafaudage pour vérifier si le volet secondaire est masqué. Si c'est le cas,
affichez un bouton qui appelle
navigateTo(SupportingPaneScaffoldRole.Supporting) pour afficher le
volet secondaire.
Pour les grands écrans, utilisez la ThreePaneScaffoldNavigator.navigateBack()
méthode pour fermer le volet secondaire, en transmettant la
BackNavigationBehavior.PopUntilScaffoldValueChange constante. L'appel de cette
méthode force une recomposition du NavigableSupportingPaneScaffold.
Lors de la recomposition, vérifiez la
ThreePaneScaffoldNavigator.currentDestination propriété pour déterminer
s'il faut afficher le volet secondaire.
Voici une implémentation complète de l'échafaudage :
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() val backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { AnimatedPane( modifier = Modifier .safeContentPadding() .background(Color.Red) ) { if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) { Button( modifier = Modifier .wrapContentSize(), onClick = { scope.launch { scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting) } } ) { Text("Show supporting pane") } } else { Text("Supporting pane is shown") } } }, supportingPane = { AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = Modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } } )
Extraire les composables de volet
Extrayez les volets individuels d'un SupportingPaneScaffold dans leurs propres composables pour les rendre réutilisables et testables. Utilisez ThreePaneScaffoldScope
pour accéder à AnimatedPane si vous souhaitez utiliser les animations par défaut :
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun ThreePaneScaffoldPaneScope.MainPane( shouldShowSupportingPaneButton: Boolean, onNavigateToSupportingPane: () -> Unit, modifier: Modifier = Modifier, ) { AnimatedPane( modifier = modifier.safeContentPadding() ) { // Main pane content if (shouldShowSupportingPaneButton) { Button(onClick = onNavigateToSupportingPane) { Text("Show supporting pane") } } else { Text("Supporting pane is shown") } } } @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun ThreePaneScaffoldPaneScope.SupportingPane( scaffoldNavigator: ThreePaneScaffoldNavigator<Any>, modifier: Modifier = Modifier, backNavigationBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange, ) { val scope = rememberCoroutineScope() AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } }
L'extraction des volets dans des composables simplifie l'utilisation de SupportingPaneScaffold (comparez ce qui suit à l'implémentation complète de l'échafaudage dans la section précédente) :
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { MainPane( shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden, onNavigateToSupportingPane = { scope.launch { scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary) } } ) }, supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) }, )
Si vous avez besoin de plus de contrôle sur des aspects spécifiques de l'échafaudage, envisagez d'utiliser
SupportingPaneScaffold au lieu de NavigableSupportingPaneScaffold. Cela
accepte un PaneScaffoldDirective et un ThreePaneScaffoldValue ou
ThreePaneScaffoldState séparément. Cette flexibilité vous permet d'implémenter une logique personnalisée pour l'espacement des volets et de déterminer le nombre de volets à afficher simultanément. Vous pouvez également activer la prise en charge de la prévisualisation du retour en ajoutant ThreePaneScaffoldPredictiveBackHandler.
Ajouter ThreePaneScaffoldPredictiveBackHandler
Associez le gestionnaire de prévisualisation du retour qui prend une instance de navigateur d'échafaudage et spécifiez le backBehavior. Cela détermine comment les destinations sont retirées de la pile "Retour" lors de la navigation de retour. Transmettez ensuite le scaffoldDirective et le scaffoldState à SupportingPaneScaffold. Utilisez la surcharge qui accepte un ThreePaneScaffoldState, en transmettant scaffoldNavigator.scaffoldState.
Définissez les volets principal et secondaire dans SupportingPaneScaffold. Utilisez AnimatedPane pour les animations de volet par défaut.
Une fois ces étapes terminées, votre code doit se présenter comme suit :
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() ThreePaneScaffoldPredictiveBackHandler( navigator = scaffoldNavigator, backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange ) SupportingPaneScaffold( directive = scaffoldNavigator.scaffoldDirective, scaffoldState = scaffoldNavigator.scaffoldState, mainPane = { MainPane( shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden, onNavigateToSupportingPane = { scope.launch { scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary) } } ) }, supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) }, )