Mises en page de flux dans Compose

FlowRow et FlowColumn sont des composables semblables à Row et Column, mais qui diffèrent par le fait que circulent dans la ligne suivante lorsque le conteneur manque d'espace. Cela crée plusieurs lignes ou colonnes. Vous pouvez aussi contrôler le nombre d'éléments en définissant maxItemsInEachRow ou maxItemsInEachColumn. Vous pouvez souvent utiliser FlowRow et FlowColumn pour créer des mises en page responsives (le contenu ne sera pas coupé) désactivé si les éléments sont trop volumineux pour une seule dimension, et l'utilisation d'une combinaison maxItemsInEach* avec Modifier.weight(weight) peut vous aider à créer des mises en page remplir/augmenter la largeur d’une ligne ou d’une colonne si nécessaire.

Voici un exemple typique pour un chip ou une UI de filtrage:

5 chips dans un FlowRow, montrant le dépassement sur la ligne suivante en l'absence de réponse
plus d'espace disponible.
Figure 1. Exemple de FlowRow

Utilisation de base

Pour utiliser FlowRow ou FlowColumn, créez ces composables et placez les éléments qui doit suivre le flux standard:

@Composable
private fun FlowRowSimpleUsageExample() {
    FlowRow(modifier = Modifier.padding(8.dp)) {
        ChipItem("Price: High to Low")
        ChipItem("Avg rating: 4+")
        ChipItem("Free breakfast")
        ChipItem("Free cancellation")
        ChipItem("£50 pn")
    }
}

Cet extrait permet d'obtenir l'interface utilisateur ci-dessus, où les éléments sont automatiquement acheminés vers la ligne suivante lorsqu'il n'y a plus d'espace dans la première ligne.

Caractéristiques de la mise en page de flux

Les mises en page de flux comportent les fonctionnalités et propriétés suivantes, que vous pouvez utiliser pour : créer différentes mises en page dans votre application.

Disposition de l'axe principal: disposition horizontale ou verticale

L'axe principal est l'axe sur lequel les articles sont disposés (par exemple, dans FlowRow, les éléments sont disposés horizontalement). horizontalArrangement dans FlowRow contrôle la façon dont l'espace libre est réparti entre les éléments.

Le tableau suivant présente des exemples de définition de horizontalArrangement sur des éléments pour FlowRow:

Disposition horizontale définie sur FlowRow

Résultat

Arrangement.Start (Default)

Articles organisés par début

Arrangement.SpaceBetween

Organisation des éléments avec un espace intermédiaire

Arrangement.Center

Éléments disposés au centre

Arrangement.End

Articles classés à la fin

Arrangement.SpaceAround

Éléments disposés avec un espace autour d’eux

Arrangement.spacedBy(8.dp)

Éléments espacés d'une certaine valeur

Pour FlowColumn, des options similaires sont disponibles avec verticalArrangement, avec la valeur par défaut de Arrangement.Top.

Disposition des axes transversaux

L'axe transversal est l'axe opposé à l'axe principal. Pour exemple, dans FlowRow, il s'agit de l'axe vertical. Pour modifier la façon dont les contenus à l'intérieur du conteneur sont disposés sur l'axe transversal, verticalArrangement pour FlowRow et horizontalArrangement pour FlowColumn

Pour FlowRow, le tableau suivant montre des exemples de définition verticalArrangement sur les articles:

Disposition verticale définie sur FlowRow

Résultat

Arrangement.Top (Default)

Disposition du haut du conteneur

Arrangement.Bottom

Disposition du fond du conteneur

Arrangement.Center

Aménagement de centres de conteneurs

Pour FlowColumn, des options similaires sont disponibles avec horizontalArrangement. La disposition par défaut de l'axe transversal est Arrangement.Start.

Alignement de l'élément individuel

Vous pouvez placer des éléments individuels sur la ligne avec différentes alignements. Ceci est différent de verticalArrangement et horizontalArrangement, car il aligne les éléments par rapport à la ligne actuelle. Vous pouvez appliquez ceci avec Modifier.align().

Par exemple, lorsque les éléments d'une FlowRow ont des hauteurs différentes, la ligne prend le hauteur de l'article le plus gros et applique Modifier.align(alignmentOption) à la éléments:

Alignement vertical défini sur FlowRow

Résultat

Alignment.Top (Default)

Éléments alignés en haut

Alignment.Bottom

Éléments alignés en bas

Alignment.CenterVertically

Éléments alignés au centre

Des options similaires sont disponibles pour FlowColumn. L'alignement par défaut est Alignment.Start

Nombre maximal d'éléments dans la ligne ou la colonne

Les paramètres maxItemsInEachRow ou maxItemsInEachColumn définissent la valeur des éléments sur l'axe principal pour permettre sur une ligne avant de passer à la suivante. La la valeur par défaut est Int.MAX_INT, ce qui autorise autant d'éléments que possible leurs tailles leur permettent de s’adapter à la ligne.

Par exemple, définir un maxItemsInEachRow oblige la mise en page initiale à n'utiliser que contient 3 articles:

Aucune valeur maximale définie

maxItemsInEachRow = 3

Aucune valeur maximale définie sur la ligne du flux Nombre maximal d'éléments défini sur la ligne du flux

Éléments du flux de chargement différé

ContextualFlowRow et ContextualFlowColumn sont des composants Versions de FlowRow et FlowColumn permettant de charger le contenu en différé de la ligne ou de la colonne de votre flux. Ils fournissent également des informations sur la position des éléments (index, numéro de ligne et taille disponible), par exemple si l'élément est dans le premier ligne. Cela est utile pour les grands ensembles de données et si vous avez besoin d'informations contextuelles sur un article.

Le paramètre maxLines limite le nombre de lignes affichées et le paramètre overflow spécifie ce qui doit s'afficher lorsqu'un dépassement d'éléments est vous permettant de spécifier un expandIndicator personnalisé collapseIndicator

Par exemple, pour afficher un signe "+ (nombre d'éléments restants)" ou "Afficher moins" bouton:

val totalCount = 40
var maxLines by remember {
    mutableStateOf(2)
}

val moreOrCollapseIndicator = @Composable { scope: ContextualFlowRowOverflowScope ->
    val remainingItems = totalCount - scope.shownItemCount
    ChipItem(if (remainingItems == 0) "Less" else "+$remainingItems", onClick = {
        if (remainingItems == 0) {
            maxLines = 2
        } else {
            maxLines += 5
        }
    })
}
ContextualFlowRow(
    modifier = Modifier
        .safeDrawingPadding()
        .fillMaxWidth(1f)
        .padding(16.dp)
        .wrapContentHeight(align = Alignment.Top)
        .verticalScroll(rememberScrollState()),
    verticalArrangement = Arrangement.spacedBy(4.dp),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    maxLines = maxLines,
    overflow = ContextualFlowRowOverflow.expandOrCollapseIndicator(
        minRowsToShowCollapse = 4,
        expandIndicator = moreOrCollapseIndicator,
        collapseIndicator = moreOrCollapseIndicator
    ),
    itemCount = totalCount
) { index ->
    ChipItem("Item $index")
}

Exemple de lignes de flux contextuels.
Figure 2. Exemple de ContextualFlowRow

Poids des articles

Le poids augmente un élément en fonction de son facteur et de l'espace disponible sur la ligne qu'il a été placé. Notez qu'il existe une différence entre FlowRow et Row. avec la façon dont les pondérations sont utilisées pour calculer la largeur d'un article. Pour Rows, poids est basé sur tous les éléments de Row. Avec FlowRow, la pondération est basée sur le les éléments de campagne dans lesquels un élément est placé, et non tous les éléments FlowRow.

Par exemple, si vous avez quatre articles sur une ligne, chacun avec une de 1f, 2f, 1f et de 3f, la pondération totale est de 7f. L'espace restant d'une ligne ou d'une colonne sera divisé par 7f. Ensuite, chaque largeur d'élément sera calculé à l'aide de: weight * (remainingSpace / totalWeight).

Vous pouvez combiner Modifier.weight et nombre maximal d'éléments avec FlowRow ou FlowColumn pour créer une mise en page sous forme de grille. Cette approche est utile pour créer des mises en page réactives qui s'adaptent au dimensionnement de votre appareil.

Il existe plusieurs exemples de ce que vous pouvez obtenir avec les pondérations. Un L'exemple est une grille dans laquelle les éléments sont de taille égale, comme indiqué ci-dessous:

Grille créée avec la ligne de flux
Figure 3. Utiliser FlowRow pour créer une grille

Pour créer une grille de tailles d'articles égales, vous pouvez procéder comme suit:

val rows = 3
val columns = 3
FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = rows
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .weight(1f)
        .clip(RoundedCornerShape(8.dp))
        .background(MaterialColors.Blue200)
    repeat(rows * columns) {
        Spacer(modifier = itemModifier)
    }
}

Il est important de noter que si vous ajoutez un autre élément et que vous le répétez 10 fois au lieu de 9, dernier élément occupe toute la dernière colonne, car la pondération totale pour l'ensemble de la ligne est 1f:

Dernier élément en taille réelle sur la grille
Figure 4. Utilisation de FlowRow pour créer une grille dont le dernier élément occupe toute la largeur

Vous pouvez combiner les pondérations avec d'autres Modifiers, comme Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) ou Modifier.fillMaxWidth(fraction) Ces modificateurs fonctionnent tous conjointement avec permettent un dimensionnement responsif des éléments dans un FlowRow (ou FlowColumn).

Vous pouvez également créer une grille alternée de différentes tailles d'éléments, où deux éléments occupent chacun la moitié de la largeur chacun, et un élément occupe toute la largeur de l’élément suivant colonne:

Grille alternée avec ligne de flux
Figure 5 : FlowRow avec des tailles de lignes alternées

Pour ce faire, utilisez le code suivant:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 2
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .clip(RoundedCornerShape(8.dp))
        .background(Color.Blue)
    repeat(6) { item ->
        // if the item is the third item, don't use weight modifier, but rather fillMaxWidth
        if ((item + 1) % 3 == 0) {
            Spacer(modifier = itemModifier.fillMaxWidth())
        } else {
            Spacer(modifier = itemModifier.weight(0.5f))
        }
    }
}

Dimensionnement fractionnaire

Avec Modifier.fillMaxWidth(fraction), vous pouvez spécifier la taille du conteneur qu'un élément doit occuper. Ceci est différent de la façon dont Modifier.fillMaxWidth(fraction) fonctionne lorsqu'il est appliqué à Row ou Column, dans que les éléments de type Row/Column occupent un pourcentage de la largeur restante toute la largeur du conteneur.

Par exemple, le code suivant produit des résultats différents lorsque vous utilisez FlowRow. par rapport à Row:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 3
) {
    val itemModifier = Modifier
        .clip(RoundedCornerShape(8.dp))
    Box(modifier = itemModifier.height(200.dp).width(60.dp).background(Color.Red))
    Box(modifier = itemModifier.height(200.dp).fillMaxWidth(0.7f).background(Color.Blue))
    Box(modifier = itemModifier.height(200.dp).weight(1f).background(Color.Magenta))
}

FlowRow: élément du milieu avec 0,7 fraction de la largeur totale du conteneur.

Largeur fractionnaire avec ligne de flux

Row: élément du milieu occupant 0,7 % de la largeur Row restante.

Largeur fractionnaire avec ligne

fillMaxColumnWidth() et fillMaxRowHeight()

Application de Modifier.fillMaxColumnWidth() ou Modifier.fillMaxRowHeight() vers un élément situé dans FlowColumn ou FlowRow permet de s'assurer que les éléments d'une même colonne ou ligne occupent la même largeur ou la même hauteur que l'élément le plus grand de la colonne/ligne.

Par exemple, cet exemple utilise FlowColumn pour afficher la liste des desserts. Vous pouvez voir la différence entre la largeur de chaque élément lorsque Modifier.fillMaxColumnWidth() est appliqué aux éléments, contrairement aux autres les éléments encapsulent.

FlowColumn(
    Modifier
        .padding(20.dp)
        .fillMaxHeight()
        .fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp),
    maxItemsInEachColumn = 5,
) {
    repeat(listDesserts.size) {
        Box(
            Modifier
                .fillMaxColumnWidth()
                .border(1.dp, Color.DarkGray, RoundedCornerShape(8.dp))
                .padding(8.dp)
        ) {

            Text(
                text = listDesserts[it],
                fontSize = 18.sp,
                modifier = Modifier.padding(3.dp)
            )
        }
    }
}

Modifier.fillMaxColumnWidth() appliqué à chaque élément

FillMaxColumnWidth

Aucune modification de largeur définie (encapsulation d'éléments)

Aucune largeur de colonne maximale de remplissage n'est définie