Tata letak flow di Compose

FlowRow dan FlowColumn adalah composable yang mirip dengan Row dan Column, tetapi berbeda dalam item tersebut yang mengalir ke baris berikutnya saat kontainer kehabisan ruang. Hal ini menciptakan beberapa baris atau kolom. Jumlah item dalam satu baris juga dapat dikontrol dengan menetapkan maxItemsInEachRow atau maxItemsInEachColumn. Anda dapat sering menggunakan FlowRow dan FlowColumn untuk membangun tata letak responsif— konten tidak akan dipotong nonaktif jika item terlalu besar untuk satu dimensi, dan menggunakan kombinasi maxItemsInEach* dengan Modifier.weight(weight) dapat membantu mem-build tata letak yang mengisi/memperluas lebar baris atau kolom bila diperlukan.

Contoh umumnya adalah untuk chip atau UI pemfilteran:

5 chip dalam FlowRow, yang menampilkan tambahan ke baris berikutnya jika tidak ada
lebih banyak ruang yang tersedia.
Gambar 1. Contoh FlowRow

Penggunaan dasar

Untuk menggunakan FlowRow atau FlowColumn, buat composable ini dan tempatkan item di dalamnya yang harus mengikuti alur standar:

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

Cuplikan ini menghasilkan UI yang ditampilkan di atas, dengan item yang secara otomatis mengalir ke baris berikutnya ketika tidak ada lagi ruang di baris pertama.

Fitur tata letak alur

Tata letak flow memiliki fitur dan properti berikut yang dapat Anda gunakan untuk membuat tata letak yang berbeda di aplikasi Anda.

Pengaturan sumbu utama: pengaturan horizontal atau vertikal

Sumbu utama adalah sumbu tempat item ditata (misalnya, di FlowRow, item disusun secara horizontal). horizontalArrangement parameter di FlowRow mengontrol cara distribusi ruang kosong antar-item.

Tabel berikut menunjukkan contoh setelan horizontalArrangement pada item untuk FlowRow:

Pengaturan horizontal disetel di FlowRow

Hasil

Arrangement.Start (Default)

Item disusun dengan awal

Arrangement.SpaceBetween

Pengaturan item dengan spasi di antaranya

Arrangement.Center

Item disusun di tengah

Arrangement.End

Item disusun di akhir

Arrangement.SpaceAround

Item tersusun dengan ruang di sekelilingnya

Arrangement.spacedBy(8.dp)

Item yang berjarak dp tertentu

Untuk FlowColumn, opsi serupa tersedia dengan verticalArrangement, dengan default-nya, yaitu Arrangement.Top.

Pengaturan sumbu silang

Sumbu silang adalah sumbu yang berlawanan arah dengan sumbu utama. Sebagai contoh, di FlowRow, ini adalah sumbu vertikal. Untuk mengubah bagaimana keseluruhan konten di dalam penampung disusun di sumbu silang, gunakan verticalArrangement untuk FlowRow, dan horizontalArrangement untuk FlowColumn.

Untuk FlowRow, tabel berikut menunjukkan contoh setelan yang berbeda-beda verticalArrangement pada item:

Pengaturan vertikal ditetapkan di FlowRow

Hasil

Arrangement.Top (Default)

Pengaturan bagian atas container

Arrangement.Bottom

Pengaturan bawah container

Arrangement.Center

Pengaturan pusat kontainer

Untuk FlowColumn, opsi serupa tersedia dengan horizontalArrangement. Susunan sumbu silang default adalah Arrangement.Start.

Perataan setiap item

Anda mungkin ingin menempatkan setiap item dalam baris dengan {i>alignment<i} (penyelarasan). Ini berbeda dengan verticalArrangement dan horizontalArrangement saat meratakan item dalam baris saat ini. Anda dapat terapkan ini dengan Modifier.align().

Misalnya, jika item di FlowRow memiliki ketinggian yang berbeda, baris akan mengambil tinggi item terbesar dan menerapkan Modifier.align(alignmentOption) ke item:

Perataan vertikal ditetapkan di FlowRow

Hasil

Alignment.Top (Default)

Item sejajar dengan bagian atas

Alignment.Bottom

Item sejajar dengan bagian bawah

Alignment.CenterVertically

Item sejajar dengan bagian tengah

Untuk FlowColumn, opsi serupa tersedia. Perataan defaultnya adalah Alignment.Start.

Item maksimum dalam baris atau kolom

Parameter maxItemsInEachRow atau maxItemsInEachColumn menentukan item di sumbu utama untuk memungkinkan satu baris sebelum menggabungkan ke yang berikutnya. Tujuan defaultnya adalah Int.MAX_INT, yang mengizinkan sebanyak mungkin item, selama ukurannya memungkinkan mereka untuk masuk ke dalam garis.

Misalnya, menyetel maxItemsInEachRow akan memaksa tata letak awal hanya untuk memiliki 3 item:

Tidak ada nilai maksimum

maxItemsInEachRow = 3

Tidak ada nilai maksimum yang ditetapkan pada baris alur Jumlah maksimum item yang ditetapkan pada baris alur

Item alur pemuatan lambat

ContextualFlowRow dan ContextualFlowColumn adalah layanan versi FlowRow dan FlowColumn yang memungkinkan Anda memuat konten dengan lambat baris atau kolom alur Anda. Mereka juga memberikan informasi mengenai posisi barang (indeks, nomor baris, dan ukuran yang tersedia), seperti jika item berada di urutan pertama baris. Hal ini berguna untuk set data besar dan jika Anda memerlukan informasi kontekstual tentang suatu item.

Parameter maxLines membatasi jumlah baris yang ditampilkan, dan overflow menentukan apa yang harus ditampilkan bila overflow item tercapai, sehingga Anda dapat menentukan expandIndicator atau collapseIndicator.

Misalnya, untuk menampilkan "+ (jumlah item tersisa)" atau "Tampilkan Lebih Sedikit" tombol:

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

Contoh baris alur kontekstual.
Gambar 2. Contoh ContextualFlowRow

Berat item

Bobot menambah item berdasarkan faktornya dan ruang yang tersedia di garis ditempatkan. Yang penting, ada perbedaan antara FlowRow dan Row dengan bagaimana bobot digunakan untuk menghitung lebar item. Untuk Rows, berat didasarkan pada semua item dalam Row. Dengan FlowRow, bobot didasarkan pada item dalam baris tempat item ditempatkan, bukan semua item dalam Penampung FlowRow.

Misalnya, jika Anda memiliki 4 item yang semuanya berada dalam satu baris, masing-masing dengan bobot 1f, 2f, 1f, dan 3f, berat total adalah 7f. Sisa ruang dalam baris atau kolom akan dibagi dengan 7f. Kemudian, setiap lebar item akan dihitung menggunakan: weight * (remainingSpace / totalWeight).

Anda dapat menggunakan kombinasi Modifier.weight dan jumlah maksimum item dengan FlowRow atau FlowColumn untuk membuat tata letak seperti petak. Pendekatan ini berguna untuk membuat tata letak responsif yang menyesuaikan dengan ukuran perangkat Anda.

Ada beberapa contoh tentang apa yang dapat Anda capai menggunakan bobot. paket Premium AI contohnya adalah {i>grid<i} dengan item yang berukuran sama, seperti yang ditunjukkan di bawah ini:

Petak dibuat dengan baris alir
Gambar 3. Menggunakan FlowRow untuk membuat petak

Untuk membuat petak dengan ukuran item yang sama, Anda dapat melakukan langkah berikut:

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

Yang penting, jika Anda menambahkan item lain dan mengulanginya 10 kali, bukan 9, item terakhir akan memenuhi seluruh kolom terakhir, karena total bobot untuk seluruh baris adalah 1f:

Ukuran penuh item terakhir di petak
Gambar 4. Menggunakan FlowRow untuk membuat petak dengan item terakhir menggunakan lebar penuh

Anda dapat menggabungkan bobot dengan Modifiers lain seperti Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio), atau Modifier.fillMaxWidth(fraction). Semua pengubah ini bekerja bersama-sama dengan memungkinkan ukuran responsif item dalam FlowRow (atau FlowColumn).

Anda juga dapat membuat petak alternatif dengan berbagai ukuran item, dengan dua item masing-masing mengambil setengah lebarnya, dan satu item memenuhi lebar penuh item berikutnya kolom:

Petak alternatif dengan baris alur
Gambar 5. FlowRow dengan ukuran baris alternatif

Anda dapat melakukannya dengan kode berikut:

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

Ukuran pecahan

Dengan Modifier.fillMaxWidth(fraction), Anda dapat menentukan ukuran yang harus diikuti oleh suatu item. Hal ini berbeda dengan bagaimana Modifier.fillMaxWidth(fraction) berfungsi jika diterapkan ke Row atau Column, di bahwa item Row/Column menggunakan persentase lebar yang tersisa, bukan seluruh lebar container.

Misalnya, kode berikut memberikan hasil yang berbeda saat menggunakan FlowRow vs. 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 tengah dengan 0,7 pecahan dari keseluruhan lebar penampung.

Lebar pecahan dengan baris alur

Row: Item tengah menempati 0,7 persen dari lebar Row yang tersisa.

Lebar pecahan dengan baris

fillMaxColumnWidth() dan fillMaxRowHeight()

Menerapkan Modifier.fillMaxColumnWidth() atau Modifier.fillMaxRowHeight() ke item di dalam FlowColumn atau FlowRow memastikan bahwa item dalam kolom atau baris yang sama memiliki lebar atau tinggi yang sama dengan item terbesar di kolom/baris.

Misalnya, contoh ini menggunakan FlowColumn untuk menampilkan daftar Android makanan penutup. Anda dapat melihat perbedaan lebar setiap item saat Modifier.fillMaxColumnWidth() diterapkan ke item versus saat tidak dan membungkus item.

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() diterapkan ke setiap item

FillMaxColumnWidth

Perubahan lebar belum ditetapkan (menggabungkan item)

Tidak ada lebar kolom maks isi yang ditetapkan