Créer des applications adaptatives avec Jetpack Compose

1. Introduction

Dans cet atelier de programmation, vous allez apprendre à créer des applications adaptatives pour les téléphones, les tablettes et les pliables, et à améliorer la maniabilité avec Jetpack Compose. Vous découvrirez également les bonnes pratiques concernant l'utilisation des composants et de la thématisation Material 3.

Avant d'entrer dans le vif du sujet, il est important de comprendre ce que nous entendons par "adaptabilité".

Adaptabilité

L'interface utilisateur de votre application doit être responsive afin de tenir compte des différentes tailles de fenêtre, orientations et facteurs de forme. La mise en page adaptative change en fonction de l'espace disponible à l'écran. Ces modifications vont d'un simple ajustement de la mise en page pour remplir l'espace, en choisissant des styles de navigation adaptés, à un changement complet de la mise en page afin d'utiliser l'espace supplémentaire.

Pour en savoir plus, consultez Design adaptatif.

Dans cet atelier de programmation, vous allez apprendre à utiliser Jetpack Compose et réfléchir à son adaptabilité. Vous allez créer une application (appelée Reply) pour savoir comment implémenter l'adaptabilité sur tous les types d'écrans. Vous découvrirez comment la maniabilité et la maniabilité fonctionnent ensemble pour offrir une expérience utilisateur optimale.

Points abordés

  • Concevoir votre application pour cibler toutes les tailles de fenêtre avec Jetpack Compose.
  • Cibler votre application pour différents pliables.
  • Utiliser différents types de navigation pour améliorer la maniabilité et l'accessibilité.
  • Comment utiliser les composants Material 3 pour offrir la meilleure expérience possible pour chaque taille de fenêtre.

Ce dont vous avez besoin

Dans cet atelier de programmation, vous allez utiliser l'émulateur redimensionnable, qui vous permet de basculer entre différents types d'appareils et tailles de fenêtre.

Émulateur redimensionnable avec options de téléphone, pliable déplié, tablette et ordinateur.

Si vous ne connaissez pas Compose, vous pouvez suivre l'atelier de programmation sur les principes de base de Jetpack Compose avant de le terminer.

Objectifs de l'atelier

  • Une application cliente de messagerie interactive qui utilise les bonnes pratiques pour des conceptions adaptables, différentes navigations Material et une utilisation optimale de l'espace à l'écran.

Présentation de la compatibilité multiappareil que vous obtiendrez avec cet atelier de programmation

2. Configuration

Pour obtenir le code de cet atelier de programmation, clonez le dépôt GitHub à partir de la ligne de commande:

git clone https://github.com/android/codelab-android-compose.git
cd codelab-android-compose/AdaptiveUiCodelab

Vous pouvez aussi télécharger le dépôt sous la forme d'un fichier ZIP :

Télécharger le fichier ZIP

Nous vous recommandons de commencer par le code de la branche main, puis de suivre l'atelier étape par étape, à votre propre rythme.

Ouvrir un projet dans Android Studio

  1. Dans la fenêtre Welcome to Android Studio (Bienvenue dans Android Studio), sélectionnez c01826594f360d94.pngOpen an existing Project (Ouvrir un projet existant).
  2. Sélectionnez le dossier <Download Location>/AdaptiveUiCodelab. (assurez-vous de sélectionner le répertoire AdaptiveUiCodelab contenant build.gradle.)
  3. Une fois qu'Android Studio a importé le projet, vérifiez que vous pouvez exécuter la branche main.

Se familiariser avec le code de démarrage

Le code de la branche main contient le package ui. Vous allez utiliser les fichiers suivants dans ce package:

  • MainActivity.kt : activité de point d'entrée à partir de laquelle vous démarrez votre application.
  • ReplyApp.kt : contient les composables de l'UI de l'écran principal.
  • ReplyHomeViewModel.kt : fournit les données et l'état de l'interface utilisateur pour le contenu de l'application.
  • ReplyListContent.kt : contient les composables permettant de fournir des listes et des écrans détaillés.

Vous allez d'abord vous concentrer sur MainActivity.kt.

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )
        }
    }
}

Si vous exécutez cette application sur un émulateur redimensionnable et que vous essayez différents types d'appareils (tels qu'un téléphone ou une tablette), l'UI épouse l'espace donné plutôt que d'exploiter l'espace de l'écran ou de fournir une ergonomie maniable.

Écran initial sur un téléphone

Vue initiale étirée sur une tablette

Vous le mettrez à jour pour profiter de l'espace à l'écran, augmenter la facilité d'utilisation et améliorer l'expérience utilisateur globale.

3. Assurer l'adaptation des applications

Cette section explique en quoi consiste l'adaptation des applications et quels sont les composants fournis par Material 3 pour vous faciliter la tâche. Il couvre également les types d'écrans et les États que vous allez cibler, y compris les téléphones, les tablettes, les grandes tablettes et les pliables.

Vous commencerez par examiner les principes de base des tailles de fenêtre, des positions de pliage et des différents types d'options de navigation. Vous pouvez ensuite utiliser ces API dans votre application pour la rendre plus adaptative.

Tailles de fenêtre

Les appareils Android se déclinent sous toutes les formes et toutes les tailles, des téléphones aux pliables, en passant par les tablettes et les appareils ChromeOS. Pour accepter un maximum de tailles de fenêtre, votre UI doit être responsive et adaptative. Pour vous aider à identifier le seuil approprié pour modifier l'interface utilisateur de votre application, nous avons défini des valeurs de point d'arrêt qui permettent de classer les appareils en classes de tailles prédéfinies (compacte, moyenne et étendue), appelées classes de taille de fenêtre. Cet ensemble de points d'arrêt de fenêtre d'affichage définis permettent de concevoir, de développer et de tester des mises en page d'applications responsives et adaptatives.

Les catégories ont été spécialement choisies pour équilibrer la simplicité de la mise en page et la flexibilité qui permet d'optimiser votre application dans des cas spécifiques. La classe de taille de fenêtre dépend toujours de l'espace d'écran disponible pour l'application, qui peut ne pas correspondre à l'intégralité de l'écran physique pour le multitâche ou d'autres segmentations.

WindowWidthSizeClass pour les largeurs compactes, moyennes et étendues.

WindowHeightSizeClass pour les hauteurs compactes, moyennes et étendues.

La largeur et la hauteur disponibles sont évaluées séparément. Votre application peut donc être associée à deux classes de taille de fenêtre : une pour la largeur et une pour la hauteur. La largeur disponible est généralement plus importante que la hauteur disponible en raison de l'omniprésence du défilement vertical. Dans ce cas, vous utiliserez donc également des classes de largeur.

États de pliage

Les appareils pliables présentent d'autres situations auxquelles votre application peut s'adapter en raison de leurs différentes tailles et de la présence de charnières. Les charnières peuvent obscurcir une partie de l'écran et rendre cette zone inadaptée à l'affichage de contenu. Elles peuvent également se séparer, c'est-à-dire qu'il existe deux écrans physiques distincts lorsque l'appareil est déplié.

Positions pliables, à plat et à moitié ouvert

De plus, l'utilisateur peut regarder l'écran intérieur lorsque la charnière est partiellement ouverte, ce qui entraîne différentes positions physiques en fonction de l'orientation du pli: position à plat (pli horizontal, illustré à droite dans l'image ci-dessus) et position du livre (pli vertical).

En savoir plus sur les positions et les charnières de pliage

Tous ces éléments sont à prendre en compte lors de l'implémentation de mises en page adaptatives compatibles avec les appareils pliables.

Obtenir des informations adaptatives

La bibliothèque adaptive Material3 fournit un accès pratique aux informations sur la fenêtre dans laquelle votre application s'exécute.

  1. Ajoutez des entrées pour cet artefact et sa version au fichier de catalogue de versions:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
  1. Dans le fichier de compilation du module d'application, ajoutez la nouvelle dépendance de bibliothèque, puis effectuez une synchronisation Gradle:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

Désormais, dans n'importe quel champ d'application composable, vous pouvez utiliser currentWindowAdaptiveInfo() pour obtenir un objet WindowAdaptiveInfo contenant des informations telles que la classe de taille de fenêtre actuelle et si l'appareil est dans une position pliable (position à plat, par exemple).

Vous pouvez essayer dans MainActivity.

  1. Dans onCreate(), à l'intérieur du bloc ReplyTheme, obtenez les informations adaptatives de la fenêtre et affichez les classes de taille dans un composable Text (vous pouvez ajouter ceci après l'élément ReplyApp()):

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )

            val adaptiveInfo = currentWindowAdaptiveInfo()
            val sizeClassText =
                "${adaptiveInfo.windowSizeClass.windowWidthSizeClass}\n" +
                "${adaptiveInfo.windowSizeClass.windowHeightSizeClass}"
            Text(
                text = sizeClassText,
                color = Color.Magenta,
                modifier = Modifier.padding(20.dp)
            )
        }
    }
}

Si vous exécutez l'application maintenant, les classes de taille de fenêtre s'afficheront sur le contenu de l'application. N'hésitez pas à explorer les autres éléments fournis dans les informations adaptatives de la fenêtre. Vous pouvez ensuite supprimer ce Text, car il couvre le contenu de l'application. Il ne sera pas nécessaire pour les étapes suivantes.

4. Navigation dynamique

Vous allez maintenant adapter la navigation de l'application à mesure que l'état et la taille de l'appareil changent afin d'améliorer la maniabilité.

La maniabilité désigne la capacité à naviguer dans une application ou à interagir avec elle sans devoir placer ses mains dans une position extrême ni les déplacer. Lorsque les utilisateurs tiennent un téléphone, leurs doigts sont généralement en bas de l'écran. Lorsque les utilisateurs tiennent un appareil pliable ouvert ou une tablette, leurs doigts sont généralement proches des côtés. Lorsque vous concevez votre application et décidez où placer les éléments interactifs de l'interface utilisateur dans votre mise en page, tenez compte des implications ergonomiques des différentes régions de l'écran.

  • Quelles zones peuvent être facilement accessibles lorsque vous tenez l'appareil ?
  • Quelles zones ne peuvent être atteintes qu'en étendant les doigts, ce qui peut être gênant ?
  • Quelles zones sont difficiles à atteindre ou sont éloignées de l'endroit où l'utilisateur tient l'appareil ?

La navigation est la première chose avec laquelle les utilisateurs interagissent. Elle contient des actions très importantes en lien avec les parcours utilisateur critiques. Elle doit donc être placée dans les zones les plus accessibles. Material fournit plusieurs composants qui vous aident à implémenter la navigation, en fonction de la classe de taille de fenêtre de l'appareil.

Barre de navigation inférieure

La barre de navigation inférieure est idéale pour les tailles compactes. En effet, nous tenons naturellement l'appareil de sorte que notre pouce atteigne facilement tous les points de contact inférieurs. Utilisez-la lorsque l'appareil est de taille compacte ou que le pliable est de taille compacte en position pliée.

Barre de navigation inférieure avec éléments

Pour une fenêtre de largeur moyenne, le rail de navigation est idéal pour la maniabilité, car notre pouce tombe naturellement sur le côté de l'appareil. Vous pouvez également associer un rail de navigation à un panneau de navigation pour afficher plus d'informations.

Rail de navigation avec éléments

Le panneau de navigation permet d'afficher facilement les informations détaillées sur les onglets de navigation. Il est facilement accessible lorsque vous utilisez une tablette ou un appareil plus grand. Il existe deux types de panneaux de navigation : un panneau de navigation modal et un panneau de navigation permanent.

Panneau de navigation modal

Vous pouvez utiliser un panneau de navigation modal pour les téléphones et tablettes compacts à moyennes, car il peut être développé ou masqué en superposition sur le contenu. Cette fonctionnalité peut parfois être associée à un rail de navigation.

Panneau de navigation modal avec éléments

Panneau de navigation permanent

Vous pouvez utiliser un panneau de navigation permanent pour une navigation fixe sur les grandes tablettes, les Chromebooks et les ordinateurs.

Panneau de navigation permanent avec éléments

Implémenter la navigation dynamique

Vous allez maintenant passer d'un type de navigation à un autre lorsque l'état et la taille de l'appareil changent.

Actuellement, l'application affiche toujours une NavigationBar sous le contenu de l'écran, quel que soit l'état de l'appareil. À la place, vous pouvez utiliser le composant Material NavigationSuiteScaffold pour basculer automatiquement entre les différents composants de navigation en fonction d'informations telles que la classe de taille de fenêtre actuelle.

  1. Ajoutez la dépendance Gradle pour obtenir ce composant en mettant à jour le catalogue de versions et le script de compilation de l'application, puis effectuez une synchronisation Gradle:

gradle/libs.versions.toml

[versions]
material3AdaptiveNavSuite = "1.3.0-beta01"

[libraries]
androidx-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3AdaptiveNavSuite" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.navigation.suite)
}
  1. Recherchez la fonction composable ReplyNavigationWrapper() dans ReplyApp.kt, puis remplacez le Column et son contenu par un NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            ReplyDestination.entries.forEach {
                item(
                    selected = it == selectedDestination,
                    onClick = { /*TODO update selection*/ },
                    icon = {
                        Icon(
                            imageVector = it.icon,
                            contentDescription = stringResource(it.labelRes)
                        )
                    },
                    label = {
                        Text(text = stringResource(it.labelRes))
                    },
                )
            }
        }
    ) {
        content()
    }
}

L'argument navigationSuiteItems est un bloc qui vous permet d'ajouter des éléments à l'aide de la fonction item(), de la même manière que l'ajout d'éléments dans une LazyColumn. Dans le lambda de fin, ce code appelle le content() transmis en tant qu'argument à ReplyNavigationWrapperUI().

Exécutez l'application sur l'émulateur et essayez de changer de taille entre téléphone, pliable et tablette. La barre de navigation se transforme en rail de navigation, puis à l'arrière.

Sur les fenêtres très larges, comme sur une tablette en mode paysage, vous pouvez afficher le panneau de navigation permanent. NavigationSuiteScaffold permet d'afficher un panneau permanent, bien qu'il ne figure dans aucune des valeurs WindowWidthSizeClass actuelles. Cependant, vous pouvez le faire avec une légère modification.

  1. Ajoutez le code suivant juste avant l'appel à NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    val windowSize = with(LocalDensity.current) {
        currentWindowSize().toSize().toDpSize()
    }
    val layoutType = if (windowSize.width >= 1200.dp) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(
            currentWindowAdaptiveInfo()
        )
    }

    NavigationSuiteScaffold(
        layoutType = layoutType,
        ...
    ) {
        content()
    }
}

Ce code obtient d'abord la taille de la fenêtre et la convertit en unités de DP à l'aide de currentWindowSize() et LocalDensity.current, puis compare la largeur de la fenêtre pour décider du type de mise en page de l'UI de navigation. Si la largeur de la fenêtre est d'au moins 1200.dp, la valeur NavigationSuiteType.NavigationDrawer est utilisée. Sinon, le calcul par défaut est appliqué.

Exécutez à nouveau l'application sur l'émulateur redimensionnable pour les différents types d'appareils. Vous remarquerez que la navigation s'adapte en fonction de la taille à chaque fois que la configuration de l'écran change ou que vous dépliez un appareil pliable.

Changements d&#39;adaptation aux différentes tailles d&#39;appareils

Félicitations, vous avez découvert les différents types de navigation pour prendre en charge différents types de tailles et d'états de fenêtre !

Dans la section suivante, vous allez apprendre à exploiter les zones d'écran restantes au lieu d'étirer un même élément de liste d'un bord à l'autre.

5. Utilisation de l'espace à l'écran

Que vous exécutiez l'application sur une petite tablette, un appareil déplié ou une grande tablette, l'écran s'étire pour occuper l'espace restant. Assurez-vous d'exploiter tout cet espace à l'écran pour afficher d'autres d'informations, comme dans cette application qui réunit la messagerie et les fils de discussion sur une même page.

Material 3 définit trois mises en page standards qui ont chacune des configurations pour les classes de taille de fenêtre compacte, moyenne et grande. La mise en page canonique Détail de la liste est idéale pour ce cas d'utilisation. Elle est disponible dans Compose en tant que ListDetailPaneScaffold.

  1. Obtenez ce composant en ajoutant les dépendances suivantes et en effectuant une synchronisation Gradle:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[libraries]
androidx-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "material3Adaptive" }
androidx-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "material3Adaptive" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.layout)
    implementation(libs.androidx.material3.adaptive.navigation)
}
  1. Recherchez la fonction composable ReplyAppContent() dans ReplyApp.kt, qui n'affiche actuellement que le volet de liste en appelant ReplyListPane(). Remplacez cette implémentation par ListDetailPaneScaffold en insérant le code suivant. Comme il s'agit d'une API expérimentale, vous allez également ajouter l'annotation @OptIn à la fonction ReplyAppContent():

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            ReplyListPane(replyHomeUIState, onEmailClick)
        },
        detailPane = {
            ReplyDetailPane(replyHomeUIState.emails.first())
        }
    )
}

Ce code crée d'abord un navigateur à l'aide de rememberListDetailPaneNavigator(). Le navigateur permet de contrôler le volet affiché et le contenu à représenter dans ce volet. Nous en reparlerons plus tard.

ListDetailPaneScaffold affiche deux volets lorsque la classe de largeur de fenêtre est étendue. Sinon, il affichera un volet ou l'autre en fonction des valeurs fournies pour deux paramètres: la directive d'échafaudage et la valeur d'échafaudage. Pour obtenir le comportement par défaut, ce code utilise la directive d'échafaudage et la valeur d'échafaudage fournie par le navigateur.

Les paramètres obligatoires restants sont des lambdas composables pour les volets. ReplyListPane() et ReplyDetailPane() (disponibles dans ReplyListContent.kt) permettent respectivement de remplir les rôles des volets de liste et de détail. ReplyDetailPane() attend un argument d'adresse e-mail. Pour l'instant, ce code utilise donc la première adresse e-mail de la liste des adresses dans ReplyHomeUIState.

Exécutez l'application et basculez la vue de l'émulateur sur pliable ou tablette (vous devrez peut-être également changer l'orientation) pour afficher la mise en page à deux volets. C'est déjà beaucoup mieux qu'avant !

Voyons maintenant certains des comportements souhaités sur cet écran. Lorsque l'utilisateur appuie sur un e-mail dans le volet de liste, celui-ci doit s'afficher dans le volet des détails avec toutes les réponses. Actuellement, l'application n'effectue pas le suivi de l'e-mail sélectionné et le fait d'appuyer sur un élément n'a aucun effet. Le meilleur endroit pour conserver ces informations est le reste de l'état de l'UI dans ReplyHomeUIState.

  1. Ouvrez ReplyHomeViewModel.kt et recherchez la classe de données ReplyHomeUIState. Ajoutez une propriété pour l'adresse e-mail sélectionnée, avec une valeur par défaut de null:

ReplyHomeViewModel.kt

data class ReplyHomeUIState(
    val emails : List<Email> = emptyList(),
    val selectedEmail: Email? = null,
    val loading: Boolean = false,
    val error: String? = null
)
  1. Dans le même fichier, ReplyHomeViewModel comporte une fonction setSelectedEmail() qui est appelée lorsque l'utilisateur appuie sur un élément de la liste. Modifiez cette fonction pour copier l'état de l'interface utilisateur et enregistrer l'e-mail sélectionné:

ReplyHomeViewModel.kt

fun setSelectedEmail(email: Email) {
    _uiState.update {
        it.copy(selectedEmail = email)
    }
}

Vous devez prendre en compte ce qui se passe avant que l'utilisateur n'ait appuyé sur un élément et que l'adresse e-mail sélectionnée est null. Que doit afficher le volet Détails ? Il existe plusieurs façons de gérer ce cas, comme afficher par défaut le premier élément de la liste.

  1. Dans le même fichier, modifiez la fonction observeEmails(). Lorsque la liste d'e-mails est chargée, si l'ancien état de l'interface utilisateur ne comportait pas d'adresse e-mail sélectionnée, définissez-le sur le premier élément:

ReplyHomeViewModel.kt

private fun observeEmails() {
    viewModelScope.launch {
        emailsRepository.getAllEmails()
            .catch { ex ->
                _uiState.value = ReplyHomeUIState(error = ex.message)
            }
            .collect { emails ->
                val currentSelection = _uiState.value.selectedEmail
                _uiState.value = ReplyHomeUIState(
                    emails = emails,
                    selectedEmail = currentSelection ?: emails.first()
                )
            }
    }
}
  1. Revenez à ReplyApp.kt et utilisez l'e-mail sélectionné, s'il est disponible, pour remplir le contenu du volet Détails:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    detailPane = {
        if (replyHomeUIState.selectedEmail != null) {
            ReplyDetailPane(replyHomeUIState.selectedEmail)
        }
    }
)

Exécutez à nouveau l'application et faites passer l'émulateur à la taille d'une tablette. Vous verrez que le fait d'appuyer sur un élément de la liste met à jour le contenu du volet de détails.

Cela fonctionne très bien lorsque les deux volets sont visibles, mais lorsque la fenêtre n'a de place pour afficher qu'un seul volet, il semble que rien ne se passe lorsque vous appuyez sur un élément. Essayez de basculer l'affichage de l'émulateur vers un téléphone ou un appareil pliable en mode portrait. Notez que seul le volet de liste est visible, même après avoir appuyé sur un élément. En effet, même si l'adresse e-mail sélectionnée est mise à jour, ListDetailPaneScaffold reste actif dans le volet de liste dans ces configurations.

  1. Pour résoudre ce problème, insérez le code suivant en tant que lambda transmis à ReplyListPane:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    listPane = {
        ReplyListPane(
            replyHomeUIState = replyHomeUIState,
            onEmailClick = { email ->
                onEmailClick(email)
                navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
            }
        )
    },
    // ...
)

Ce lambda utilise le navigateur créé précédemment pour ajouter un comportement supplémentaire lorsqu'un utilisateur clique sur un élément. Elle appellera le lambda d'origine transmis à cette fonction, puis appelle également navigator.navigateTo() en spécifiant le volet à afficher. Chaque volet de l'échafaudage est associé à un rôle. Pour le volet des détails, il s'agit de ListDetailPaneScaffoldRole.Detail. Sur les fenêtres de petite taille, cela donne l'impression que l'application a été avancée.

L'application doit également gérer ce qui se passe lorsque l'utilisateur appuie sur le bouton "Retour" dans le volet de détails. Ce comportement varie selon qu'un ou deux volets sont visibles.

  1. Prenez en charge la navigation vers l'arrière en ajoutant le code suivant.

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

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

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                ReplyListPane(
                    replyHomeUIState = replyHomeUIState,
                    onEmailClick = { email ->
                        onEmailClick(email)
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
                    }
                )
            }
        },
        detailPane = {
            AnimatedPane {
                if (replyHomeUIState.selectedEmail != null) {
                    ReplyDetailPane(replyHomeUIState.selectedEmail)
                }
            }
        }
    )
}

Le navigateur connaît l'état complet de ListDetailPaneScaffold, si la navigation vers l'arrière est possible et ce qu'il faut faire dans tous ces scénarios. Ce code crée un BackHandler qui est activé chaque fois que le navigateur peut revenir en arrière et qui appelle navigateBack() dans le lambda. De plus, pour faciliter la transition entre les volets, chacun d'eux est encapsulé dans un composable AnimatedPane().

Exécutez à nouveau l'application sur un émulateur redimensionnable pour tous les différents types d'appareils. Notez que chaque fois que la configuration de l'écran change ou que vous dépliez un appareil pliable, la navigation et le contenu de l'écran changent de manière dynamique en fonction de l'état de l'appareil. Appuyez également sur les e-mails dans le volet de liste pour observer le comportement de la mise en page sur différents écrans, en affichant les deux volets côte à côte ou en créant une animation fluide entre eux.

Changements d&#39;adaptation aux différentes tailles d&#39;appareils

Félicitations, vous avez adapté votre application à tous les états et les tailles d'appareils. Essayez d'exécuter l'application sur des appareils pliables, des tablettes ou d'autres appareils mobiles.

6. Félicitations

Félicitations ! Vous avez terminé cet atelier de programmation et appris à rendre des applications adaptatives avec Jetpack Compose.

Vous avez découvert comment vérifier la taille et l'état de pliage d'un appareil, et comment adapter l'UI, l'outil de navigation et d'autres fonctions en conséquence. Vous avez également appris comment l'adaptabilité améliore la maniabilité et l'expérience utilisateur.

Étape suivante

Consultez les autres ateliers de programmation du parcours Compose.

Exemples d'applications

  • Les exemples Compose sont une collection de nombreuses applications intégrant les bonnes pratiques expliquées dans des ateliers de programmation.

Documents de référence