Status dan Jetpack Compose

Status di aplikasi adalah nilai yang dapat berubah dari waktu ke waktu. Ini adalah definisi yang sangat luas dan mencakup semua dari database Room hingga variabel di class.

Semua aplikasi Android menampilkan status kepada pengguna. Beberapa contoh status di aplikasi Android:

  • Snackbar yang muncul saat koneksi jaringan tidak dapat dibuat.
  • Postingan blog dan komentar terkait.
  • Animasi ripple pada tombol yang diputar saat pengguna mengkliknya.
  • Stiker yang dapat digambar pengguna di atas gambar.

Jetpack Compose membantu Anda menjelaskan lokasi dan cara Anda menyimpan serta menggunakan status di aplikasi Android. Panduan ini berfokus pada koneksi antara status dan komponen, dan pada API yang ditawarkan Jetpack Compose untuk bekerja lebih mudah dengan status.

Status dalam Compose

Konsep status adalah inti dari Compose. Pertimbangkan contoh sederhana: layar tempat pengguna memasukkan nama dan salam akan ditampilkan sebagai respons. Kode berikut mencakup teks untuk salam dan kolom teks untuk input nama:

@Composable
fun HelloContent() {
   Column(modifier = Modifier.padding(16.dp)) {
       Text(
           text = "Hello!",
           modifier = Modifier.padding(bottom = 8.dp),
           style = MaterialTheme.typography.h5
       )
       OutlinedTextField(
           value = "",
           onValueChange = { },
           label = { Text("Name") }
       )
   }
}

Jika menjalankan ini, Anda akan melihat bahwa tidak ada yang terjadi. Hal itu karena TextField tidak diupdate sendiri—parameter akan diupdate saat parameter value berubah. Hal ini terjadi karena cara kerja komposisi dan rekomposisi di Compose.

Komposisi dan rekomposisi

Komposisi mendeskripsikan UI dan dihasilkan dengan menjalankan komponen. Komposisi adalah struktur pohon dari komponen yang mendeskripsikan UI.

Selama komposisi awal, Compose akan tetap melacak komponen yang dipanggil untuk mendeskripsikan UI dalam komposisi. Kemudian, saat status aplikasi berubah, Jetpack Compose menjadwalkan rekomposisi. Rekomposisi menjalankan komponen yang mungkin telah berubah sebagai respons terhadap perubahan status, dan Jetpack Compose memperbarui komposisi untuk mencerminkan setiap perubahan.

Komposisi hanya dapat dihasilkan oleh komposisi awal dan diperbarui dengan rekomposisi. Satu-satunya cara untuk mengubah komposisi adalah melalui rekomposisi.

Untuk mempelajari komposisi awal dan rekomposisi lebih lanjut, lihat Berpikir dalam Compose.

Memperkenalkan status

Untuk memperbarui komposisi, masukkan nilai yang mewakili status TextField dan tambahkan kode untuk mengupdate status saat nilai TextField berubah.

Untuk memperkenalkan status lokal yang menampung nama yang harus ditampilkan, gunakan remember { mutableStateOf() } yang meneruskan nilai default untuk teks tersebut. Dengan begitu, setiap kali status name berubah, nilai yang ditampilkan oleh TextField juga akan berubah.

@Composable
fun HelloContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        var name by remember { mutableStateOf("") }
        Text(
            text = "Hello",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") }
        )
    }
}

Fungsi yang dapat dikomposisi bisa menyimpan satu objek dalam memori menggunakan komponen remember. Nilai yang dihitung oleh remember disimpan dalam komposisi selama komposisi awal, dan nilai yang disimpan ditampilkan selama rekomposisi. Anda dapat menggunakan remember untuk menyimpan objek yang dapat diubah dan tidak dapat diubah.

mutableStateOf membuat MutableState yang merupakan jenis yang dapat diamati dalam Compose. Setiap perubahan pada nilainya akan menjadwalkan rekomposisi fungsi yang dapat dikomposisi, yang membaca nilai tersebut.

remember membantu Anda mempertahankan status di seluruh rekomposisi. Jika Anda menggunakan mutableStateOf tanpa menggunakan remember, status akan diinisialisasi ulang ke string kosong setiap kali komponen HelloContent direkomposisi.

Anda dapat menggunakan nilai yang diingat sebagai parameter untuk komponen lain atau bahkan sebagai logika dalam pernyataan untuk mengubah komponen mana yang ditampilkan. Misalnya, jika tidak ingin menampilkan salam saat nama kosong, gunakan status dalam pernyataan if:

@Composable
fun HelloContent() {
   Column(modifier = Modifier.padding(16.dp)) {
       var name by remember { mutableStateOf("") }
       if (name.isNotEmpty()) {
           Text(
               text = "Hello, $name!",
               modifier = Modifier.padding(bottom = 8.dp),
               style = MaterialTheme.typography.h5
           )
       }
       OutlinedTextField(
           value = name,
           onValueChange = { name = it },
           label = { Text("Name") }
       )
   }
}

Meskipun remember membantu Anda mempertahankan status di seluruh rekomposisi, status tidak dipertahankan di seluruh perubahan konfigurasi. Untuk melakukannya, Anda harus menggunakan rememberSaveable. rememberSaveable otomatis menyimpan nilai apa pun yang dapat disimpan di Bundle. Untuk nilai lain, Anda dapat meneruskan objek penghemat kustom.

@Composable
fun HelloContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        var name by rememberSaveable { mutableStateOf("") }
        if (name.isNotEmpty()) {
            Text(
                text = "Hello, $name!",
                modifier = Modifier.padding(bottom = 8.dp),
                style = MaterialTheme.typography.h5
            )
        }
        OutlinedTextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") }
        )
    }
}

Komponen stateless

Jika suatu komponen memiliki statusnya sendiri seperti contoh di atas, komponen tersebut akan sulit untuk digunakan kembali dan diuji, dan tindakan ini juga akan tetap membuat komponen terkait erat dengan cara statusnya disimpan. Sebaliknya, Anda harus menjadikan ini sebagai komponen stateless —yaitu komponen yang tidak menyimpan status apa pun.

Untuk melakukannya, Anda dapat menggunakan penarikan status. Penarikan status adalah suatu pola pemrograman yang memungkinkan Anda memindahkan status komponen ke pemanggil komponen tersebut. Cara mudah untuk melakukannya adalah dengan mengganti status dengan parameter dan menggunakan lambda untuk mewakili peristiwa.

Dalam contoh kasus, Anda mengekstrak name dan onValueChange dari HelloContent dan menaikkannya ke pohon ke HelloScreen komponen yang memanggil HelloContent.

@Composable
fun HelloScreen() {
    var name by rememberSaveable { mutableStateOf("") }

    HelloContent(name = name, onNameChange = { name = it })
}

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = onNameChange,
            label = { Text("Name") }
        )
    }
}

HelloContent memiliki akses ke status sebagai parameter String yang tidak dapat diubah, serta lambda onNameChange yang dapat dipanggil saat ingin meminta perubahan status.

Lambda adalah cara yang paling umum untuk menjelaskan peristiwa pada komponen. Dalam contoh ini, Anda menentukan peristiwa yang disebut onNameChange menggunakan lambda yang menggunakan String, dengan menggunakan sintaksis jenis fungsi Kotlin: (String) -> Unit. Lambda ini disebut onNameChange—saat ini, karena peristiwa tersebut tidak berarti status telah berubah, tetapi komponen meminta pengendali peristiwa mengubahnya.

Dengan menarik status keluar dari HelloContent, akan lebih mudah untuk memahami tentang komponen, menggunakannya kembali dalam situasi berbeda, dan melakukan pengujian. HelloContent dipisahkan dari cara status disimpan. Pemisahan berarti jika HelloScreen diubah atau diganti, Anda tidak perlu mengubah cara HelloContent diterapkan.

Pola penurunan status, dan peristiwa naik disebut aliran data searah. Dalam kasus ini, status menurun dari HelloScreen menjadi HelloContent dan peristiwa naik dari HelloContent menjadi HelloScreen. Dengan mengikuti aliran data searah, Anda dapat memisahkan komponen yang menampilkan status di UI dari bagian aplikasi yang menyimpan dan mengubah status.

ViewModel dan status

Di Jetpack Compose, Anda dapat menggunakan ViewModel untuk menampilkan status dalam penampung yang dapat dilihat (seperti LiveData atau Flow) dan juga menangani peristiwa yang memengaruhi status tersebut. Contoh HelloScreen di atas akan diterapkan menggunakan ViewModel seperti ini:

class HelloViewModel : ViewModel() {

    // LiveData holds state which is observed by the UI
    // (state flows down from ViewModel)
    private val _name = MutableLiveData("")
    val name: LiveData<String> = _name

    // onNameChange is an event we're defining that the UI can invoke
    // (events flow up from UI)
    fun onNameChange(newName: String) {
        _name.value = newName
    }
}

@Composable
fun HelloScreen(helloViewModel: HelloViewModel = viewModel()) {
    // by default, viewModel() follows the Lifecycle as the Activity or Fragment
    // that calls HelloScreen(). This lifecycle can be modified by callers of HelloScreen.

    // name is the current value of [helloViewModel.name]
    // with an initial value of ""
    val name: String by helloViewModel.name.observeAsState("")
    HelloContent(name = name, onNameChange = { helloViewModel.onNameChange(it) })
}

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = onNameChange,
            label = { Text("Name") }
        )
    }
}

observeAsState mengamati LiveData<T> dan menampilkan State<T> objek yang diupdate kapan pun LiveData berubah. State<T> adalah jenis yang dapat diamati dan dapat digunakan secara langsung oleh Jetpack Compose. observeAsState hanya akan mengamati LiveData saat berada dalam komposisi.

Baris berikut:

val name: String by helloViewModel.name.observeAsState("")

...adalah sugar sintaksis untuk otomatis membuka objek status yang ditampilkan oleh observeAsState. Anda juga dapat menetapkan objek status menggunakan operator penetapan (=) yang menjadikannya State<String>, bukan String:

val nameState: State<String> = helloViewModel.name.observeAsState("")

HelloViewModel dan HelloScreen mengikuti pola aliran data searah dengan status mengalir turun dari HelloViewModel, dan peristiwa mengalir naik dari HelloScreen.

Aliran status dan peristiwa di antara HelloInput, HelloScreen, dan HelloViewModel

Pertimbangkan loop peristiwa UI untuk layar ini:

  1. Peristiwa: onValueChange dipanggil sebagai respons atas tindakan pengguna mengetik karakter.
  2. Status update: HelloViewModel.onNameChange menangani pemrosesan, lalu menetapkan status dari LiveData dan _name yang dapat diubah.
  3. Status tampilan: Nilai HelloViewmodel.name berubah dan teramati oleh Compose di observeAsState. Kemudian, HelloScreen kembali berjalan (atau merekomposisi) untuk mendeskripsikan UI berdasarkan nilai baru name.

Lihat Merancang UI Compose untuk mempelajari cara aliran data searah diimplementasikan menggunakan ViewModel dan Jetpack Compose lebih lanjut.

Menggunakan remember

Fungsi yang dapat dikomposisi bisa menyimpan satu objek dalam memori menggunakan komponen remember. Nilai yang dihitung oleh remember disimpan dalam Komposisi selama komposisi awal, dan nilai yang disimpan dikembalikan selama rekomposisi. remember dapat digunakan untuk menyimpan objek yang dapat diubah dan tidak dapat diubah.

Menggunakan remember untuk menyimpan nilai yang tidak dapat diubah

Anda dapat menyimpan nilai yang tidak dapat diubah saat melakukan cache operasi UI yang mahal, seperti menghitung pemformatan teks. Nilai yang diingat disimpan dalam Komposisi dengan komponen yang disebut remember.

@Composable
fun FancyText(text: String) {
    // by passing text as a parameter to remember, it will re-run the calculation on
    // recomposition if text has changed since the last recomposition
    val formattedText = remember(text) { computeTextFormatting(text) }
    /*...*/
}
Komposisi FancyText dengan formattedText sebagai turunan

Menggunakan remember untuk membuat status internal dalam komponen

Bila menyimpan objek yang dapat diubah menggunakan remember, Anda menambahkan status ke komponen. Anda dapat menggunakan pendekatan ini guna membuat status internal untuk satu komponen stateful.

Kami sangat merekomendasikan agar semua status yang diubah yang digunakan komponen dapat diamati. Hal ini memungkinkan Compose otomatis menulis ulang setiap kali status berubah. Compose dilengkapi jenis State<T> bawaan yang dapat diamati dan terintegrasi langsung dengan runtime Compose.

Contoh bagus dari status internal dalam komponen adalah ExpandingCard yang bergerak antara diciutkan dan diluaskan bila pengguna mengklik tombol.

komponen ExpandedCard bergerak antara diciutkan dan diluaskan

Komponen ini memiliki satu status penting: expanded. Bila expanded komponen akan menampilkan isi, dan bila diciutkan komponen akan menyembunyikan isi.

Komposisi ExpandingCard dengan status expanded sebagai turunan

Anda dapat menambahkan status expanded ke komponen dengan mengingat mutableStateOf(initialValue).

@Composable
fun ExpandingCard(title: String, body: String) {
    // expanded is "internal state" for ExpandingCard
    var expanded by remember { mutableStateOf(false) }

    // describe the card for the current state of expanded
    Card {
        Column(
            Modifier
                .width(280.dp)
                .animateContentSize() // automatically animate size when it changes
                .padding(top = 16.dp, start = 16.dp, end = 16.dp)
        ) {
            Text(text = title)

            // content of the card depends on the current value of expanded
            if (expanded) {
                // TODO: show body & collapse icon
            } else {
                // TODO: show expand icon
            }
        }
    }
}

mutableStateOf membuat MutableState<T> yang dapat diamati, yakni jenis yang dapat diamati dan terintegrasi dengan runtime Compose.

interface MutableState<T> : State<T> {
   override var value: T
}

Setiap perubahan pada value akan menjadwalkan rekomposisi fungsi yang dapat dikomposisi yang membaca value. Dalam kasus ExpandingCard, setiap perubahan yang terjadi pada expanded menyebabkan ExpandingCard direkomposisi.

Ada tiga cara untuk mendeklarasikan objek MutableState dalam komponen:

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remember { mutableStateOf(default) }

Deklarasi ini setara, dan diberikan sebagai sugar sintaksis untuk berbagai penggunaan status. Anda harus memilih deklarasi yang menghasilkan kode yang paling mudah dibaca dalam komponen yang ditulis.

Anda dapat menggunakan nilai status internal dalam komponen sebagai parameter untuk komponen lain, atau bahkan untuk mengubah komponen yang dipanggil. Di ExpandingCard, pernyataan if akan mengubah konten kartu berdasarkan nilai expanded saat ini.

if (expanded) {
   // TODO: show body & collapse icon
} else {
   // TODO: show expand icon
}

Mengubah status internal dalam komponen

Status harus diubah oleh peristiwa dalam komponen. Jika Anda mengubah status saat menjalankan komponen, bukan dalam peristiwa, hal ini merupakan efek samping komponen, yang harus dihindari. Untuk informasi selengkapnya tentang efek samping di Jetpack Compose, lihat Berpikir dalam Compose.

Untuk melengkapi komponen ExpandingCard, tampilkan body dan tombol ciutkan saat expanded adalah true dan tombol luaskan saat expanded adalah false.

@Composable
fun ExpandingCard(title: String, body: String) {
    var expanded by remember { mutableStateOf(false) }

    // describe the card for the current state of expanded
    Card {
        Column(
            Modifier
                .width(280.dp)
                .animateContentSize() // automatically animate size when it changes
                .padding(top = 16.dp, start = 16.dp, end = 16.dp)
        ) {
            Text(text = title)

            // content of the card depends on the current value of expanded
            if (expanded) {
                Text(text = body, Modifier.padding(top = 8.dp))
                // change expanded in response to click events
                IconButton(onClick = { expanded = false }, modifier = Modifier.fillMaxWidth()) {
                    Icon(imageVector = Icons.Default.ExpandLess, contentDescription = "Expand less")
                }
            } else {
                // change expanded in response to click events
                IconButton(onClick = { expanded = true }, modifier = Modifier.fillMaxWidth()) {
                    Icon(imageVector = Icons.Default.ExpandMore, contentDescription = "Expand more")
                }
            }
        }
    }
}

Dalam komponen ini, status diubah sebagai respons terhadap peristiwa onClick. Karena expanded menggunakan var dengan sintaksis delegasikan properti, callback onClick dapat langsung menetapkan expanded.

IconButton(onClick = { expanded = true }, /* … */) {
   // ...
}

Sekarang kita dapat mendeskripsikan Loop Update UI untuk ExpandingCard guna melihat cara status internal diubah dan digunakan oleh Compose.

  1. Peristiwa: onClick dipanggil sebagai respons saat pengguna mengetuk salah satu tombol.
  2. Status update: expanded diubah di pemroses onClick menggunakan penetapan.
  3. Status tampilan: ExpandingCard merekomposisi karena expanded adalah State<Boolean> yang berubah, dan ExpandingCard membacanya di baris if(expanded). ExpandingCard kemudian mendeskripsikan layar untuk nilai baru expanded.

Menggunakan jenis status lain di Jetpack Compose

Jetpack Compose tidak mengharuskan Anda menggunakan MutableState<T> untuk mempertahankan status. Jetpack Compose mendukung jenis lain yang dapat diamati. Sebelum membaca jenis lain yang dapat diamati di Jetpack Compose, Anda harus mengonversinya menjadi State<T> agar Jetpack Compose dapat otomatis merekomposisi saat status berubah.

Compose menghadirkan fungsi untuk membuat State<T> dari jenis umum yang dapat diamati yang digunakan di aplikasi Android:

Anda dapat membuat fungsi ekstensi untuk Jetpack Compose guna membaca jenis lain yang dapat diamati jika aplikasi menggunakan class kustom yang dapat diamati. Lihat penerapan bawaan untuk contoh cara melakukannya. Objek apa pun yang mengizinkan Jetpack Compose untuk berlangganan ke setiap perubahan dapat dikonversi menjadi State<T> dan dibaca komponen.

Memisahkan status internal dari komponen UI

ExpandingCard di bagian terakhir memiliki status internal. Akibatnya, pemanggil tidak dapat mengontrol status. Artinya, misalnya, jika Anda ingin memulai ExpandingCard dalam status diluaskan, tidak ada cara untuk melakukannya. Anda juga tidak dapat membuat kartu diluaskan sebagai respons terhadap peristiwa lain, seperti pengguna mengklik Fab. Ini juga berarti bahwa jika ingin memindahkan status expanded ke ViewModel, Anda tidak dapat melakukannya.

Di sisi lain, dengan menggunakan status internal di ExpandingCard, pemanggil yang tidak perlu mengontrol atau mengangkat status dapat menggunakannya tanpa harus mengelola status sendiri.

Saat mengembangkan komponen yang dapat digunakan kembali, Anda sering kali ingin mengekspos versi stateful dan stateless komponen yang sama. Versi stateful praktis bagi pemanggil yang tidak peduli dengan status, dan versi stateless diperlukan untuk pemanggil yang harus mengontrol atau mengangkat status.

Untuk memberikan keduanya sebagai antarmuka stateful dan stateless, ekstrak komponen stateless yang menampilkan UI menggunakan pengangkatan status.

Perlu diketahui bahwa kedua komponen diberi nama ExpandingCard meskipun parameternya berbeda. Konvensi penamaan untuk komponen yang membuat UI adalah kata benda capital-case yang menggambarkan apa yang diwakili komponen di layar. Dalam hal ini, keduanya mewakili ExpandingCard. Konvensi penamaan ini berlaku di seluruh library Compose, misalnya dalam TextField dan TextField.

Berikut adalah ExpandingCard yang dibagi menjadi komponen stateful dan stateless:

// this stateful composable is only responsible for holding internal state
// and defers the UI to the stateless composable
@Composable
fun ExpandingCard(title: String, body: String) {
    var expanded by remember { mutableStateOf(false) }
    ExpandingCard(
        title = title,
        body = body,
        expanded = expanded,
        onExpand = { expanded = true },
        onCollapse = { expanded = false }
    )
}

// this stateless composable is responsible for describing the UI based on the state
// passed to it and firing events in response to the buttons being pressed
@Composable
fun ExpandingCard(
    title: String,
    body: String,
    expanded: Boolean,
    onExpand: () -> Unit,
    onCollapse: () -> Unit
) {
    Card {
        Column(
            Modifier
                .width(280.dp)
                .animateContentSize() // automatically animate size when it changes
                .padding(top = 16.dp, start = 16.dp, end = 16.dp)
        ) {
            Text(title)
            if (expanded) {
                Spacer(Modifier.height(8.dp))
                Text(body)
                IconButton(onClick = onCollapse, Modifier.fillMaxWidth()) {
                    Icon(imageVector = Icons.Default.ExpandLess, contentDescription = "Expand less")
                }
            } else {
                IconButton(onClick = onExpand, Modifier.fillMaxWidth()) {
                    Icon(imageVector = Icons.Default.ExpandMore, contentDescription = "Expand more")
                }
            }
        }
    }
}

Pengangkatan status di Compose adalah pola pemindahan status ke pemanggil komponen untuk menjadikan komponen stateless. Pola umum untuk status pengangkatan status di Jetpack Compose adalah mengganti variabel status dengan dua parameter:

  • value: T: nilai saat ini yang akan ditampilkan
  • onValueChange: (T) -> Unit: peristiwa yang meminta perubahan nilai, dengan T yang merupakan nilai baru yang diusulkan

Namun, Anda tidak terbatas pada onValueChange. Jika peristiwa yang lebih spesifik sesuai untuk komponen, Anda harus mendefinisikannya menggunakan lambda seperti ExpandingCard dengan onExpand dan onCollapse.

Status yang diangkat dengan cara ini memiliki beberapa properti penting:

  • Satu sumber kebenaran: dengan memindahkan status dan bukan membuat duplikatnya, kita memastikan hanya ada satu sumber kebenaran untuk expanded. Tindakan ini membantu menghindari bug.
  • Dienkapsulasi: hanya ExpandingCard stateful yang dapat mengubah statusnya. Ini sepenuhnya internal.
  • Dapat dibagikan: status yang diangkat dapat dibagikan dengan beberapa komponen. Misalnya kita ingin menyembunyikan tombol Fab saat Card diluaskan, pengangkatan akan memungkinkan kita melakukannya.
  • Dapat dicegat: pemanggil ExpandingCard stateless dapat memutuskan untuk mengabaikan atau mengubah peristiwa sebelum mengubah status.
  • Dipisahkan: status untuk ExpandingCard stateless dapat disimpan di mana pun. Misalnya, sekarang Anda dapat memindahkan title, body, dan expanded ke ViewModel.

Hosting dengan cara ini juga mengikuti aliran data searah. Status diturunkan dari komponen stateful, dan peristiwa mengalir naik dari komponen stateless.

Diagram aliran data searah untuk ExpandingCard stateful dan stateless

Memulihkan status UI setelah aktivitas atau proses pembuatan ulang

Gunakan rememberSaveable untuk memulihkan status UI Anda setelah suatu aktivitas atau proses dibuat ulang. rememberSaveable mempertahankan status di seluruh rekomposisi. Selain itu, rememberSaveable juga mempertahankan status pada seluruh pembuatan ulang aktivitas dan proses.

@Composable
fun MyExample() {
    var selectedId by rememberSaveable<String?> { mutableStateOf(null) }
    /*...*/
}

Semua jenis data yang ditambahkan ke Bundle disimpan secara otomatis. Jika Anda ingin menyimpan sesuatu yang tidak dapat ditambahkan ke Bundle, ada beberapa opsi.

Solusi paling sederhana adalah menambahkan anotasi @Parcelize ke objek. Objek menjadi parcelable dan dapat dijadikan bundle. Misalnya, kode ini membuat jenis data City parcelable dan menyimpannya ke status.

@Parcelize
data class City(val name: String, val country: String)

@Composable
fun MyExample() {
  var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) }
}

Jika karena alasan tertentu @Parcelize tidak cocok, Anda dapat menggunakan mapSaver untuk menentukan aturan sendiri guna mengonversi objek menjadi kumpulan nilai yang dapat disimpan oleh sistem ke Bundle.

data class City(val name: String, val country: String)

val CitySaver = run {
    val nameKey = "Name"
    val countryKey = "Country"
    mapSaver(
        save = { mapOf(nameKey to it.name, nameKey to it.country) },
        restore = { City(it[nameKey] as String, it[countryKey] as String) }
    )
}

@Composable
fun MyExample() {
    var selectedCity = rememberSaveable(saver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) }
}

Agar tidak perlu menentukan kunci untuk peta, Anda juga dapat menggunakan listSaver dan menggunakan indeksnya sebagai kunci:

data class City(val name: String, val country: String)

val CitySaver = listSaver<City, Any>(
    save = { listOf(it.name, it.country) },
    restore = { City(it[0] as String, it[1] as String) }
)

@Composable
fun MyExample() {
    var selectedCity = rememberSaveable(saver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) }
    /*...*/
}

Pelajari lebih lanjut

Untuk mempelajari status dan Jetpack Compose lebih lanjut, buka codelab Menggunakan Status di Jetpack Compose.