Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

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 menyimpan serta menggunakan status di aplikasi Android.

Peristiwa dan loop update UI

Di aplikasi Android, status diperbarui sebagai respons terhadap peristiwa. Peristiwa adalah input yang dihasilkan dari luar aplikasi, seperti pengguna mengetuk tombol yang memanggil OnClickListener, EditText memanggil afterTextChanged, atau akselerometer yang mengirimkan nilai baru.

Semua aplikasi Android memiliki loop update UI inti yang terlihat seperti ini:

Loop update UI inti untuk aplikasi Android.

  • Peristiwa: Peristiwa dihasilkan oleh pengguna atau bagian lain dari program.
  • Status update: Pengendali peristiwa mengubah status.
  • Status tampilan: UI diupdate untuk menampilkan status baru.

Di Jetpack Compose, status dan peristiwa bersifat terpisah. Status mewakili nilai yang dapat diubah, sedangkan peristiwa mewakili notifikasi bahwa sesuatu terjadi.

Dengan memisahkan status dari peristiwa, Anda dapat memisahkan status yang ditampilkan dari cara penyimpanan dan perubahan status tersebut.

Aliran data searah di Jetpack Compose

Compose dibuat untuk aliran data searah. Ini adalah desain tempat status mengalir turun dan peristiwa mengalir naik.

Gambar 1. Aliran data searah

Dengan mengikuti aliran data searah, Anda dapat memisahkan komponen yang menampilkan status di UI dari bagian aplikasi yang menyimpan dan mengubah status.

Loop update UI untuk aplikasi yang menggunakan aliran data searah terlihat seperti ini:

  • Peristiwa: Peristiwa dihasilkan oleh bagian UI dan diteruskan.
  • Status update: Pengendali peristiwa dapat mengubah status.
  • Status tampilan: Status diturunkan, lalu UI mengamati status baru dan menampilkannya.

Mengikuti pola ini saat menggunakan Jetpack Compose memberikan beberapa keuntungan:

  • Kemudahan untuk diuji: pemisahan status dari UI yang menampilkannya akan memudahkan pengujian keduanya secara terpisah.
  • Enkapsulasi status: karena status hanya dapat diperbarui di satu tempat, kecil kemungkinannya Anda membuat status yang tidak konsisten (atau bug).
  • Konsistensi UI: semua update status langsung tercermin di UI dengan menggunakan pemegang status yang dapat diamati.

ViewModel dan aliran data searah

Saat menggunakan ViewModel dan LiveData dari Komponen Arsitektur Android, Anda menerapkan aliran data searah di aplikasi.

Sebelum melihat ViewModel dengan Compose, pertimbangkan Activity menggunakan Tampilan Android dan aliran data searah yang menampilkan "Hello, ${name}" dan memungkinkan para pengguna memasukkan nama mereka.

Contoh input pengguna dengan ViewModels.

Kode untuk layar ini menggunakan ViewModel dan Activity:

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

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

class HelloActivity : AppCompatActivity() {
   val helloViewModel by viewModels<HelloViewModel>()

   override fun onCreate(savedInstanceState: Bundle?) {
       /* … */
       // binding represents the activity layout, inflated with ViewBinding
       binding.textInput.doAfterTextChanged {
           helloViewModel.onNameChanged(it.toString())
       }

       helloViewModel.name.observe(this) { name ->
           binding.helloText.text = "Hello, $name"
       }
   }
}

Dengan menggunakan Komponen Arsitektur Android, kami telah menerapkan desain aliran data searah ke Activity ini.

Gambar 2. Aliran data searah dalam Activity menggunakan ViewModel

Untuk melihat cara kerja aliran data searah di loop update UI, pertimbangkan loop untuk Activity ini:

  1. Peristiwa: onNameChanged dipanggil oleh UI saat input teks berubah.
  2. Status update: onNameChanged memproses, lalu menetapkan status _name.
  3. Status tampilan: Pengamat name dipanggil dan UI menampilkan status baru.

ViewModel dan Jetpack Compose

Anda dapat menggunakan LiveData dan ViewModel di Jetpack Compose untuk menerapkan aliran data searah, seperti yang dilakukan dalam Activity di bagian sebelumnya.

Berikut adalah kode untuk layar yang sama dengan HelloActivity yang ditulis di Jetpack Compose menggunakan HelloViewModel yang sama:

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

   // onNameChanged is an event we're defining that the UI can invoke
   // (events flow up from UI)
   fun onNameChanged(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("")

   Column {
       Text(text = name)
       TextField(
           value = name,
           onValueChange = { helloViewModel.onNameChanged(it) },
           label = { Text("Name") }
       )
   }
}

HelloViewModel dan HelloScreen mengikuti desain aliran data searah. Status mengalir turun dari HelloViewModel, dan peristiwa mengalir naik dari HelloScreen.

Aliran searah antara model tampilan dan layar halo.

Pertimbangkan loop peristiwa UI untuk komponen ini:

  1. Peristiwa: onNameChanged dipanggil sebagai respons saat pengguna mengetik karakter.
  2. Status update: onNameChanged memproses, lalu menetapkan status _name.
  3. Status tampilan: Nilai name berubah, yang terlihat dengan Compose di observeAsState. Kemudian, HelloScreen kembali berjalan (atau merekomposisi) untuk mendeskripsikan UI berdasarkan nilai baru name.

Untuk mempelajari lebih lanjut cara menggunakan ViewModel dan LiveData guna membuat aliran data searah di Android, baca panduan arsitektur aplikasi.

Komponen stateless

Komponen stateless adalah komponen yang tidak dapat mengubah statusnya sendiri. Komponen stateless lebih mudah diuji, cenderung memiliki lebih sedikit bug, dan membuka lebih banyak peluang untuk digunakan kembali.

Jika komponen memiliki status, Anda dapat membuatnya stateless menggunakan pengangkatan status. Pengekangan status adalah pola pemrograman yang memindahkan status ke pemanggil komponen dengan mengganti status internal dalam komponen dengan parameter dan peristiwa.

Untuk melihat contoh pengangkatan status, ekstrak komponen stateless dari HelloScreen.

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

   // name is the _current_ value of [helloViewModel.name]
   val name: String by helloViewModel.name.observeAsState("")

   HelloInput(name = name, onNameChange = { helloViewModel.onNameChanged(it) })
}

@Composable
fun HelloInput(
   /* state */ name: String,
   /* event */ onNameChange: (String) -> Unit
) {
   Column {
       Text(name)
       TextField(
           value = name,
           onValueChange = onNameChange,
           label = { Text("Name") }
       )
   }
}

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

Lambda adalah cara yang paling umum untuk menjelaskan peristiwa pada komponen. Di sini kita mendefinisikan peristiwa onNameChange menggunakan lambda yang mengambil String, menggunakan sintaksis jenis fungsi Kotlin (String) -> Unit. Perlu diketahui bahwa onNameChange adalah bentuk masa kini, karena peristiwa tersebut tidak berarti status telah berubah, tetapi komponen meminta pengendali peristiwa mengubahnya.

HelloScreen adalah komponen stateful karena memiliki dependensi pada class akhir, HelloViewModel, yang dapat langsung mengubah status name. Pemanggil HelloScreen tidak dapat mengontrol update untuk status name. HelloInput adalah komponen stateless karena tidak memiliki kemampuan untuk langsung mengubah status.

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

Proses pengangkatan status memungkinkan Anda memperluas aliran data searah ke komponen stateless. Diagram aliran data searah untuk komponen ini mempertahankan turunnya status, dan naiknya peristiwa seiring dengan semakin banyaknya komponen yang berinteraksi dengan status.

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

Penting untuk memahami bahwa komponen stateless masih dapat berinteraksi dengan status yang berubah dari waktu ke waktu menggunakan aliran data searah dan pengangkatan status.

Untuk memahami cara kerjanya, pertimbangkan Loop Update UI untuk HelloInput:

  1. Peristiwa: onNameChange dipanggil sebagai respons saat pengguna mengetik karakter.
  2. Status update: HelloInput tidak dapat langsung mengubah status. Pemanggil dapat memilih untuk mengubah status sebagai respons terhadap peristiwa onNameChange. Di sini pemanggil, HelloScreen, akan memanggil onNameChanged pada HelloViewModel yang menyebabkan status name diperbarui.
  3. Status tampilan: Bila nilai name berubah, HelloScreen dipanggil kembali dengan name yang diperbarui karena observeAsState. Hal ini selanjutnya akan kembali memanggil HelloInput dengan parameter name baru. Memanggil kembali komponen sebagai respons terhadap perubahan status disebut rekomposisi.

Komposisi dan rekomposisi

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

Selama komposisi awal, Jetpack Compose akan 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.

Status dalam komponen

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) }
    …
}
Gambar 3. 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.

Gambar 4. 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.

Gambar 5. 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(Icons.Default.ExpandLess)
               }
           } else {
               // change expanded in response to click events
               IconButton(onClick = { expanded = true }, modifier = Modifier.fillMaxWidth()) {
                   Icon(Icons.Default.ExpandMore)
               }
           }
       }
   }
}

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.

Anda juga dapat membuat lapisan integrasi untuk objek status yang tidak dapat diamati menggunakan invalidate untuk memicu rekomposisi secara manual. Ini harus direservasi untuk situasi saat Anda harus saling beroperasi dengan jenis yang tidak dapat diamati. Penggunaan invalidate mudah mengalami kesalahan dan cenderung mengarah ke kode kompleks yang lebih sulit dibaca daripada kode sama menggunakan objek status yang dapat diamati.

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 CapitalCase 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(Icons.Default.ExpandLess)
               }
           } else {
               IconButton(onClick = onExpand, Modifier.fillMaxWidth()) {
                   Icon(Icons.Default.ExpandMore)
               }
           }
       }
   }
}

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.

Gambar 6. Diagram aliran data searah untuk ExpandingCardstateful dan stateless

Status internal dan perubahan konfigurasi

Nilai yang diingat oleh remember dalam Komposisi dilupakan dan dibuat ulang selama perubahan konfigurasi seperti rotasi.

Jika Anda menggunakan remember { mutableStateOf(false) }, ExpandingCard stateful direset ke diciutkan setiap kali pengguna memutar ponsel. Kita dapat memperbaikinya dengan menggunakan status instance tersimpan, untuk menyimpan dan memulihkan status secara otomatis pada perubahan konfigurasi.

@Composable
fun ExpandingCard(title: String, body: String) {
   var expanded by savedInstanceState { false }
   ExpandingCard(
       title = title,
       body = body,
       expanded = expanded,
       onExpand = { expanded = true },
       onCollapse = { expanded = false }
   )
}

Fungsi yang dapat dikomposisi savedInstanceState<T> menampilkan MutableState<T> yang otomatis menyimpan dan memulihkan diri pada perubahan konfigurasi. Anda harus menggunakannya untuk status internal apa pun yang diharapkan pengguna tetap berlaku setelah perubahan konfigurasi.

Pelajari lebih lanjut

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