Layout di flusso in Scrivi

FlowRow e FlowColumn sono componibili simili a Row e Column, ma si differenziano per il fatto che gli elementi scorrono nella riga successiva quando il contenitore esaurisce lo spazio. In questo modo vengono create più righe o colonne. Il numero di elementi in una riga può essere controllato anche impostando maxItemsInEachRow o maxItemsInEachColumn. Puoi spesso usare 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 è per un chip o un'interfaccia utente di filtro:

5 chip in una 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 componibili e posiziona al suo interno gli elementi che devono seguire la procedura 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 restituisce l'interfaccia utente mostrata sopra, con gli elementi che passano automaticamente alla riga successiva quando non c'è più spazio nella prima riga.

Caratteristiche del layout del flusso

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

Disposizione dell'asse principale: orizzontale o verticale

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

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

Disposizione orizzontale impostata su FlowRow

Risultato

Arrangement.Start (Default)

Elementi disposti con inizio

Arrangement.SpaceBetween

Disposizione degli elementi con uno spazio intermedio

Arrangement.Center

Elementi disposti al centro

Arrangement.End

Elementi disposti alla fine

Arrangement.SpaceAround

Elementi disposti con uno spazio intorno

Arrangement.spacedBy(8.dp)

Elementi con un determinato dp

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

Disposizione dell'asse trasversale

L'asse trasversale corrisponde all'asse nella direzione opposta all'asse principale. Ad esempio, in FlowRow, questo è l'asse verticale. Per modificare il modo in cui i contenuti complessivi all'interno del container sono disposti sull'asse trasversale, utilizza verticalArrangement per FlowRow e horizontalArrangement per FlowColumn.

Per FlowRow, la tabella seguente mostra esempi di impostazione di verticalArrangement diverse per gli elementi:

Disposizione verticale impostata su FlowRow

Risultato

Arrangement.Top (Default)

Disposizione nella parte superiore del contenitore

Arrangement.Bottom

Disposizione nella parte inferiore del contenitore

Arrangement.Center

Disposizione in Container Center

Per FlowColumn, sono disponibili opzioni simili con horizontalArrangement. La disposizione predefinita degli assi trasversali è Arrangement.Start.

Allineamento di singoli elementi

Ti consigliamo di posizionare singoli elementi all'interno della riga con allineamenti diversi. È diverso da verticalArrangement e horizontalArrangement in quanto allinea gli elementi all'interno della riga corrente. Puoi applicarla con Modifier.align().

Ad esempio, quando gli elementi in FlowRow hanno altezze diverse, la riga prende 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 di inserire una riga prima di eseguire il wrapping a quella successiva. Il valore predefinito è Int.MAX_INT, che consente il maggior numero possibile di elementi, a condizione che le loro dimensioni ne permettano l'inserimento nella riga.

Ad esempio, l'impostazione di un maxItemsInEachRow impone al layout iniziale di includere solo 3 elementi:

Nessun valore massimo impostato

maxItemsInEachRow = 3

Nessun valore massimo impostato sulla riga di flusso Numero massimo di elementi impostati nella riga del flusso

Elementi del flusso di caricamento lento

ContextualFlowRow e ContextualFlowColumn sono una versione specializzata di FlowRow e FlowColumn che ti consente di caricare tramite caricamento lento i contenuti della riga o della colonna del flusso. Forniscono inoltre informazioni sulla posizione degli elementi (indice, numero di riga e dimensione disponibile), ad esempio se l'elemento si trova nella prima riga. Questo è utile per set di dati di grandi dimensioni e se hai bisogno di informazioni contestuali su un elemento.

Il parametro maxLines limita il numero di righe visualizzate, mentre il parametro overflow specifica cosa visualizzare quando viene raggiunto un overflow di elementi, consentendoti di specificare un valore expandIndicator o collapseIndicator personalizzato.

Ad esempio, per mostrare un pulsante "+ (numero di elementi rimanenti)" o "Mostra meno":

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

Esempio di righe di flusso contestuali.
Figura 2. Esempio di ContextualFlowRow

Ponderazioni degli articoli

La ponderazione di un elemento aumenta in base al fattore e allo spazio disponibile sulla riga in cui è stato posizionato. È importante sottolineare che c'è una differenza tra FlowRow e Row nel modo in cui le ponderazioni vengono utilizzate per calcolare la larghezza di un elemento. Per Rows, il peso si basa su tutti gli articoli della categoria Row. Con FlowRow, la ponderazione si basa sugli elementi nella riga in cui è inserito un elemento, non su tutti gli elementi contenuti nel contenitore FlowRow.

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

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

Esistono diversi esempi di obiettivi che puoi ottenere utilizzando le ponderazioni. Un esempio è una griglia in cui gli elementi hanno le stesse dimensioni, come mostrato di seguito:

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

Per creare una griglia di dimensioni uguali per gli articoli:

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 sottolineare che, se aggiungi un altro elemento e lo ripeti 10 volte invece di 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 4. Utilizzo di FlowRow per creare una griglia con l'ultimo elemento a larghezza intera

Puoi combinare le ponderazioni con altri Modifiers, ad esempio Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) o Modifier.fillMaxWidth(fraction). Questi modificatori funzionano tutti insieme per consentire il dimensionamento adattabile degli elementi in un elemento FlowRow (o FlowColumn).

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

Griglia alternata con riga di flusso
Figura 5. FlowRow con dimensioni alternative di righe

Puoi ottenere questo risultato 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))
        }
    }
}

Dimensioni frazionarie

Con Modifier.fillMaxWidth(fraction), puoi specificare le dimensioni del contenitore che deve occupare un elemento. Questo è diverso da come Modifier.fillMaxWidth(fraction) funziona quando viene applicato a Row o Column, in quanto Row/Column elementi occupano una percentuale della larghezza rimanente, anziché la larghezza dell'intero contenitore.

Ad esempio, il codice seguente produce risultati diversi quando utilizzi 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 0,7 frazione dell'intera larghezza del contenitore.

Larghezza frazionata con riga di flusso

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

Larghezza frazionata con riga

fillMaxColumnWidth() e fillMaxRowHeight()

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

Ad esempio, in questo esempio viene utilizzato FlowColumn per visualizzare l'elenco di dolci Android. Puoi vedere la differenza nelle larghezze di ogni elemento quando Modifier.fillMaxColumnWidth() viene applicato agli elementi rispetto a quando non è applicato e l'elemento è 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)
            )
        }
    }
}

Valore Modifier.fillMaxColumnWidth() applicato a ogni elemento

Larghezza colonnaMax.

Nessuna modifica alla larghezza impostata (elementi a capo)

Nessuna larghezza massima della colonna di riempimento impostata