Créer une mise en page liste/détails

Liste/détails est un modèle d'interface utilisateur qui se compose d'une mise en page à deux volets dans laquelle un volet présente une liste d'éléments, et un autre affiche les détails des éléments sélectionnés dans la liste.

Ce modèle est particulièrement utile pour les applications qui fournissent des informations détaillées sur les éléments de grandes collections, par exemple un client de messagerie contenant une liste d'e-mails et le contenu détaillé de chaque e-mail. Vous pouvez également utiliser la fonctionnalité Liste/Détail pour les chemins moins critiques, tels que la division des préférences d'application en une liste de catégories avec les préférences pour chaque catégorie dans le volet de détails.

Implémenter un modèle d'interface utilisateur avec ListDetailPaneScaffold

ListDetailPaneScaffold est un composable qui simplifie l'implémentation du modèle Liste/Détails dans votre application. Un échafaudage "Liste/Détail" peut comporter jusqu'à trois volets: un volet Liste, un volet Détails et un volet supplémentaire facultatif. L'échafaudage gère les calculs d'espace à l'écran. Lorsqu'une taille d'écran suffisante est disponible, le volet de vue détaillée s'affiche à côté du volet de liste. Sur les petites tailles d'écran, l'échafaudage passe automatiquement à l'affichage en plein écran de la liste ou du volet des détails.

Volet d'informations affiché à côté de la page de la liste.
Figure 1. Lorsque la taille d'écran disponible est suffisante, le volet de détails s'affiche à côté du volet de liste.
Une fois qu'un élément est sélectionné, le volet des détails occupe la totalité de l'écran.
Figure 2. Lorsque la taille de l'écran est limitée, le volet des détails (puisque un élément a été sélectionné) occupe la totalité de l'espace.

Déclarer des dépendances

ListDetailPaneScaffold fait partie de la bibliothèque adaptative Material 3. Ajoutez une dépendance pour la bibliothèque dans le fichier build.gradle de votre application ou de votre module:

implementation("androidx.compose.material3.adaptive:adaptive:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha07")

Utilisation de base

Voici l'utilisation de base de ListDetailPaneScaffold:

  1. Stockez l'élément actuellement sélectionné dans la liste dans une variable d'état modifiable. La variable contient l'élément à afficher dans le volet des détails. En règle générale, vous devez initialiser l'élément actuellement sélectionné avec null, ce qui indique qu'aucune sélection n'a encore été effectuée.

    class MyItem(val id: Int) {
        companion object {
            val Saver: Saver<MyItem?, Int> = Saver(
                { it?.id },
                ::MyItem,
            )
        }
    }

    var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
        mutableStateOf(null)
    }

  2. Créez le ThreePaneScaffoldNavigator avec rememberListDetailPaneScaffoldNavigator et ajoutez un BackHandler. Ce navigateur permet de naviguer entre les volets de liste, de détails et supplémentaires, et de fournir l'état à l'échafaudage. Le BackHandler ajouté permet de revenir en arrière à l'aide du geste ou du bouton Retour du système. Le comportement attendu du bouton "Retour" pour une ListDetailPaneScaffold dépend de la taille de la fenêtre et de la valeur d'échafaudage actuelle. Si ListDetailPaneScaffold peut permettre de revenir à l'état actuel, canNavigateBack() sera true, ce qui activera BackHandler.

    val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. Transmettez le scaffoldState du navigator que vous avez créé au composable ListDetailPaneScaffold.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

  4. Fournissez l'implémentation de votre volet de liste au ListDetailPaneScaffold. Assurez-vous que votre implémentation inclut un argument de rappel pour capturer le nouvel élément sélectionné. Lorsque ce rappel est déclenché, mettez à jour la variable d'état selectedItem et utilisez ThreePaneScaffoldNavigator pour afficher le volet de détails (ListDetailPaneScaffoldRole.Detail).

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane(Modifier) {
                MyList(
                    onItemClick = { id ->
                        // Set current item
                        selectedItem = id
                        // Switch focus to detail pane
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                    }
                )
            }
        },
        // ...
    )

  5. Incluez l'implémentation du volet de détails dans ListDetailPaneScaffold. N'affichez le contenu détaillé que si la valeur de selectedItem n'est pas nulle.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane(Modifier) {
                selectedItem?.let { item ->
                    MyDetails(item)
                }
            }
        },
    )

Une fois que vous avez suivi les étapes ci-dessus, votre code doit se présenter comme suit:

// Currently selected item
var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
    mutableStateOf(null)
}

// Create the ListDetailPaneScaffoldState
val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane(Modifier) {
            MyList(
                onItemClick = { id ->
                    // Set current item
                    selectedItem = id
                    // Display the detail pane
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane(Modifier) {
            // Show the detail pane content if selected item is available
            selectedItem?.let { item ->
                MyDetails(item)
            }
        }
    },
)