Tata letak flow di Compose

FlowRow dan FlowColumn adalah composable yang mirip dengan Row dan Column, tetapi berbeda karena item mengalir ke baris berikutnya saat penampung kehabisan ruang. Tindakan ini akan membuat beberapa baris atau kolom. Jumlah item dalam baris juga dapat dikontrol dengan menyetel maxItemsInEachRow atau maxItemsInEachColumn. Anda sering kali dapat menggunakan FlowRow dan FlowColumn untuk membuat tata letak responsif—konten tidak akan terpotong jika item terlalu besar untuk satu dimensi, dan menggunakan kombinasi maxItemsInEach* dengan Modifier.weight(weight) dapat membantu membuat tata letak yang mengisi/memperluas lebar baris atau kolom jika diperlukan.

Contoh umumnya adalah untuk chip atau UI pemfilteran:

5 chip dalam FlowRow, yang menunjukkan overflow ke baris berikutnya jika tidak ada lagi 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 otomatis mengalir ke baris berikutnya jika tidak ada lagi ruang di baris pertama.

Fitur tata letak mengalir

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

Pengaturan sumbu utama: pengaturan horizontal atau vertikal

Sumbu utama adalah sumbu tempat item ditata (misalnya, di FlowRow, item disusun secara horizontal). Parameter horizontalArrangement di FlowRow mengontrol cara ruang kosong didistribusikan di antara item.

Tabel berikut menunjukkan contoh cara menetapkan horizontalArrangement pada item untuk FlowRow:

Pengaturan horizontal ditetapkan pada FlowRow

Hasil

Arrangement.Start (Default)

Item yang disusun dengan awalan

Arrangement.SpaceBetween

Susunan item dengan ruang di antaranya

Arrangement.Center

Item disusun di tengah

Arrangement.End

Item yang disusun di akhir

Arrangement.SpaceAround

Item yang disusun dengan ruang di sekitarnya

Arrangement.spacedBy(8.dp)

Item yang berjarak sekian dp

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

Tata letak sumbu silang

Sumbu silang adalah sumbu dalam arah yang berlawanan dengan sumbu utama. Misalnya, di FlowRow, ini adalah sumbu vertikal. Untuk mengubah cara penataan keseluruhan konten di dalam penampung pada sumbu silang, gunakan verticalArrangement untuk FlowRow, dan horizontalArrangement untuk FlowColumn.

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

Pengaturan vertikal ditetapkan pada FlowRow

Hasil

Arrangement.Top (Default)

Susunan atas penampung

Arrangement.Bottom

Tata letak bagian bawah penampung

Arrangement.Center

Susunan tengah penampung

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

Penyejajaran item individual

Anda mungkin ingin memosisikan setiap item dalam baris dengan perataan yang berbeda. Hal ini berbeda dengan verticalArrangement dan horizontalArrangement karena menyelaraskan item dalam baris saat ini. Anda dapat menerapkannya dengan Modifier.align().

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

Perataan vertikal ditetapkan pada FlowRow

Hasil

Alignment.Top (Default)

Item diratakan di bagian atas

Alignment.Bottom

Item diratakan ke bawah

Alignment.CenterVertically

Item diratakan ke tengah

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

Jumlah maksimum item dalam baris atau kolom

Parameter maxItemsInEachRow atau maxItemsInEachColumn menentukan jumlah item maksimum di sumbu utama yang diizinkan dalam satu baris sebelum di-wrap ke baris berikutnya. Nilai defaultnya adalah Int.MAX_INT, yang memungkinkan sebanyak mungkin item, selama ukurannya memungkinkan item tersebut masuk ke dalam baris.

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

Tidak ada batas maksimum yang ditetapkan

maxItemsInEachRow = 3

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

Berat item

Bobot memperbesar item berdasarkan faktornya dan ruang yang tersedia di baris tempat item tersebut ditempatkan. Yang penting, ada perbedaan antara FlowRow dan Row dalam cara penggunaan bobot untuk menghitung lebar item. Untuk Rows, weight 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 yang berbeda, total bobotnya adalah 7f. Ruang yang tersisa dalam baris atau kolom akan dibagi dengan 7f. Kemudian, lebar setiap item akan dihitung menggunakan: weight * (remainingSpace / totalWeight).

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

Ada beberapa contoh berbeda tentang apa yang dapat Anda capai menggunakan bobot. Salah satu contohnya adalah petak dengan item berukuran sama, seperti yang ditunjukkan di bawah ini:

Petak yang dibuat dengan baris alur
Gambar 2. Menggunakan FlowRow untuk membuat petak

Untuk membuat petak dengan ukuran item yang sama, Anda dapat melakukan hal 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 kali, item terakhir akan menempati seluruh kolom terakhir, karena total bobot untuk seluruh baris adalah 1f:

Item terakhir berukuran penuh di petak
Gambar 3. Menggunakan FlowRow untuk membuat petak dengan item terakhir yang menempati lebar penuh

Anda dapat menggabungkan bobot dengan Modifiers lainnya seperti Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio), atau Modifier.fillMaxWidth(fraction). Semua pengubah ini bekerja bersama untuk memungkinkan penskalaan item yang responsif dalam FlowRow (atau FlowColumn).

Anda juga dapat membuat petak bergantian dengan ukuran item yang berbeda, di mana dua item masing-masing menempati setengah lebar, dan satu item menempati lebar penuh dari kolom berikutnya:

Grid bergantian dengan baris flow
Gambar 4. FlowRow dengan ukuran baris yang bergantian

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

Penentuan ukuran pecahan

Dengan Modifier.fillMaxWidth(fraction), Anda dapat menentukan ukuran penampung yang harus digunakan oleh item. Hal ini berbeda dengan cara kerja Modifier.fillMaxWidth(fraction) saat diterapkan ke Row atau Column, karena item Row/Column menggunakan persentase lebar yang tersisa, bukan lebar seluruh penampung.

Misalnya, kode berikut menghasilkan 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 fraksi 0,7 dari lebar seluruh penampung.

Lebar fraksional dengan baris tata letak

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

Lebar fraksional 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 dalam kolom/baris.

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

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

Tidak ada perubahan lebar yang ditetapkan (item yang di-wrap)

Tidak ada lebar kolom maksimum pengisian yang ditetapkan