Serba-Serbi Jetpack Compose (Bagian 3) — State

Crul
Arunatech
Published in
3 min readMay 31, 2023

Assalamualaikum sobat. Artikel ini merupakan lanjutan dari rangkaian artikel Serba-Serbi Jetpack Compose. Anda bisa melihat artikel sebelumnya di link berikut :

Bagian 1: Pengenalan
Bagian 2: View & Layout
Bagian 3: State (Anda di sini)

Dalam artikel ini saya akan membahas terkait dasar-dasar implementasi state saat menggunakan Jetpack Compose.

Sebagaimana telah dijelaskan pada bagian 1, kita sudah mengetahui bahwa Jetpack Compose adalah sebuah Declarative UI Tool, dan salah satu ciri khas dari UI Tool jenis ini adalah bagian UI atau View-nya yang sangat tergantung pada state yang diberikan.

Di Jetpack Compose terdapat sebuah konsep yang dikenal dengan nama “Recomposition”. Recomposition adalah sebuah event dimana composable function di eksekusi ulang, atau dengan kata lain adalah proses re-render. Hal yang sangat penting untuk digaris bawahi di sini adalah data yang bisa men-trigger proses recomposition hanyalah jika data tersebut merupakan sebuah objek State. Jika kita mengaitkan sebuah composable function dengan sebuah objek State maka jika State tersebut berubah akan terjadi proses re-render pada UI yang terkait.

Berikut adalah contoh penggunaan State pada Jetpack Compose

@Composable
fun Greeting()
var name by remember { mutableStateOf("") }
if (name.isNotEmpty()) {
Text(
text = "Hello, $name!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
}
}

Fungsi mutableStateOf() merupakan fungsi untuk membuat sebuah State. State tersebut akan otomatis ter-observe oleh composable function Greeting(). Jika State tersebut berubah maka akan terjadi proses recomposition. Pada contoh tersebut juga terlihat bahwa kita menggunakan delegate function remember{} untuk membungkus State name. Fungsi tersebut berguna untuk mencegah State name terinisialisasi ulang (menjadi empty string) setiap kali terjadi recomposition.

Ada informasi tambahan terkait fungsi remember{}. Meskipun fungsi tersebut bisa menjaga data saat terjadi recomposition, tetapi fungsi tersebut tidak bisa menjaga data pada saat terjadi configuration changes. Untuk dapat melakukan hal tersebut, kita perlu menggunakan fungsi rememberSaveable{}.

Saat terjadi recomposition pada sebuah view maka child view di dalam-nya pun akan otomatis melakukan recomposition pula. Itulah kenapa dalam contoh diatas jika terjadi recomposition pada fungsi Greeting() maka semua view yang ada di dalam nya pun akan ter-render ulang juga.

State Hoisting

Jika kita lihat contoh fungsi Greeting() di atas, kita bisa lihat bahwa Composable function tersebut bertanggung jawab untuk menampilkan view sekaligus menyimpan data State-nya. Dalam banyak kondisi, hal ini menimbulkan situasi dimana composable function menjadi sulit di re-use dan sulit dites karena view tersebut tightly coupled dengan State-nya. Satu cara yang bisa menjadi solusi adalah melakukan State Hoisting. State Hoisting adalah sebuah programming pattern dimana kita memindahkan State ke si pemanggil fungsinya. Berikut contoh dari State Hoisting

@Composable
fun GreetingScreen() {
var name by remember { mutableStateOf("") }
Greeting(name)
}

@Composable
fun Greeting(name: String) {
if (name.isNotEmpty()) {
Text(
text = "Hello, $name!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
}
}

Karena State sudah dipindahkan ke fungsi pemanggil (GreetingScreen()) maka fungsi Greeting() hanya akan menerima sebuah data String biasa. Dengan pola seperti ini maka fungsi Greeting() akan lebih mudah untuk di-re-use di tempat lain dan akan lebih mudah untuk dites.

Lalu bagaiman cara untuk merubah State dari child composable function ?

Caranya adalah dengan implementasi sebuah listener yang akan digunakan child function untuk memberitahukan bahwa perlu adanya perubahan pada State. Contoh-nya adalah seperti berikut ini :

@Composable
fun GreetingScreen() {
var name by remember { mutableStateOf("") }
Greeting(name = name, onNameChange = { name = it })
}

@Composable
fun Greeting(name: String, onNameChange: (String) -> Unit) {
if (name.isNotEmpty()) {
Text(
text = "Hello, $name!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
}

OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") })
}

Bagimana dengan Flow atau LiveData ?

Jika di UI kita sekarang implementasinya itu memanfaatkan Flow atau LiveData untuk mendapatkan sebuah data/state maka agar dapat terintegrasi dengan Jetpack Compose kita perlu menggunakan salah satu dari fungsi-fungsi berikut

Flow:
- collectAsState() (jika digunakan di environment yang platform-agnostic)
- collectAsStateWithLifecycle() (lifecycle-aware, khusus di Android)
LiveData:
-
observeAsState()

Berikut adalah contoh penggunaannya :

@Composable
fun CounterScreen() {
val counter by viewmodel.myFlow.collectAsState()
Counter(counter = counter)
}

--

--