Layouts de fluxo no Compose

FlowRow e FlowColumn são combináveis semelhantes a Row e Column, mas diferem porque os itens fluem para a próxima linha quando o contêiner fica sem espaço. Isso cria várias linhas ou colunas. O número de itens em uma linha também pode ser controlado definindo maxItemsInEachRow ou maxItemsInEachColumn. Muitas vezes, é possível usar FlowRow e FlowColumn para criar layouts responsivos. O conteúdo não será cortado se os itens forem muito grandes para uma dimensão, e o uso de uma combinação de maxItemsInEach* com Modifier.weight(weight) pode ajudar a criar layouts que preenchem/expandem a largura de uma linha ou coluna quando necessário.

O exemplo típico é para um ícone ou uma interface de filtragem:

Cinco ícones em um FlowRow, mostrando o transbordo para a próxima linha quando não há mais espaço disponível.
Figura 1. Exemplo de FlowRow

Uso básico

Para usar FlowRow ou FlowColumn, crie esses elementos combináveis e coloque os itens que precisam seguir o fluxo padrão dentro deles:

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

Esse snippet resulta na interface mostrada acima, com itens que fluem automaticamente para a próxima linha quando não há mais espaço na primeira.

Recursos do layout de fluxo

Os layouts de fluxo têm os seguintes recursos e propriedades que podem ser usados para criar layouts diferentes no app.

Organização do eixo principal: horizontal ou vertical

O eixo principal é aquele em que os itens são dispostos (por exemplo, em FlowRow, os itens são organizados horizontalmente). O parâmetro horizontalArrangement em FlowRow controla a forma como o espaço livre é distribuído entre os itens.

A tabela a seguir mostra exemplos de como definir horizontalArrangement em itens para FlowRow:

Organização horizontal definida em FlowRow

Resultado

Arrangement.Start (Default)

Itens organizados com início

Arrangement.SpaceBetween

Organização de itens com espaço entre eles

Arrangement.Center

Itens organizados no centro

Arrangement.End

Itens organizados no final

Arrangement.SpaceAround

Itens organizados com espaço ao redor deles

Arrangement.spacedBy(8.dp)

Itens espaçados por uma determinada dp

Para FlowColumn, opções semelhantes estão disponíveis com verticalArrangement, com o padrão de Arrangement.Top.

Organização entre eixos

O eixo cruzado é o eixo na direção oposta ao eixo principal. Por exemplo, em FlowRow, esse é o eixo vertical. Para mudar como o conteúdo geral dentro do contêiner é organizado no eixo transversal, use verticalArrangement para FlowRow e horizontalArrangement para FlowColumn.

Para FlowRow, a tabela a seguir mostra exemplos de como definir diferentes verticalArrangement nos itens:

Organização vertical definida em FlowRow

Resultado

Arrangement.Top (Default)

Arranjo superior do contêiner

Arrangement.Bottom

Arranjo inferior do contêiner

Arrangement.Center

Disposição central do contêiner

Para FlowColumn, opções semelhantes estão disponíveis com horizontalArrangement. O arranjo padrão do eixo transversal é Arrangement.Start.

Alinhamento de itens individuais

Talvez você queira posicionar itens individuais na linha com alinhamentos diferentes. Isso é diferente de verticalArrangement e horizontalArrangement, porque alinha os itens na linha atual. É possível aplicar isso com Modifier.align().

Por exemplo, quando os itens em uma FlowRow têm alturas diferentes, a linha assume a altura do maior item e aplica Modifier.align(alignmentOption) aos itens:

Alinhamento vertical definido em FlowRow

Resultado

Alignment.Top (Default)

Itens alinhados à parte de cima

Alignment.Bottom

Itens alinhados à parte de baixo

Alignment.CenterVertically

Itens alinhados ao centro

Para FlowColumn, opções semelhantes estão disponíveis. O alinhamento padrão é Alignment.Start.

Máximo de itens na linha ou coluna

Os parâmetros maxItemsInEachRow ou maxItemsInEachColumn definem o número máximo de itens no eixo principal que podem ser incluídos em uma linha antes de passar para a próxima. O padrão é Int.MAX_INT, que permite o maior número possível de itens, desde que os tamanhos deles caibam na linha.

Por exemplo, definir um maxItemsInEachRow força o layout inicial a ter apenas três itens:

Nenhum máximo definido

maxItemsInEachRow = 3

Nenhum máximo definido na linha de fluxo Máximo de itens definidos na linha de fluxo

Pesos dos itens

O peso aumenta um item com base no fator dele e no espaço disponível na linha em que ele foi colocado. É importante ressaltar que há uma diferença entre FlowRow e Row na forma como os pesos são usados para calcular a largura de um item. Para Rows, o peso é baseado em todos os itens no Row. Com FlowRow, o peso é baseado nos itens na linha em que um item é colocado, não em todos os itens no contêiner FlowRow.

Por exemplo, se você tiver quatro itens em uma linha, cada um com pesos diferentes de 1f, 2f, 1f e 3f, o peso total será 7f. O espaço restante em uma linha ou coluna será dividido por 7f. Em seguida, a largura de cada item será calculada usando: weight * (remainingSpace / totalWeight).

É possível usar uma combinação de Modifier.weight e itens máximos com FlowRow ou FlowColumn para criar um layout semelhante a uma grade. Essa abordagem é útil para criar layouts responsivos que se ajustam ao tamanho do dispositivo.

Há alguns exemplos diferentes do que você pode fazer usando pesos. Um exemplo é uma grade em que os itens têm o mesmo tamanho, como mostrado abaixo:

Grade criada com linha de fluxo
Figura 2. Como usar FlowRow para criar uma grade

Para criar uma grade de itens de tamanhos iguais, faça o seguinte:

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

Se você adicionar outro item e repetir 10 vezes em vez de 9, o último item vai ocupar toda a última coluna, já que o peso total de toda a linha é 1f:

Último item em tamanho original na grade
Figura 3. Como usar FlowRow para criar uma grade em que o último item ocupa toda a largura

É possível combinar pesos com outros Modifiers, como Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) ou Modifier.fillMaxWidth(fraction). Todos esses modificadores funcionam em conjunto para permitir o dimensionamento responsivo de itens em um FlowRow (ou FlowColumn).

Também é possível criar uma grade alternada de tamanhos de itens diferentes, em que dois itens ocupam metade da largura cada um, e um item ocupa toda a largura da próxima coluna:

Grade alternada com linha de fluxo
Figura 4. FlowRow com linhas de tamanhos alternados

Para isso, use o seguinte código:

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 fracionário

Usando Modifier.fillMaxWidth(fraction), é possível especificar o tamanho do contêiner que um item deve ocupar. Isso é diferente de como Modifier.fillMaxWidth(fraction) funciona quando aplicado a Row ou Column, em que os itens Row/Column ocupam uma porcentagem da largura restante, em vez de toda a largura do contêiner.

Por exemplo, o código a seguir produz resultados diferentes ao usar FlowRow em vez de 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: item do meio com fração de 0,7 da largura total do contêiner.

Largura fracionária com linha de fluxo

Row: item do meio ocupando 0,7% da largura restante de Row.

Largura fracionária com linha

fillMaxColumnWidth() e fillMaxRowHeight()

Aplicar Modifier.fillMaxColumnWidth() ou Modifier.fillMaxRowHeight() a um item dentro de um FlowColumn ou FlowRow garante que os itens na mesma coluna ou linha ocupem a mesma largura ou altura que o maior item na coluna/linha.

Por exemplo, este exemplo usa FlowColumn para mostrar a lista de sobremesas do Android. É possível ver a diferença na largura de cada item quando Modifier.fillMaxColumnWidth() é aplicado aos itens em comparação com quando não é e os itens são agrupados.

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() aplicado a cada item

fillMaxColumnWidth

Nenhuma mudança de largura definida (itens de agrupamento)

Nenhuma largura máxima de coluna de preenchimento definida