Membuat tata letak daftar-detail

Daftar-detail adalah pola UI yang terdiri dari tata letak dua panel dengan satu panel menampilkan daftar item dan panel lainnya menampilkan detail item yang dipilih dari daftar.

Pola ini sangat berguna untuk aplikasi yang memberikan informasi mendalam tentang elemen koleksi besar, misalnya, program email yang memiliki daftar email dan konten terperinci setiap pesan email. Daftar-detail juga dapat digunakan untuk jalur yang tidak terlalu penting, seperti membagi preferensi aplikasi ke dalam daftar kategori dengan preferensi untuk setiap kategori di panel detail.

Mengimplementasikan pola UI dengan ListDetailPaneScaffold

ListDetailPaneScaffold adalah composable yang menyederhanakan implementasi pola daftar-detail di aplikasi Anda. Scaffold daftar-detail dapat terdiri dari hingga tiga panel: panel daftar, panel detail, dan panel tambahan opsional. Scaffold menangani perhitungan ruang layar. Jika ukuran layar yang memadai tersedia, panel detail akan ditampilkan di samping panel daftar. Pada ukuran layar kecil, scaffold akan otomatis beralih untuk menampilkan layar daftar atau panel detail dalam layar penuh.

Panel detail yang ditampilkan di samping halaman daftar.
Gambar 1. Jika ukuran layar yang tersedia cukup, panel detail akan ditampilkan di samping panel daftar.
Setelah item dipilih, panel detail akan mengambil alih seluruh layar.
Gambar 2. Jika ukuran layar dibatasi, panel detail (karena item telah dipilih) akan mengambil alih seluruh ruang.

Mendeklarasikan dependensi

ListDetailPaneScaffold adalah bagian dari library adaptif Material 3. Tambahkan dependensi untuk library dalam file build.gradle bagi aplikasi atau modul Anda:

implementation("androidx.compose.material3.adaptive:adaptive:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha07")

Penggunaan dasar

Berikut ini ilustrasi penggunaan dasar ListDetailPaneScaffold:

  1. Simpan item yang saat ini dipilih dari daftar dalam variabel status yang dapat berubah. Variabel menyimpan item yang akan ditampilkan di panel detail. Biasanya, Anda perlu menginisialisasi item yang saat ini dipilih dengan null, yang menunjukkan bahwa belum ada pilihan yang dibuat.

    class MyItem(val id: Int) {
        companion object {
            val Saver: Saver<MyItem?, Int> = Saver(
                { it?.id },
                ::MyItem,
            )
        }
    }

    var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
        mutableStateOf(null)
    }

  2. Buat ThreePaneScaffoldNavigator dengan rememberListDetailPaneScaffoldNavigator, lalu tambahkan BackHandler. Navigator ini digunakan untuk berpindah antara panel daftar, detail, dan tambahan, serta memberikan status ke scaffold. BackHandler yang ditambahkan memberikan dukungan untuk menavigasi kembali menggunakan tombol atau gestur kembali sistem. Perilaku tombol kembali yang diharapkan untuk ListDetailPaneScaffold bergantung pada ukuran jendela dan nilai scaffold saat ini. Jika ListDetailPaneScaffold dapat mendukung pengembalian dengan status saat ini, canNavigateBack() akan menjadi true, yang mengaktifkan BackHandler.

    val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. Teruskan scaffoldState dari navigator yang Anda buat ke composable ListDetailPaneScaffold.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

  4. Masukkan implementasi panel daftar Anda ke ListDetailPaneScaffold. Pastikan implementasi Anda menyertakan argumen callback untuk mengambil item yang baru dipilih. Saat callback ini dipicu, update variabel status selectedItem dan gunakan ThreePaneScaffoldNavigator untuk menampilkan panel detail (ListDetailPaneScaffoldRole.Detail).

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane(Modifier) {
                MyList(
                    onItemClick = { id ->
                        // Set current item
                        selectedItem = id
                        // Switch focus to detail pane
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                    }
                )
            }
        },
        // ...
    )

  5. Sertakan implementasi panel detail Anda di ListDetailPaneScaffold. Menampilkan konten detail hanya jika selectedItem bukan null.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane(Modifier) {
                selectedItem?.let { item ->
                    MyDetails(item)
                }
            }
        },
    )

Setelah menerapkan langkah-langkah di atas, kode Anda akan terlihat seperti ini:

// Currently selected item
var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
    mutableStateOf(null)
}

// Create the ListDetailPaneScaffoldState
val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane(Modifier) {
            MyList(
                onItemClick = { id ->
                    // Set current item
                    selectedItem = id
                    // Display the detail pane
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane(Modifier) {
            // Show the detail pane content if selected item is available
            selectedItem?.let { item ->
                MyDetails(item)
            }
        }
    },
)