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 la section 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é.
  • Utiliser les composants Material 3 pour offrir une expérience optimale en fonction de la taille de fenêtre

Prérequis

  • La dernière version stable d'Android Studio
  • Un appareil virtuel redimensionnable Android 13
  • Connaissances de Kotlin.
  • Connaissances de base de Compose (annotation @Composable, entre autres).
  • Connaissances de base sur les mises en page Compose (Row et Column, par exemple).
  • Connaissances de base sur les modificateurs (par exemple, Modifier.padding())

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, il peut être utile de suivre l'atelier de programmation Principes de base de Jetpack Compose avant de poursuivre.

Ce que vous allez faire

  • Une application cliente de messagerie interactive appelée Reply, 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 le 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 du package:

  • MainActivity.kt : activité de point d'entrée à partir de laquelle vous démarrez l'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.

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 définit l'adaptation des applications et les composants que Material 3 met à votre disposition pour simplifier 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 passer en revue les principes liés aux tailles de fenêtre, aux positions de pliage et aux 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 à déterminer le seuil auquel modifier l'UI de votre application, nous avons défini des valeurs de point d'arrêt. Elles permettent de classer les appareils en classes de tailles prédéfinies (compacte, moyenne et étendue), appelées classes de tailles 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 une largeur compacte, moyenne et étendue.

WindowHeightSizeClass pour une hauteur compacte, moyenne et étendue.

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 utilisez également les classes de largeur.

États de pliage

Les appareils pliables présentent encore plus de 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 masquer une partie de l'écran, ce qui rend cette zone inadaptée à l'affichage de contenu. Elles peuvent également se séparer, ce qui signifie 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 debout (pli vertical).

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

Tous ces éléments doivent être pris en compte lors de l'implémentation de mises en page adaptatives compatibles avec les appareils pliables.

Obtenir des informations adaptatives

La bibliothèque Material3 adaptive permet d'accéder facilement 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"

[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 de 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, comme la position sur table.

Vous pouvez essayer de le faire dès maintenant dans MainActivity.

  1. Dans onCreate(), dans le bloc ReplyTheme, obtenez les informations d'adaptation de la fenêtre et affichez les classes de taille dans un composable Text. Vous pouvez l'ajouter 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(
                    WindowInsets.safeDrawing.asPaddingValues()
                )
            )
        }
    }
}

Exécuter l'application maintenant affichera les classes de taille de fenêtre imprimées sur le contenu de l'application. N'hésitez pas à explorer les autres informations fournies dans les informations d'adaptation de la fenêtre. Vous pouvez ensuite supprimer cet élément Text, car il recouvre le contenu de l'application et n'est pas nécessaire pour les prochaines étapes.

4. Navigation dynamique

Vous allez maintenant adapter la navigation de l'application en fonction de l'état et de la taille de l'appareil afin de la rendre plus facile à utiliser.

Lorsque l'utilisateur tient son téléphone, ses 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. Vos utilisateurs doivent pouvoir naviguer ou interagir avec une application sans devoir placer leurs mains dans une position extrême ni les déplacer.

Lorsque vous concevez votre application et que vous décidez où placer les éléments d'interface utilisateur interactifs dans votre mise en page, tenez compte des implications ergonomiques des différentes régions de l'écran.

  • Quelles zones sont 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 le premier élément avec lequel les utilisateurs interagissent. Elle contient des actions de grande importance liées à des parcours utilisateur critiques. Elle doit donc être placée dans les zones les plus faciles d'accès. La bibliothèque adaptative de 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 le pouce se place naturellement sur le côté de l'appareil. Vous pouvez également combiner un rail de navigation et 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 compacts et les tablettes de taille moyenne. En effet, il peut être développé ou masqué en superposition avec le contenu. Il peut parfois être associé à un rail de navigation.

Panneau de navigation modal avec éléments

Panneau de navigation permanent

Vous pouvez utiliser un panneau de navigation permanent pour résoudre les problèmes de navigation 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 en fonction de l'état et de la taille de l'appareil.

Actuellement, l'application affiche toujours une NavigationBar sous le contenu de l'écran, quel que soit l'état de l'appareil. Vous pouvez plutôt utiliser le composant Material NavigationSuiteScaffold pour passer automatiquement d'un composant de navigation à un autre 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"

[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 et remplacez Column et son contenu par 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(), comme vous le feriez dans un 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. Vous verrez la barre de navigation se transformer en rail de navigation, puis revenir à sa forme initiale.

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, mais il n'est affiché dans aucune des valeurs WindowWidthSizeClass actuelles. Vous pouvez toutefois le faire avec une légère modification.

  1. Ajoutez le code suivant juste avant l'appel de 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 DP à l'aide de currentWindowSize() et LocalDensity.current, puis compare la largeur de la fenêtre pour déterminer le 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 utilisé.

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 connaissez les types de navigation compatibles avec les différents types de tailles et é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 canoniques, chacune avec des configurations pour les classes de taille de fenêtre compacte, moyenne et agrandie. 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. Pour obtenir ce composant, ajoutez les dépendances suivantes et effectuez une synchronisation Gradle:

gradle/libs.versions.toml

[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 devez é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 qui doit y être représenté, comme nous le verrons plus tard.

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

Les autres paramètres obligatoires 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 le moment, ce code utilise donc le premier e-mail de la liste d'e-mails dans ReplyHomeUIState.

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

Passons maintenant à certains des comportements souhaités de cet écran. Lorsque l'utilisateur appuie sur un e-mail dans le volet de liste, il doit s'afficher dans le volet d'informations avec toutes les réponses. Actuellement, l'application ne garde pas la trace de l'e-mail sélectionné, et 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'e-mail sélectionné, 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 possède une fonction setSelectedEmail() qui est appelée lorsque l'utilisateur appuie sur un élément de 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 réfléchir à ce qui se passe avant que l'utilisateur n'ait appuyé sur un élément et que l'adresse e-mail sélectionnée soit null. Que doit-il afficher dans le volet d'informations ? Il existe plusieurs façons de gérer ce cas, par exemple en affichant le premier élément de la liste par défaut.

  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 renseigner le contenu du volet d'informations:

ReplyApp.kt

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

Exécutez à nouveau l'application et définissez l'émulateur sur la taille d'une tablette. Vous constaterez que l'appui sur un élément de liste met à jour le contenu du volet d'informations.

Cela fonctionne très bien lorsque les deux volets sont visibles, mais lorsque la fenêtre n'a de place que pour afficher 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'e-mail sélectionné est mis à jour, le ListDetailPaneScaffold maintient la sélection sur 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. Il appelle 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 d'informations, 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 depuis le volet d'informations. Ce comportement sera différent 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 l'ListDetailPaneScaffold, si la navigation arrière est possible et ce qu'il doit 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 rendre la transition entre les volets beaucoup plus fluide, chaque volet est encapsulé dans un composable AnimatedPane().

Exécutez à nouveau l'application sur un émulateur redimensionnable pour les différents types d'appareils. Vous remarquerez que la navigation et le contenu de l'écran s'adaptent de façon dynamique en fonction de l'état de l'appareil à chaque fois que la configuration de l'écran change ou que vous dépliez un appareil pliable. Essayez également d'appuyer sur des e-mails dans le volet de liste et de voir comment la mise en page se comporte sur différents écrans, en affichant les deux volets côte à côte ou en les animant de manière fluide.

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 à créer 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.

Applications exemples

  • 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