Layout di flusso in Compose

FlowRow e FlowColumn sono componibili simili a Row e Column, ma si differenziano per il fatto che gli elementi vengono spostati nella riga successiva quando lo spazio nel container si esaurisce. In questo modo vengono create più righe o colonne. Il numero di elementi in una riga può essere controllato anche impostando maxItemsInEachRow o maxItemsInEachColumn. Spesso puoi utilizzare FlowRow e FlowColumn per creare layout adattabili: i contenuti non verranno tagliati se gli elementi sono troppo grandi per una dimensione e l'utilizzo di una combinazione di maxItemsInEach* con Modifier.weight(weight) può aiutarti a creare layout che riempiono/espandono la larghezza di una riga o di una colonna quando necessario.

L'esempio tipico è quello di un chip o di un'interfaccia utente di filtro:

5 chip in un FlowRow, che mostrano l'overflow alla riga successiva quando non c'è più spazio disponibile.
Figura 1. Esempio di FlowRow

Utilizzo di base

Per utilizzare FlowRow o FlowColumn, crea questi elementi componibili e inserisci al loro interno gli elementi che devono seguire il flusso 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")
    }
}

Questo snippet genera l'interfaccia utente mostrata sopra, con gli elementi che passano automaticamente alla riga successiva quando non c'è più spazio nella prima riga.

Funzionalità del layout dinamico

I layout dinamici hanno le seguenti funzionalità e proprietà che puoi utilizzare per creare layout diversi nella tua app.

Disposizione dell'asse principale: disposizione orizzontale o verticale

L'asse principale è l'asse su cui sono disposti gli elementi (ad esempio, in FlowRow, gli elementi sono disposti in orizzontale). Il parametro horizontalArrangement in FlowRow controlla il modo in cui lo spazio libero viene distribuito tra gli elementi.

La seguente tabella mostra esempi di impostazione di horizontalArrangement sugli elementi per FlowRow:

Disposizione orizzontale impostata su FlowRow

Risultato

Arrangement.Start (Default)

Elementi disposti con inizio

Arrangement.SpaceBetween

Disposizione degli elementi con spazio tra loro

Arrangement.Center

Elementi disposti al centro

Arrangement.End

Elementi disposti alla fine

Arrangement.SpaceAround

Elementi disposti con spazio intorno

Arrangement.spacedBy(8.dp)

Elementi distanziati di un determinato dp

Per FlowColumn, sono disponibili opzioni simili con verticalArrangement, con il valore predefinito Arrangement.Top.

Disposizione asse trasversale

L'asse trasversale è l'asse nella direzione opposta all'asse principale. Ad esempio, in FlowRow, questo è l'asse verticale. Per modificare la disposizione dei contenuti complessivi all'interno del container nell'asse trasversale, utilizza verticalArrangement per FlowRow e horizontalArrangement per FlowColumn.

Per FlowRow, la tabella seguente mostra esempi di impostazione di verticalArrangement diversi sugli elementi:

Disposizione verticale impostata su FlowRow

Risultato

Arrangement.Top (Default)

Disposizione superiore del contenitore

Arrangement.Bottom

Disposizione in basso del contenitore

Arrangement.Center

Disposizione centrale del container

Per FlowColumn, sono disponibili opzioni simili con horizontalArrangement. La disposizione predefinita dell'asse trasversale è Arrangement.Start.

Allineamento dei singoli elementi

Potresti voler posizionare i singoli elementi all'interno della riga con allineamenti diversi. Questo valore è diverso da verticalArrangement e horizontalArrangement perché allinea gli elementi all'interno della riga corrente. Puoi applicare questa opzione con Modifier.align().

Ad esempio, se gli elementi di un FlowRow hanno altezze diverse, la riga assume l'altezza dell'elemento più grande e applica Modifier.align(alignmentOption) agli elementi:

Allineamento verticale impostato su FlowRow

Risultato

Alignment.Top (Default)

Elementi allineati in alto

Alignment.Bottom

Elementi allineati in basso

Alignment.CenterVertically

Elementi allineati al centro

Per FlowColumn sono disponibili opzioni simili. L'allineamento predefinito è Alignment.Start.

Numero massimo di elementi in una riga o colonna

I parametri maxItemsInEachRow o maxItemsInEachColumn definiscono il numero massimo di elementi nell'asse principale da consentire in una riga prima di andare a capo. Il valore predefinito è Int.MAX_INT, che consente di inserire il maggior numero possibile di elementi, purché le loro dimensioni consentano di inserirli nella riga.

Ad esempio, l'impostazione di un maxItemsInEachRow forza il layout iniziale ad avere solo 3 elementi:

Nessun valore massimo impostato

maxItemsInEachRow = 3

Nessun limite massimo impostato nella riga del flusso Numero massimo di elementi impostati nella riga del flusso

Pesi degli elementi

Il peso aumenta un elemento in base al suo fattore e allo spazio disponibile sulla riga in cui è stato inserito. È importante notare che esiste una differenza tra FlowRow e Row per quanto riguarda il modo in cui i pesi vengono utilizzati per calcolare la larghezza di un elemento. Per Rows, il peso si basa su tutti gli elementi in Row. Con FlowRow, il peso si basa sugli elementi nella riga in cui è posizionato un elemento, non su tutti gli elementi nel contenitore FlowRow.

Ad esempio, se hai 4 elementi che rientrano tutti in una riga, ciascuno con ponderazioni diverse di 1f, 2f, 1f e 3f, la ponderazione totale è 7f. Lo spazio rimanente in una riga o colonna verrà diviso per 7f. La larghezza di ogni elemento verrà quindi calcolata utilizzando: weight * (remainingSpace / totalWeight).

Puoi utilizzare una combinazione di Modifier.weight e numero massimo di elementi con FlowRow o FlowColumn per creare un layout a griglia. Questo approccio è utile per creare layout adattabili che si adattano alle dimensioni del dispositivo.

Ecco alcuni esempi di ciò che puoi ottenere utilizzando i pesi. Un esempio è una griglia in cui gli elementi hanno le stesse dimensioni, come mostrato di seguito:

Griglia creata con riga di flusso
Figura 2. Utilizzo di FlowRow per creare una griglia

Per creare una griglia di elementi di dimensioni uguali, puoi procedere nel seguente modo:

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)
    }
}

È importante notare che, se aggiungi un altro elemento e lo ripeti 10 volte anziché 9, l'ultimo elemento occupa l'intera ultima colonna, poiché il peso totale dell'intera riga è 1f:

Ultimo elemento a grandezza originale nella griglia
Figura 3. Utilizzo di FlowRow per creare una griglia con l'ultimo elemento che occupa l'intera larghezza

Puoi combinare i pesi con altri Modifiers, ad esempio Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) o Modifier.fillMaxWidth(fraction). Tutti questi modificatori funzionano insieme per consentire il dimensionamento adattabile degli elementi all'interno di un FlowRow (o FlowColumn).

Puoi anche creare una griglia alternata di elementi di dimensioni diverse, in cui due elementi occupano ciascuno metà della larghezza e un elemento occupa l'intera larghezza della colonna successiva:

Griglia alternata con riga di flusso
Figura 4. FlowRow con righe di dimensioni alternate

Puoi farlo con il seguente codice:

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))
        }
    }
}

Dimensionamento frazionario

Utilizzando Modifier.fillMaxWidth(fraction), puoi specificare le dimensioni del container che un elemento deve occupare. Questo comportamento è diverso da quello di Modifier.fillMaxWidth(fraction) quando viene applicato a Row o Column, in quanto gli elementi Row/Column occupano una percentuale della larghezza rimanente, anziché l'intera larghezza del contenitore.

Ad esempio, il codice seguente produce risultati diversi quando si utilizza FlowRow rispetto a 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: elemento centrale con una frazione di 0,7 della larghezza dell'intero container.

Larghezza frazionaria con riga di flusso

Row: l'elemento centrale occupa lo 0,7% della larghezza rimanente di Row.

Larghezza frazionaria con riga

fillMaxColumnWidth() e fillMaxRowHeight()

L'applicazione di Modifier.fillMaxColumnWidth() o Modifier.fillMaxRowHeight() a un elemento all'interno di un FlowColumn o FlowRow garantisce che gli elementi nella stessa colonna o riga occupino la stessa larghezza o altezza dell'elemento più grande nella colonna/riga.

Ad esempio, questo esempio utilizza FlowColumn per visualizzare l'elenco dei dessert Android. Puoi vedere la differenza nella larghezza di ogni elemento quando Modifier.fillMaxColumnWidth() viene applicato agli elementi rispetto a quando non viene applicato e gli elementi vengono mandati a capo.

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() applicato a ogni elemento

fillMaxColumnWidth

Nessuna modifica della larghezza impostata (elementi con ritorno a capo)

Nessuna larghezza massima della colonna impostata per l'assenza di riempimento