mutableStateOf adalah fungsi di Jetpack Compose yang membuat wadah status yang dapat diamati dan dapat diubah. Ketika nilai di dalam wadah ini berubah, Compose secara otomatis memulai rekomposisi semua komponen yang membaca status ini. Tanpa mutableStateOf, UI tidak akan dapat memperbarui secara reaktif saat data berubah. Menurut Google Android Developers, 2026, mutableStateOf adalah blok bangunan utama untuk status lokal di Compose.
Poin Utama
mutableStateOf adalah fungsi dari paket compose.runtime yang membuat objek MutableState<T> yang menyimpan nilai dan dapat memberi tahu Compose Runtime tentang perubahan. Tanda tangan: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. Parameter policy menentukan kapan perubahan dianggap signifikan — saat kesetaraan struktural, kesetaraan referensial, atau tidak pernah.
MutableState adalah antarmuka dengan satu properti value: getter untuk membaca dan setter untuk menulis. Saat setter dipanggil, Compose Runtime mencatat perubahan dalam snapshot dan menandai semua fungsi Composable yang membaca variabel State ini sebagai memerlukan rekomposisi. Proses ini terjadi secara sinkron dalam satu siklus snapshot, yang menghilangkan status antara saat perubahan berantai.
Parameter policy — argumen kedua dari mutableStateOf, yang menentukan perilaku saat perbandingan. structuralEquality() memeriksa equals() — ini adalah perilaku default. referentialEquality() memeriksa === (kesetaraan referensial). neverEqual() menganggap setiap penugasan sebagai perubahan. Pemilihan policy memengaruhi apakah rekomposisi akan dimulai saat menetapkan nilai yang sama.
Cara paling sederhana untuk mendeklarasikan status yang dapat diamati adalah menggunakan mutableStateOf dengan remember. Tanpa remember, setiap rekomposisi akan membuat State baru dan semua perubahan sebelumnya akan hilang. remember menjamin bahwa MutableState yang sama bertahan melalui serangkaian rekomposisi selama fungsi Composable tetap dalam komposisi.
@Composable
fun Counter() {
// Tanpa delegasi: membaca/menulis melalui .value
val count = remember { mutableStateOf(0) }
Button(onClick = { count.value++ }) {
Text("Count: ${count.value}")
}
}
@Composable
fun CounterDelegated() {
// Dengan delegasi: var + by = Property Delegation
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
Perbedaan antara kedua varian bersifat sintaksis. Property Delegation (by) menggunakan konvensi Kotlin: kompiler menghasilkan panggilan getValue() dan setValue() untuk membaca dan menulis. Ini setara dengan akses langsung ke count.value, tetapi terlihat seperti bekerja dengan variabel biasa. Kedua varian secara fungsional identik: Compose melacak pembacaan di getter dan penulisan di setter terlepas dari bentuk penulisan.
| Bentuk | Kode | Membaca | Menulis |
|---|---|---|---|
| Tanpa delegasi | val count = mutableStateOf(0) | count.value | count.value = n |
| Dengan delegasi | var count by mutableStateOf(0) | count | count = n |
Mekanisme properti yang didelegasikan Kotin bukanlah fitur Compose, melainkan kemampuan bawaan bahasa. Kelas apa pun dapat mengimplementasikan operator getValue(thisRef, property) dan setValue(thisRef, property, value), setelah itu instance-nya dapat digunakan dengan kata kunci by. MutableState bekerja persis seperti ini: getValue mengembalikan nilai saat ini, dan setValue menetapkan nilai baru.
Perbedaan penting: val dan var. mutableStateOf dapat ditetapkan ke val maupun var. Dalam kasus val (val count = mutableStateOf(0)), objek MutableState itu sendiri tidak dapat diubah, tetapi properti value-nya dapat diubah. Dalam kasus var (var count by mutableStateOf(0)), delegasi menciptakan ilusi bekerja dengan primitif, tetapi sebenarnya setter memanggil setValue pada MutableState. Pilihan antara val dan var adalah pilihan antara akses eksplisit dan implisit ke .value.
State Delegation adalah gula sintaksis yang menyederhanakan kode, tetapi tidak mengubah mekanika. Kompiler Kotlin mentranspilasi var x by mutableStateOf(0) menjadi getter/setter yang memanggil mutableStateOf.getValue() dan mutableStateOf.setValue(). Dalam bytecode yang dihasilkan, tidak ada perbedaan antara val dan var dengan by — keduanya bekerja melalui wadah MutableState yang sama.
// Delegasi kustom untuk State Compose
class ValidatedState<T>(initialValue: T) {
private val state = mutableStateOf(initialValue)
operator fun getValue(thisRef: Any?, property: KProperty<*>) = state.value
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
if (value != state.value) {
state.value = value
}
}
}
@Composable
fun Test() {
var text by remember { ValidatedState("") }
}
Snapshot adalah mekanisme Compose Runtime yang menjamin konsistensi pembacaan State saat perubahan paralel. Ketika fungsi Composable membaca mutableStateOf, snapshot mencatat nilai saat ini. Jika selama komposisi perubahan lain menulis ke State yang sama, snapshot melihat penulisan tetapi tidak mengizinkan pembacaan data yang tidak konsisten — pembacaan selalu mengembalikan nilai yang terkini pada saat snapshot dimulai.
Saat setter mutableStateOf.value = newValue dipanggil, Compose Runtime tidak segera memulai rekomposisi. Sebagai gantinya, perubahan dicatat dalam snapshot saat ini. Ketika snapshot diterapkan (di batas frame), Compose menelusuri daftar State yang diubah dan menandai komponen yang membaca sebagai Invalid. Hanya di frame berikutnya rekomposisi dimulai. Ini menjamin bahwa UI tidak digambar ulang puluhan kali saat perubahan berantai.
Snapshot global dan lokal: secara default mutableStateOf bekerja dalam snapshot global yang diterapkan secara otomatis. Snapshot lokal dapat dibuat melalui Snapshot.takeSnapshot() untuk pembacaan terisolasi tanpa efek samping. Ini digunakan di dalam Modifier, di mana State perlu dibaca tetapi tidak perlu berlangganan perubahan. Pendekatan ini mengoptimalkan kinerja dan mencegah rekomposisi yang tidak terduga.
Mari kita lihat skenario nyata — formulir login dengan tiga bidang: email, kata sandi, dan status pemuatan. Ketiga bidang menggunakan mutableStateOf, tetapi dengan policy dan tingkatan bersarang yang berbeda. email menggunakan delegasi, kata sandi — akses langsung.
data class LoginState(
val email: String = "",
val password: String = "",
val isLoading: Boolean = false,
val error: String? = null
)
@Composable
fun LoginForm(onLogin: (String, String) -> Unit) {
// State tunggal untuk formulir, policy = referentialEquality
var formState by remember {
mutableStateOf(LoginState(), SnapshotMutationPolicy.referentialEquality())
}
val isValid = remember(formState) {
formState.email.contains("@") && formState.password.length() >= 6
}
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = formState.email,
onValueChange = { formState = formState.copy(email = it) },
label = { Text("Email") }
)
OutlinedTextField(
value = formState.password,
onValueChange = { formState = formState.copy(password = it) },
label = { Text("Kata Sandi") },
visualTransformation = PasswordVisualTransformation()
)
Button(
onClick = { onLogin(formState.email, formState.password) },
enabled = isValid
) {
Text("Masuk")
}
}
}
Dalam contoh ini, mutableStateOf digunakan dengan kelas data kustom LoginState dan policy referentialEquality. Ini berarti rekomposisi hanya akan dimulai saat instance baru LoginState ditetapkan melalui copy(). isValid dihitung berdasarkan formState dan hanya dihitung ulang saat berubah. Pendekatan ini memberikan kontrol yang jelas atas rekomposisi: setiap bidang formulir hanya berubah dengan membuat salinan baru.
Pertanyaan yang Sering Diajukan
mutableStateOf adalah wadah khusus Compose yang bekerja di dalam snapshot. StateFlow berasal dari kotlinx.coroutines.flow dan tidak terikat pada Compose. mutableStateOf secara otomatis memulai rekomposisi, StateFlow memerlukan collectAsState(). Untuk status UI di dalam Composable, mutableStateOf lebih disukai.
Ya, mutableStateOf dapat dipanggil di luar fungsi Composable, tetapi tidak akan dilacak. Untuk reaktivitas di UI, State harus dibaca di dalam Composable. Banyak ViewModel menggunakan MutableStateField (pembungkus di atas mutableStateOf) untuk mengirim status ke UI melalui StateFlow.
Snapshot sistem menjamin konsistensi: setiap rekomposisi melihat status yang konsisten pada saat snapshot dimulai. Perubahan dari thread yang berbeda diterapkan secara atomik di batas frame, yang menghilangkan race condition saat pembacaan dalam satu komposisi.
Tetapkan nilai baru: count.value = 0 (atau count = 0 dengan delegasi). Jika perlu pembuatan ulang State secara lengkap — gunakan remember dengan kunci: remember(key) { mutableStateOf(initial) } — saat kunci berubah, State akan dibuat ulang.
Composer menggunakan snapshot yang mengelompokkan perubahan: bahkan dengan seratus penugasan dalam satu frame, rekomposisi hanya dilakukan satu kali. Untuk pembaruan yang sangat sering (animasi), gunakan Animatable atau animate*AsState — keduanya dioptimalkan untuk pembaruan per frame.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga