Mises en page de base dans Compose

1. Introduction

En tant que kit d'UI, Compose facilite l'implémentation des conceptions de votre application. Vous décrivez l'apparence que vous souhaitez pour votre UI, et Compose se charge de la dessiner à l'écran. Dans cet atelier de programmation, vous apprendrez à écrire des UI Compose. Nous partons du principe que vous comprenez les concepts enseignés dans l'atelier de programmation sur les principes de base. Dès lors, assurez-vous de commencer par cet atelier. Dans cet atelier sur les principes de base, vous avez appris à implémenter des mises en page simples à l'aide de Surfaces, Rows et Columns. Vous avez également enrichi ces mises en page avec des modificateurs tels que padding, fillMaxWidth et size.

Dans cet atelier de programmation, vous allez implémenter une mise en page plus réaliste et plus complexe, et découvrir différents modificateurs et composables prêts à l'emploi. À la fin de cet atelier de programmation, vous devriez être en mesure de transformer la conception d'une application de base en code fonctionnel.

Cet atelier de programmation n'ajoute aucun comportement réel à l'application. Si vous souhaitez en savoir plus sur les états et les interactions, suivez plutôt l'atelier de programmation L'état dans Compose.

Pour obtenir de l'aide tout au long de cet atelier de programmation, reportez-vous au code suivant :

Points abordés

Cet atelier de programmation traite des points suivants :

  • Comment les modificateurs vous aident à enrichir vos composables
  • Comment les composants de mise en page standards, tels que Column et LazyRow, positionnent les composables enfants
  • Comment les alignements et les dispositions modifient la position des composables enfants dans leur élément parent
  • Comment les composables Material, tels que Scaffold et Bottom Navigation, vous aident à créer des mises en page complètes
  • Comment créer des composables flexibles à l'aide des API d'emplacement
  • Comment créer des mises en page pour différentes configurations d'écran

Ce dont vous avez besoin

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez implémenter une conception d'application réaliste sur la base de maquettes fournies par un concepteur. MySoothe est une application de bien-être qui liste différents moyens d'améliorer votre santé physique et mentale. Il contient une section qui présente vos collections préférées et une autre avec des exercices physiques. Voici comment se présente l'application :

Version portrait de l'application

Version paysage de l'application

2. Configuration

Au cours de cette étape, vous allez télécharger un code contenant des thèmes et une configuration de base.

Obtenir le code

Le code de cet atelier de programmation est disponible dans le dépôt GitHub codelab-android-compose. Pour le cloner, exécutez la commande suivante :

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

Vous pouvez également télécharger deux fichiers ZIP :

Consulter le code

Le code téléchargé contient du code pour tous les ateliers de programmation Compose disponibles. Pour cet atelier, ouvrez le projet BasicLayoutsCodelab dans Android Studio.

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

3. Commencer par un plan

Nous allons commencer par implémenter la conception portrait de l'application. Examinons cela de plus près :

conception portrait

Lorsque vous êtes invité à implémenter une conception, l'examen de sa structure constitue un bon point de départ. Ne commencez pas immédiatement le codage, mais analysez plutôt la conception proprement dite. Comment scinder cette UI en plusieurs parties réutilisables ?

Voyons cela avec notre conception. Au niveau d'abstraction le plus élevé, nous pouvons décomposer cette conception en deux parties :

  • Le contenu de l'écran
  • La navigation en bas de l'écran

détails de la conception de l'application

En y regardant d'un peu plus près, on constate que le contenu de l'écran se divise en trois sous-parties :

  • La barre de recherche
  • Une section intitulée "Align your body" (Aligner votre corps)
  • Une section intitulée "Favorite collections" (Collections préférées)

détails de la conception de l'application

Chaque section comprend également des composants de niveau inférieur qui sont réutilisés :

  • L'élément "Align your body" affiché sur une ligne à défilement horizontal

élément "Align your body" (Aligner votre corps)

  • La fiche "Favorite collections" affichée dans une grille à défilement horizontal

fiche "Favorite collections" (Collections préférées)

Maintenant que vous avez analysé la conception, vous pouvez commencer à implémenter des composables pour chaque élément identifié de l'interface utilisateur. Commencez par les composables du niveau le plus bas, puis continuez à les combiner en éléments plus complexes. À la fin de l'atelier de programmation, votre nouvelle application ressemblera à la conception fournie.

4. Barre de recherche : modificateurs

Le premier élément à transformer en composable est la barre de recherche. Revenons un instant à la conception :

barre de recherche

En se basant uniquement sur cette capture d'écran, il serait assez difficile d'implémenter la conception de façon optimale. En règle générale, un concepteur transmet plus d'informations sur la conception. Il peut vous donner accès à son outil de conception ou partager ce que l'on appelle des conceptions de révision (avec des lignes rouges). Dans le cas présent, notre concepteur a fourni des conceptions de révision que vous pouvez utiliser pour lire n'importe quelle valeur de dimensionnement. La conception est illustrée avec une superposition de grille de 8 dp. Vous pouvez ainsi voir facilement l'espace entre et autour des éléments. De plus, certains espacements sont explicitement ajoutés pour clarifier certaines tailles.

conception de révision de la barre de recherche

Comme vous pouvez le voir, la barre de recherche doit avoir une hauteur de 56 pixels indépendants de la densité. Elle doit également occuper toute la largeur de son élément parent.

Pour implémenter la barre de recherche, utilisez un composant Material appelé champ de texte. La bibliothèque Material de Compose contient un composable appelé TextField, qui est l'implémentation de ce composant Material.

Commencez par une implémentation TextField de base. Dans votre code base, ouvrez MainActivity.kt et recherchez le composable SearchBar.

Dans le composable appelé SearchBar, écrivez l'implémentation TextField de base :

import androidx.compose.material3.TextField

@Composable
fun SearchBar(
   modifier: Modifier = Modifier
) {
   TextField(
       value = "",
       onValueChange = {},
       modifier = modifier
   )
}

Quelques points à noter :

  • Vous avez codé en dur la valeur du champ de texte et le rappel onValueChange n'a aucun effet. Puisqu'il s'agit d'un atelier de programmation axé sur la mise en page, vous ignorez tout ce qui concerne l'état.
  • La fonction composable SearchBar accepte un paramètre modifier, qu'elle transmet à TextField. Il s'agit d'une bonne pratique conformément aux consignes de Compose. Cela permet à l'appelant de la méthode de modifier l'apparence du composable, ce qui le rend plus flexible et plus facile à réutiliser. Vous allez appliquer cette bonne pratique à tous les composables de cet atelier de programmation.

Observons l'aperçu de ce composable. Pour rappel, vous pouvez utiliser la fonctionnalité d'aperçu d'Android Studio pour itérer rapidement vos différents composables. MainActivity.kt contient des aperçus de tous les composables que vous allez créer dans cet atelier de programmation. Dans le cas présent, la méthode SearchBarPreview effectue un rendu de notre composable SearchBar, avec un arrière-plan et une marge intérieure qui lui donnent un peu plus de contexte. Avec l'implémentation que vous venez d'ajouter, vous devriez obtenir le résultat suivant :

aperçu de la barre de recherche

Il manque certaines choses. Commençons par corriger la taille du composable à l'aide de modificateurs.

Lorsque vous écrivez des composables, vous utilisez des modificateurs pour les opérations suivantes :

  • Modifier la taille, la mise en page, le comportement et l'apparence du composable.
  • Ajouter des informations, comme des libellés d'accessibilité.
  • Traiter les entrées utilisateur.
  • Ajouter des interactions de haut niveau (par exemple, faire en sorte que l'utilisateur puisse cliquer sur un élément, le faire défiler, le déplacer ou zoomer dessus).

Chaque composable que vous appelez possède un paramètre modifier que vous pouvez définir afin d'adapter son apparence et son comportement. Lorsque vous définissez le modificateur, vous pouvez enchaîner plusieurs méthodes de modification pour créer une adaptation plus complexe.

Dans ce cas, la barre de recherche doit avoir une hauteur d'au moins 56 dp et remplir la largeur de l'élément parent. Pour trouver les modificateurs appropriés, vous pouvez parcourir la liste des modificateurs et consulter la section Taille. Pour la hauteur, vous pouvez utiliser le modificateur heightIn. Vous avez ainsi la garantie que le composable a une hauteur minimale spécifique. Il peut toutefois être plus grand lorsque, par exemple, l'utilisateur augmente la taille de sa police système. Pour la largeur, vous pouvez utiliser le modificateur fillMaxWidth. Ce modificateur garantit que la barre de recherche utilise tout l'espace horizontal de l'élément parent.

Mettez à jour le modificateur pour qu'il corresponde au code ci-dessous :

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.material3.TextField

@Composable
fun SearchBar(
   modifier: Modifier = Modifier
) {
   TextField(
       value = "",
       onValueChange = {},
       modifier = modifier
           .fillMaxWidth()
           .heightIn(min = 56.dp)
   )
}

Dans le cas présent, l'ordre des modificateurs n'a pas d'importance, dans la mesure où l'un a une influence sur la largeur et l'autre, sur la hauteur.

Vous devez également définir certains paramètres de TextField. Essayez de faire en sorte que le composable ressemble à la conception en définissant les valeurs des paramètres. Voici à nouveau la conception à titre de référence :

barre de recherche

Pour mettre à jour votre implémentation, procédez comme suit :

  • Ajoutez l'icône de recherche. TextField contient un paramètre leadingIcon qui accepte un autre composable. À l'intérieur, vous pouvez définir un élément Icon qui, dans notre cas, devrait être Search. Veillez à utiliser l'importation Icon Compose appropriée.
  • Vous pouvez utiliser TextFieldDefaults.textFieldColors pour remplacer des couleurs spécifiques. Définissez les valeurs focusedContainerColor et unfocusedContainerColor du champ de texte sur la couleur surface de MaterialTheme.
  • Ajoutez un texte d'espace réservé "Search" (vous pouvez le trouver en tant que ressource de chaîne R.string.placeholder_search).

Une fois que vous avez terminé, votre composable doit se présenter comme suit :

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.ui.res.stringResource
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search

@Composable
fun SearchBar(
   modifier: Modifier = Modifier
) {
   TextField(
       value = "",
       onValueChange = {},
       leadingIcon = {
           Icon(
               imageVector = Icons.Default.Search,
               contentDescription = null
           )
       },
       colors = TextFieldDefaults.colors(
           unfocusedContainerColor = MaterialTheme.colorScheme.surface,
           focusedContainerColor = MaterialTheme.colorScheme.surface
       ),
       placeholder = {
           Text(stringResource(R.string.placeholder_search))
       },
       modifier = modifier
           .fillMaxWidth()
           .heightIn(min = 56.dp)
   )
}

barre de recherche

Remarque :

  • Vous avez ajouté un élément leadingIcon affichant l'icône de recherche. La description du contenu de cette icône n'est pas nécessaire, car l'espace réservé du champ de texte permet déjà d'en connaître la signification. N'oubliez pas qu'une description de contenu est généralement utilisée à des fins d'accessibilité et donne à l'utilisateur de votre application une représentation textuelle d'une image ou d'une icône.
  • Pour adapter la couleur d'arrière-plan du champ de texte, vous devez définir la propriété colors. Au lieu d'un paramètre distinct pour chaque couleur, le composable contient un paramètre combiné. Vous transmettez ici une copie de la classe de données TextFieldDefaults, dans laquelle vous ne mettez à jour que les couleurs différentes. Dans le cas présent, il ne s'agit que de la couleur unfocusedContainerColor et focusedContainerColor.

Au cours de cette étape, vous avez vu comment utiliser des paramètres et des modificateurs composables pour modifier l'apparence d'un composable. Cela s'applique aussi bien aux composables fournis par les bibliothèques Compose et Material qu'à ceux que vous écrivez vous-même. Vous devez toujours penser à fournir des paramètres pour personnaliser le composable que vous écrivez. Vous devez également ajouter une propriété modifier pour que l'apparence du composable puisse être adaptée depuis l'extérieur.

5. Align your body : alignement

Le prochain composable que vous allez implémenter est l'élément "Align your body". Observons son apparence, y compris la conception de révision (lignes rouges) affichée à côté :

composant "Align your body" (Aligner votre corps)

conception de révision "Align your body" (Aligner votre corps)

À présent, la conception de révision contient également des espacements orientés vers la ligne de base. Voici les informations que nous pouvons en tirer :

  • La hauteur de l'image doit être de 88 dp.
  • L'espacement entre la ligne de base du texte et l'image doit être de 24 dp.
  • L'espacement entre la ligne de base et le bas de l'élément doit être de 8 dp.
  • Le texte doit avoir le style typographique bodyMedium.

Pour implémenter ce composable, vous avez besoin d'un composable Image et d'un composable Text. Ils doivent être inclus dans un élément Column, de manière à être positionnés les uns sous les autres.

Recherchez le composable AlignYourBodyElement dans votre code et mettez à jour son contenu avec cette implémentation de base :

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.res.painterResource

@Composable
fun AlignYourBodyElement(
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
   ) {
       Image(
           painter = painterResource(R.drawable.ab1_inversions),
           contentDescription = null
       )
       Text(text = stringResource(R.string.ab1_inversions))
   }
}

Remarque :

  • Vous définissez la valeur contentDescription de l'image sur "null", car celle-ci est purement décorative. Le texte affiché sous l'image est suffisamment explicite. L'image n'a donc pas besoin d'une description supplémentaire.
  • Vous utilisez une image et un texte codés en dur. À l'étape suivante, vous allez les déplacer pour utiliser les paramètres fournis dans le composable AlignYourBodyElement afin de les rendre dynamiques.

Voici un aperçu de ce composable :

aperçu de "Align your body" (Aligner votre corps)

Des améliorations doivent y être apportées. On constate tout particulièrement que l'image est trop grande et qu'elle n'a pas la forme d'un cercle. Vous pouvez adapter le composable Image avec les modificateurs size et clip, et le paramètre contentScale.

Le modificateur size adapte le composable à une certaine taille, de la même manière que les modificateurs fillMaxWidth et heightIn que vous avez étudiés à l'étape précédente. Le modificateur clip fonctionne différemment et adapte l'apparence du composable. Vous pouvez le définir sur n'importe quelle Shape. Le contenu du composable est alors tronqué et adapté à cette forme.

import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip
@Composable
fun AlignYourBodyElement(
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
   ) {
       Image(
           painter = painterResource(R.drawable.ab1_inversions),
           contentDescription = null,
           modifier = Modifier
               .size(88.dp)
               .clip(CircleShape)
       )
       Text(text = stringResource(R.string.ab1_inversions))
   }
}

Actuellement, votre conception dans l'aperçu se présente comme suit :

aperçu de "Align your body" (Aligner votre corps)

L'image doit également être mise à l'échelle de manière correcte. Pour ce faire, vous pouvez utiliser le paramètre contentScale de Image. Plusieurs options s'offrent à vous. Voici les principales :

aperçu du contenu "Align your body" (Aligner votre corps)

Dans ce cas, le type de recadrage utilisé est correct. Après avoir appliqué les modificateurs et le paramètre, votre code devrait se présenter comme suit :

import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
@Composable
fun AlignYourBodyElement(
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
   ) {
       Image(
           painter = painterResource(R.drawable.ab1_inversions),
           contentDescription = null,
           contentScale = ContentScale.Crop,
           modifier = Modifier
               .size(88.dp)
               .clip(CircleShape)
       )
       Text( text = stringResource(R.string.ab1_inversions) )
   }
}

Votre conception devrait maintenant ressembler à ceci :

aperçu de "Align your body" (Aligner votre corps)

L'étape suivante consiste à aligner le texte horizontalement en définissant l'alignement de Column.

En règle générale, pour aligner des composables dans un conteneur parent, vous devez définir l'alignement de ce conteneur. Ainsi, au lieu de demander à l'élément enfant de se positionner dans son parent, vous indiquez au parent comment aligner ses enfants.

Pour Column, vous déterminez l'alignement horizontal des éléments enfants. Vous disposez des options suivantes :

  • Start
  • CenterHorizontally
  • End

Pour Row, vous définissez l'alignement vertical. Les options sont semblables à celles de Column :

  • Top
  • CenterVertically
  • Bottom

Pour Box, vous combinez les alignements horizontal et vertical. Vous disposez des options suivantes :

  • TopStart
  • TopCenter
  • TopEnd
  • CenterStart
  • Center
  • CenterEnd
  • BottomStart
  • BottomCenter
  • BottomEnd

Tous les éléments enfants du conteneur suivront ce même schéma d'alignement. Vous pouvez ignorer le comportement d'un seul élément enfant en y ajoutant un modificateur align.

Pour cette conception, le texte doit être centré horizontalement. Pour ce faire, définissez le horizontalAlignment de Column pour un centrage horizontal :

import androidx.compose.ui.Alignment
@Composable
fun AlignYourBodyElement(
   modifier: Modifier = Modifier
) {
   Column(
       horizontalAlignment = Alignment.CenterHorizontally,
       modifier = modifier
   ) {
       Image(
           //..
       )
       Text(
           //..
       )
   }
}

Une fois ces éléments implémentés, il ne vous reste plus qu'à apporter quelques petites modifications pour que le composable soit identique à la conception. Essayez de les implémenter vous-même ou reportez-vous au code final si vous rencontrez des difficultés. Effectuez les étapes suivantes :

  • Rendez l'image et le texte dynamiques. Transmettez-les en tant qu'arguments à la fonction composable. N'oubliez pas de mettre à jour l'aperçu correspondant et de transmettre certaines données codées en dur.
  • Mettez à jour le texte pour qu'il utilise le style typographique bodyMedium.
  • Mettez à jour les espacements de référence de l'élément de texte conformément au diagramme.

conception de révision "Align your body" (Aligner votre corps)

Une fois ces étapes terminées, votre code doit se présenter comme suit :

import androidx.compose.foundation.layout.paddingFromBaseline
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.ContentScale

@Composable
fun AlignYourBodyElement(
   @DrawableRes drawable: Int,
   @StringRes text: Int,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier,
       horizontalAlignment = Alignment.CenterHorizontally
   ) {
       Image(
           painter = painterResource(drawable),
           contentDescription = null,
           contentScale = ContentScale.Crop,
           modifier = Modifier
               .size(88.dp)
               .clip(CircleShape)
       )
       Text(
           text = stringResource(text),
           modifier = Modifier.paddingFromBaseline(top = 24.dp, bottom = 8.dp),
           style = MaterialTheme.typography.bodyMedium
       )
   }
}

@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun AlignYourBodyElementPreview() {
   MySootheTheme {
       AlignYourBodyElement(
           text = R.string.ab1_inversions,
           drawable = R.drawable.ab1_inversions,
           modifier = Modifier.padding(8.dp)
       )
   }
}

Observez l'élément AlignYourBodyElement dans l'onglet Design (Conception).

aperçu de "Align your body" (Aligner votre corps)

6. Fiche des collections préférées : composant Surface de Material

Le prochain composable à implémenter est, d'une certaine façon, semblable à l'élément "Align the body" (Aligner le corps). Voici la conception, avec la version annotée :

fiche "Favorite collections" (Collections préférées)

conception de révision de la fiche "Favorite collections" (Collections préférées)

Dans le cas présent, la taille réelle du composable est indiquée. Vous pouvez constater que le texte est titleMedium.

Ce conteneur utilise surfaceVariant comme couleur d'arrière-plan, qui est différente de la couleur d'arrière-plan de l'ensemble de l'écran. Il présente également des angles arrondis. Nous spécifions ces éléments pour la fiche de collection préférée à l'aide du composable Surface de Material.

Vous pouvez adapter Surface à vos besoins en définissant ses paramètres et son modificateur. Dans ce cas, les angles de la surface doivent être arrondis. Pour ce faire, vous pouvez utiliser le paramètre shape. Au lieu de définir la forme sur Shape, comme pour l'image de l'étape précédente, vous allez utiliser une valeur provenant de notre thème Material.

Voyons le résultat :

import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Surface

@Composable
fun FavoriteCollectionCard(
   modifier: Modifier = Modifier
) {
   Surface(
       shape = MaterialTheme.shapes.medium,
       modifier = modifier
   ) {
       Row {
           Image(
               painter = painterResource(R.drawable.fc2_nature_meditations),
               contentDescription = null
           )
           Text(text = stringResource(R.string.fc2_nature_meditations))
       }
   }
}

Et observons un aperçu de cette implémentation :

aperçu de la collection préférée

Mettez ensuite en pratique ce que vous avez appris à l'étape précédente.

  • Définissez la largeur de Row et alignez ses éléments enfants verticalement.
  • Définissez la taille de l'image en fonction du diagramme et recadrez-la dans son conteneur.

conception de révision de la collection préférée

Essayez d'implémenter ces modifications vous-même avant de regarder le code de la solution.

Votre code devrait maintenant se présenter comme suit :

import androidx.compose.foundation.layout.width

@Composable
fun FavoriteCollectionCard(
   modifier: Modifier = Modifier
) {
   Surface(
       shape = MaterialTheme.shapes.medium,
       modifier = modifier
   ) {
       Row(
           verticalAlignment = Alignment.CenterVertically,
           modifier = Modifier.width(255.dp)
       ) {
           Image(
               painter = painterResource(R.drawable.fc2_nature_meditations),
               contentDescription = null,
               contentScale = ContentScale.Crop,
               modifier = Modifier.size(80.dp)
           )
           Text(
               text = stringResource(R.string.fc2_nature_meditations)
           )
       }
   }
}

L'aperçu devrait maintenant ressembler à ceci :

aperçu de la collection préférée

Pour terminer ce composable, procédez comme suit :

  • Rendez l'image et le texte dynamiques. Transmettez-les en tant qu'arguments à la fonction composable.
  • Remplacez la couleur par "SurfaceVariant".
  • Mettez à jour le texte pour qu'il utilise le style typographique titleMedium.
  • Modifiez l'espacement entre l'image et le texte.

Vous devriez obtenir un résultat final semblable à ceci :

@Composable
fun FavoriteCollectionCard(
   @DrawableRes drawable: Int,
   @StringRes text: Int,
   modifier: Modifier = Modifier
) {
   Surface(
       shape = MaterialTheme.shapes.medium,
       color = MaterialTheme.colorScheme.surfaceVariant,
       modifier = modifier
   ) {
       Row(
           verticalAlignment = Alignment.CenterVertically,
           modifier = Modifier.width(255.dp)
       ) {
           Image(
               painter = painterResource(drawable),
               contentDescription = null,
               contentScale = ContentScale.Crop,
               modifier = Modifier.size(80.dp)
           )
           Text(
               text = stringResource(text),
               style = MaterialTheme.typography.titleMedium,
               modifier = Modifier.padding(horizontal = 16.dp)
           )
       }
   }
}

//..

@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun FavoriteCollectionCardPreview() {
   MySootheTheme {
       FavoriteCollectionCard(
           text = R.string.fc2_nature_meditations,
           drawable = R.drawable.fc2_nature_meditations,
           modifier = Modifier.padding(8.dp)
       )
   }
}

Consultez l'aperçu de FavoriteCollectionCardPreview.

aperçu de la collection préférée

7. Ligne "Align your body" : dispositions

Maintenant que vous avez créé les composables de base affichés à l'écran, vous pouvez commencer à créer les différentes sections.

Commencez par la ligne déroulante "Align your body".

ligne déroulante "Align you body" (Aligner votre corps)

Voici la conception de révision de ce composant :

conception de révision "Align your body" (Aligner votre corps)

Pour rappel, un bloc de la grille représente 8 dp. Dans cette conception, il y a donc un espace de 16 dp avant le premier élément et après le dernier élément de la ligne. Il y a 8 dp d'espacement entre chaque élément.

Dans Compose, vous pouvez implémenter une ligne à faire défiler comme ceci à l'aide du composable LazyRow. La documentation sur les listes contient beaucoup plus d'informations sur les listes différées telles que LazyRow et LazyColumn. Pour cet atelier de programmation, il suffit de savoir que LazyRow effectue uniquement le rendu des éléments affichés à l'écran, et non de tous les éléments en même temps. Cela contribue à préserver les performances de votre application.

Commencez par une implémentation de base de ce LazyRow :

import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items

@Composable
fun AlignYourBodyRow(
   modifier: Modifier = Modifier
) {
   LazyRow(
       modifier = modifier
   ) {
       items(alignYourBodyData) { item ->
           AlignYourBodyElement(item.drawable, item.text)
       }
   }
}

Comme vous pouvez le voir, les éléments enfants d'un LazyRow ne sont pas des composables. Au lieu de cela, vous utilisez la DSL de liste différée qui fournit des méthodes telles que item et items qui émettent des composables sous la forme d'éléments de liste. Pour chaque élément du alignYourBodyData fourni, vous émettez un composable AlignYourBodyElement que vous avez implémenté précédemment.

Voici comment cela se présente à l'écran :

aperçu de "Align your body" (Aligner votre corps)

Il manque toujours les espacements que nous avons vus dans la conception de révision. Pour les implémenter, vous devez vous renseigner sur les dispositions.

À l'étape précédente, vous avez découvert les alignements, lesquels servent à aligner les éléments enfants d'un conteneur sur l'axe transversal. Pour Column, l'axe transversal est l'axe horizontal, tandis que pour Row, il s'agit de l'axe vertical.

Toutefois, nous pouvons également déterminer le positionnement des composables enfants sur l'axe principal d'un conteneur (horizontalement pour Row, verticalement pour Column).

Pour Row, vous pouvez choisir les dispositions suivantes :

disposition des lignes

Et pour Column :

dispositions des colonnes

En plus de ces dispositions, vous pouvez utiliser la méthode Arrangement.spacedBy() pour ajouter un espace fixe entre chaque composable enfant.

Dans cet exemple, la méthode spacedBy est celle que vous devez utiliser, car vous souhaitez insérer un espacement de 8 dp entre chaque élément de LazyRow.

import androidx.compose.foundation.layout.Arrangement

@Composable
fun AlignYourBodyRow(
   modifier: Modifier = Modifier
) {
   LazyRow(
       horizontalArrangement = Arrangement.spacedBy(8.dp),
       modifier = modifier
   ) {
       items(alignYourBodyData) { item ->
           AlignYourBodyElement(item.drawable, item.text)
       }
   }
}

La conception se présente maintenant comme ceci :

aperçu de "Align your body" (Aligner votre corps)

Vous devez également ajouter une marge intérieure sur les côtés de LazyRow. Dans ce cas, l'ajout d'un simple modificateur de marge intérieure ne suffit pas. Essayez d'ajouter une marge intérieure à LazyRow et observez son comportement à l'aide de l'aperçu interactif :

conception de révision "Align your body" (Aligner votre corps)

Comme vous pouvez le voir, lors du défilement, les premier et dernier éléments visibles sont tronqués des deux côtés de l'écran.

Pour conserver la même marge intérieure, tout en faisant défiler votre contenu dans les limites de la liste parente sans la couper, toutes les listes fournissent un paramètre pour LazyRow appelé contentPadding et le configurent sur 16.dp.

import androidx.compose.foundation.layout.PaddingValues

@Composable
fun AlignYourBodyRow(
   modifier: Modifier = Modifier
) {
   LazyRow(
       horizontalArrangement = Arrangement.spacedBy(8.dp),
       contentPadding = PaddingValues(horizontal = 16.dp),
       modifier = modifier
   ) {
       items(alignYourBodyData) { item ->
           AlignYourBodyElement(item.drawable, item.text)
       }
   }
}

Essayez l'aperçu interactif pour voir la différence apportée par la marge intérieure.

ligne déroulante "Align you body" (Aligner votre corps)

8. Grille de collections préférées : grilles différées

La prochaine section à implémenter est la partie "Favorite collections" (Collections préférées) de l'écran. Ce composable a besoin d'une grille et non d'une seule ligne :

défilement des collections préférées

Vous pouvez implémenter cette section de la même manière que dans la section précédente, en créant LazyRow et en laissant chaque élément contenir Column avec deux instances FavoriteCollectionCard. Cependant, au cours de cette étape, vous allez utiliser la fonction LazyHorizontalGrid qui permet de mieux mapper les éléments aux éléments de la grille.

Commencez par une implémentation simple de la grille avec deux lignes fixes :

import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.items

@Composable
fun FavoriteCollectionsGrid(
   modifier: Modifier = Modifier
) {
   LazyHorizontalGrid(
       rows = GridCells.Fixed(2),
       modifier = modifier
   ) {
       items(favoriteCollectionsData) { item ->
           FavoriteCollectionCard(item.drawable, item.text)
       }
   }
}

Comme vous pouvez le voir, vous avez simplement remplacé le composable LazyRow de l'étape précédente par LazyHorizontalGrid. Cependant, vous n'obtiendrez pas encore le résultat correct :

aperçu des collections préférées

La grille occupe autant d'espace que son élément parent, ce qui signifie que les fiches des collections préférées sont beaucoup trop étirées verticalement.

Adaptez le composable pour respecter les conditions suivantes :

  • Le contentPadding horizontal de la grille est de 16 dp.
  • La disposition horizontale et verticale est espacée de 16 dp.
  • La hauteur de la grille est de 168 dp.
  • Le modificateur de FavoriteCollectionCard spécifie une hauteur de 80 dp.

Le nouveau code doit ressembler à ceci :

@Composable
fun FavoriteCollectionsGrid(
   modifier: Modifier = Modifier
) {
   LazyHorizontalGrid(
       rows = GridCells.Fixed(2),
       contentPadding = PaddingValues(horizontal = 16.dp),
       horizontalArrangement = Arrangement.spacedBy(16.dp),
       verticalArrangement = Arrangement.spacedBy(16.dp),
       modifier = modifier.height(168.dp)
   ) {
       items(favoriteCollectionsData) { item ->
           FavoriteCollectionCard(item.drawable, item.text, Modifier.height(80.dp))
       }
   }
}

L'aperçu doit se présenter comme suit :

aperçu des collections préférées

9. Section d'accueil : API d'emplacement

L'écran d'accueil de MySoothe comporte plusieurs sections qui suivent le même schéma. Elles ont toutes un titre, ainsi que du contenu qui varie en fonction de la section. Voici la conception de révision que nous souhaitons implémenter :

conception de révision de la section d'accueil

Comme vous pouvez le voir, chaque section comporte un titre et un emplacement. Le titre est associé à des informations sur le style et l'espacement. L'emplacement peut être rempli de manière dynamique avec un contenu différent en fonction de la section.

Pour implémenter ce conteneur de section flexible, vous utilisez ce que l'on appelle des API d'emplacement. Avant l'implémentation, lisez la section traitant des mises en page basées sur les emplacements sur la page de documentation. Vous comprendrez ainsi ce qu'est une mise en page basée sur les emplacements et comment utiliser les API d'emplacement pour créer une mise en page de ce type.

Adaptez le composable HomeSection pour qu'il puisse recevoir le titre et le contenu de l'emplacement. Vous devez également adapter l'aperçu associé pour appeler ce composable HomeSection avec le titre et le contenu "Align your body" :

@Composable
fun HomeSection(
   @StringRes title: Int,
   modifier: Modifier = Modifier,
   content: @Composable () -> Unit
) {
   Column(modifier) {
       Text(stringResource(title))
       content()
   }
}

@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE)
@Composable
fun HomeSectionPreview() {
   MySootheTheme {
       HomeSection(R.string.align_your_body) {
           AlignYourBodyRow()
       }
   }
}

Vous pouvez utiliser le paramètre content pour l'emplacement du composable. Ainsi, lorsque vous utilisez le composable HomeSection, vous pouvez utiliser un lambda de fin pour remplir l'emplacement du contenu. Lorsqu'un composable fournit plusieurs emplacements à remplir, vous pouvez leur attribuer des noms explicites qui représentent leur fonction dans le conteneur de composables de plus grande taille. Par exemple, TopAppBar de Material fournit les emplacements pour title, navigationIcon et actions.

Voyons à quoi ressemble la section avec cette implémentation :

aperçu de la section d'accueil

Le composable Text a besoin d'informations supplémentaires pour être aligné sur la conception.

conception de révision de la section d'accueil

Modifiez-le pour qu'il réponde aux critères suivants :

  • Il utilise la typographie titleMedium.
  • L'espacement entre la ligne de base du texte et le haut de l'élément est de 40 dp.
  • L'espacement entre la ligne de base et le bas de l'élément est de 16 dp.
  • La marge intérieure horizontale est de 16 dp.

Une fois terminée, votre solution doit se présenter comme suit :

@Composable
fun HomeSection(
   @StringRes title: Int,
   modifier: Modifier = Modifier,
   content: @Composable () -> Unit
) {
   Column(modifier) {
       Text(
           text = stringResource(title),
           style = MaterialTheme.typography.titleMedium,
           modifier = Modifier
               .paddingFromBaseline(top = 40.dp, bottom = 16.dp)
               .padding(horizontal = 16.dp)
       )
       content()
   }
}

10. Écran d'accueil : défilement

Maintenant que vous avez créé tous les composants principaux, vous pouvez les combiner dans une implémentation en plein écran.

Voici la conception que vous essayez d'implémenter :

conception de révision de la section d'accueil

Nous plaçons simplement la barre de recherche et les deux sections l'une en dessous de l'autre. Vous devez ajouter un espacement pour que tout soit adapté à la conception. Spacer est un composable que nous n'avons jamais utilisé auparavant. Il nous permet d'ajouter de l'espace dans notre élément Column. Si, au lieu de cela, vous définissez la marge intérieure de Column, vous obtiendrez le même comportement de "coupure" que celui observé précédemment dans la grille des collections préférées.

@Composable
fun HomeScreen(modifier: Modifier = Modifier) {
   Column(modifier) {
       Spacer(Modifier.height(16.dp))
       SearchBar(Modifier.padding(horizontal = 16.dp))
       HomeSection(title = R.string.align_your_body) {
           AlignYourBodyRow()
       }
       HomeSection(title = R.string.favorite_collections) {
           FavoriteCollectionsGrid()
       }
       Spacer(Modifier.height(16.dp))
   }
}

Bien que la conception s'adapte bien à la plupart des tailles d'appareil, il doit être possible de la faire défiler verticalement si l'écran n'est pas assez grand (en mode Paysage, par exemple). Pour cela, vous devez ajouter le comportement de défilement.

Comme nous l'avons vu précédemment, les mises en page différées (Lazy), telles que LazyRow et LazyHorizontalGrid, ajoutent automatiquement le comportement de défilement. Cependant, vous n'avez pas toujours besoin d'une mise en page de ce type. En règle générale, vous utilisez une mise en page différée lorsqu'il y a de nombreux éléments dans une liste ou de grands ensembles de données à charger. De ce fait, l'émission simultanée de tous les éléments aurait un impact sur les performances et ralentirait votre application. Lorsqu'une liste ne contient qu'un nombre limité d'éléments, vous pouvez choisir d'utiliser un élément Column ou Row simple, puis d'ajouter le comportement de défilement manuellement. Pour ce faire, utilisez les modificateurs verticalScroll ou horizontalScroll. Ils nécessitent un élément ScrollState, qui contient l'état actuel du défilement et qui est utilisé pour modifier l'état depuis l'extérieur. Dans le cas présent, vous ne cherchez pas à modifier l'état de défilement. Vous allez donc simplement créer une instance ScrollState persistante à l'aide de rememberScrollState.

Le résultat final doit se présenter comme suit :

import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll

@Composable
fun HomeScreen(modifier: Modifier = Modifier) {
   Column(
       modifier
           .verticalScroll(rememberScrollState())
   ) {
       Spacer(Modifier.height(16.dp))
       SearchBar(Modifier.padding(horizontal = 16.dp))
       HomeSection(title = R.string.align_your_body) {
           AlignYourBodyRow()
       }
       HomeSection(title = R.string.favorite_collections) {
           FavoriteCollectionsGrid()
       }
       Spacer(Modifier.height(16.dp))
   }
}

Pour vérifier le comportement de défilement du composable, limitez la hauteur de l'aperçu et exécutez-le dans l'aperçu interactif :

@Preview(showBackground = true, backgroundColor = 0xFFF5F0EE, heightDp = 180)
@Composable
fun ScreenContentPreview() {
   MySootheTheme { HomeScreen() }
}

défilement du contenu à l'écran

11. Navigation inférieure : Material

Maintenant que vous avez implémenté le contenu de l'écran, vous êtes prêt à ajouter la décoration de la fenêtre. Dans le cas de MySoothe, une barre de navigation permet à l'utilisateur de basculer entre différents écrans.

Commencez par implémenter le composable de barre de navigation, puis incluez-le dans votre application.

Observons la conception :

conception de la barre de navigation inférieure

Heureusement, vous n'avez pas à implémenter vous-même l'intégralité de ce composable en partant de zéro. Vous pouvez utiliser le composable NavigationBar qui fait partie de la bibliothèque Material de Compose. Dans le composable NavigationBar, vous pouvez ajouter un ou plusieurs éléments NavigationBarItem auxquels un style sera automatiquement appliqué par la bibliothèque Material.

Commencez par une implémentation de base de cette navigation inférieure :

import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Spa

@Composable
private fun SootheBottomNavigation(modifier: Modifier = Modifier) {
   NavigationBar(
       modifier = modifier
   ) {
       NavigationBarItem(
           icon = {
               Icon(
                   imageVector = Icons.Default.Spa,
                   contentDescription = null
               )
           },
           label = {
               Text(
                   text = stringResource(R.string.bottom_navigation_home)
               )
           },
           selected = true,
           onClick = {}
       )
       NavigationBarItem(
           icon = {
               Icon(
                   imageVector = Icons.Default.AccountCircle,
                   contentDescription = null
               )
           },
           label = {
               Text(
                   text = stringResource(R.string.bottom_navigation_profile)
               )
           },
           selected = false,
           onClick = {}
       )
   }
}

Voici à quoi ressemble l'implémentation de base. Il n'y a pas beaucoup de contraste entre la couleur du contenu et celle de la barre de navigation.

aperçu de la barre de navigation inférieure

Vous devez effectuer quelques adaptations stylistiques. Tout d'abord, vous pouvez modifier la couleur d'arrière-plan de la barre de navigation inférieure en définissant son paramètre containerColor. Pour cela, vous pouvez utiliser la couleur surfaceVariant du thème Material. Une fois terminée, votre solution doit se présenter comme suit :

@Composable
private fun SootheBottomNavigation(modifier: Modifier = Modifier) {
   NavigationBar(
       containerColor = MaterialTheme.colorScheme.surfaceVariant,
       modifier = modifier
   ) {
       NavigationBarItem(
           icon = {
               Icon(
                   imageVector = Icons.Default.Spa,
                   contentDescription = null
               )
           },
           label = {
               Text(stringResource(R.string.bottom_navigation_home))
           },
           selected = true,
           onClick = {}
       )
       NavigationBarItem(
           icon = {
               Icon(
                   imageVector = Icons.Default.AccountCircle,
                   contentDescription = null
               )
           },
           label = {
               Text(stringResource(R.string.bottom_navigation_profile))
           },
           selected = false,
           onClick = {}
       )
   }
}

La barre de navigation devrait maintenant se présenter comme suit. Notez qu'elle offre un contraste plus élevé.

conception de la barre de navigation inférieure

12. Application MySoothe : échafaudage (scaffolding)

Au cours de cette étape, vous allez créer l'implémentation plein écran, y compris la barre de navigation inférieure. Utilisez le composable Scaffold de Material Design. Scaffold fournit un composable configurable de niveau supérieur pour les applications qui implémentent Material Design. Il contient des emplacements pour différents concepts Material, dont l'un est la barre inférieure. Dans cette barre inférieure, vous pouvez placer le composable de navigation inférieure que vous avez créé à l'étape précédente.

Implémentez le composable MySootheAppPortrait(). Il s'agit du composable de niveau supérieur de votre application. Vous devez donc effectuer les opérations suivantes :

  • Appliquer le thème Material MySootheTheme.
  • Ajouter Scaffold.
  • Définir la barre inférieure comme étant votre composable SootheBottomNavigation.
  • Définir le contenu comme étant votre composable HomeScreen.

Vous devriez obtenir le résultat suivant :

import androidx.compose.material3.Scaffold

@Composable
fun MySootheAppPortrait() {
   MySootheTheme {
       Scaffold(
           bottomBar = { SootheBottomNavigation() }
       ) { padding ->
           HomeScreen(Modifier.padding(padding))
       }
   }
}

Votre implémentation est maintenant terminée. Pour vérifier que votre version a été implémentée au pixel près, vous pouvez comparer cette image à votre propre implémentation d'aperçu.

implémentation my soothe

13. Rail de navigation - Material

Lorsque vous créez des mises en page pour des applications, vous devez également faire attention à son apparence dans plusieurs configurations, y compris en mode Paysage sur votre téléphone. Voici la conception de l'application en mode Paysage. Notez que la barre de navigation inférieure se transforme en rail à gauche du contenu de l'écran.

conception Paysage

Pour l'implémenter, vous allez utiliser le composable NavigationRail, qui fait partie de la bibliothèque Compose Material, et dont l'implémentation est semblable à celle de l'élément NavigationBar, qui a été utilisé pour créer la barre de navigation inférieure. Dans le composable NavigationRail, vous ajouterez des éléments NavigationRailItem pour Home (Accueil) et Profile (Profil).

conception de la barre de navigation inférieure

Commençons par l'implémentation de base d'un rail de navigation.

import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem

@Composable
private fun SootheNavigationRail(modifier: Modifier = Modifier) {
   NavigationRail(
   ) {
       Column(
       ) {
           NavigationRailItem(
               icon = {
                   Icon(
                       imageVector = Icons.Default.Spa,
                       contentDescription = null
                   )
               },
               label = {
                   Text(stringResource(R.string.bottom_navigation_home))
               },
               selected = true,
               onClick = {}
           )

           NavigationRailItem(
               icon = {
                   Icon(
                       imageVector = Icons.Default.AccountCircle,
                       contentDescription = null
                   )
               },
               label = {
                   Text(stringResource(R.string.bottom_navigation_profile))
               },
               selected = false,
               onClick = {}
           )
       }
   }
}

aperçu du rail de navigation

Vous devez effectuer quelques adaptations stylistiques.

  • Ajoutez une marge intérieure de 8 dp au début et à la fin du rail.
  • Modifiez la couleur d'arrière-plan du rail de navigation en définissant son paramètre containerColor sur la couleur d'arrière-plan du thème Material. Lorsque vous définissez la couleur de l'arrière-plan, la couleur des icônes et du texte s'adapte automatiquement à la couleur onBackground du thème.
  • La colonne doit remplir la hauteur maximale.
  • Définissez la disposition verticale de la colonne au centre.
  • Définissez l'alignement horizontal de la colonne pour la centrer horizontalement.
  • Ajoutez une marge intérieure de 8 dp entre les deux icônes.

Une fois terminée, votre solution doit se présenter comme suit :

import androidx.compose.foundation.layout.fillMaxHeight

@Composable
private fun SootheNavigationRail(modifier: Modifier = Modifier) {
   NavigationRail(
       modifier = modifier.padding(start = 8.dp, end = 8.dp),
       containerColor = MaterialTheme.colorScheme.background,
   ) {
       Column(
           modifier = modifier.fillMaxHeight(),
           verticalArrangement = Arrangement.Center,
           horizontalAlignment = Alignment.CenterHorizontally
       ) {
           NavigationRailItem(
               icon = {
                   Icon(
                       imageVector = Icons.Default.Spa,
                       contentDescription = null
                   )
               },
               label = {
                   Text(stringResource(R.string.bottom_navigation_home))
               },
               selected = true,
               onClick = {}
           )
           Spacer(modifier = Modifier.height(8.dp))
           NavigationRailItem(
               icon = {
                   Icon(
                       imageVector = Icons.Default.AccountCircle,
                       contentDescription = null
                   )
               },
               label = {
                   Text(stringResource(R.string.bottom_navigation_profile))
               },
               selected = false,
               onClick = {}
           )
       }
   }
}

conception du rail de navigation

Ajoutons maintenant le rail de navigation à la conception Paysage.

conception Paysage

Pour la version Portrait de l'application, vous avez utilisé un échafaudage (Scaffold). Toutefois, pour le mode paysage, vous allez utiliser une ligne et placer le rail de navigation et le contenu de l'écran l'un à côté de l'autre.

@Composable
fun MySootheAppLandscape() {
   MySootheTheme {
       Row {
           SootheNavigationRail()
           HomeScreen()
       }
   }
}

Lorsque vous avez utilisé un échafaudage en mode portrait, la couleur d'arrière-plan a également été définie pour le contenu. Pour définir la couleur du rail de navigation, encapsulez la ligne dans une surface et attribuez-lui la couleur d'arrière-plan.

@Composable
fun MySootheAppLandscape() {
   MySootheTheme {
       Surface(color = MaterialTheme.colorScheme.background) {
           Row {
               SootheNavigationRail()
               HomeScreen()
           }
       }
   }
}

aperçu en mode Paysage

14. Application MySoothe : taille de la fenêtre

L'aperçu du mode Paysage est optimal. Toutefois, si vous exécutez l'application sur un appareil ou dans un émulateur et que vous la tournez sur le côté, la version Paysage ne s'affiche pas. En effet, nous devons indiquer à l'application quand afficher quelle configuration de l'application. Pour ce faire, utilisez la fonction calculateWindowSizeClass() pour voir dans quelle configuration se trouve le téléphone.

diagramme de taille de fenêtre

Il existe trois largeurs de classe de taille de fenêtre : compacte, moyenne et grande. Lorsque l'application est en mode Portrait, sa largeur est Compacte, alors qu'elle est Grande en mode Paysage. Pour les besoins de cet atelier de programmation, vous n'utiliserez pas la largeur Moyenne.

Dans le composable MySootheApp, mettez-le à jour pour qu'il intègre la classe WindowSizeClass de l'appareil. Si la largeur est Compacte, transmettez la version Portrait de l'application. Si elle Grande, transmettez la version Paysage de l'application.

import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
@Composable
fun MySootheApp(windowSize: WindowSizeClass) {
   when (windowSize.widthSizeClass) {
       WindowWidthSizeClass.Compact -> {
           MySootheAppPortrait()
       }
       WindowWidthSizeClass.Expanded -> {
           MySootheAppLandscape()
       }
   }
}

Dans setContent(), créez une valeur appelée windowSizeClass définie sur calculateWindowSize() et transmettez-la à MySootheApp().

calculateWindowSize() étant encore en phase expérimentale, vous devez activer la classe ExperimentalMaterial3WindowSizeClassApi.

import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass

class MainActivity : ComponentActivity() {
   @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           val windowSizeClass = calculateWindowSizeClass(this)
           MySootheApp(windowSizeClass)
       }
   }
}

Exécutez maintenant l'application dans votre émulateur ou sur votre appareil et observez l'évolution de l'affichage en cas de rotation.

Version portrait de l'application

Version paysage de l'application

15. Félicitations

Félicitations, vous avez terminé cet atelier de programmation au cours duquel vous avez découvert les mises en page dans Compose. En implémentant une conception réelle, vous avez découvert les modificateurs, les alignements, les dispositions, les mises en page différées (Lazy), les API d'emplacement, le défilement, les composants Material et les conceptions propres à la mise en page.

Consultez les autres ateliers de programmation du parcours Compose. Consultez également les exemples de code.

Documentation

Pour en savoir plus et obtenir des conseils à ce sujet, consultez la documentation suivante :