MutableState, Jetpack Compose'da değiştirilebilir gözlemlenebilir bir değer için kapsayıcıyı temsil eden bir arayüzdür. Compose'un tepkisel sisteminin temelidir: MutableState değeri setter aracılığıyla her değiştiğinde, Compose Runtime tüm okuyan bileşenlere bildirim gönderir ve yeniden oluşturmayı başlatır. Google Android Developers, 2026'ya göre, bildirimsel bir UI'da durumla doğru çalışmak için MutableState'i anlamak zorunludur.
Önemli noktalar
MutableState, androidx.compose.runtime paketinden tek bir özellik bildiren bir arayüzdür: override var value: T. Getter mevcut değeri döndürür, setter yeni bir değer yazar ve Compose Runtime'a değişikliği bildirir. Arayüz, value'nun salt okunur olduğu State<T>'den miras alır. Bu iki seviyeli mimari, erişim ayrımına izin verir: yalnızca değeri okuması gereken bir bileşen State<T> alırken, sahip bileşen MutableState<T> alır.
MutableState'in varsayılan uygulaması, değişiklikleri izlemek için bir snapshot mekanizması kullanan iç sınıf SnapshotMutableStateImpl'dir. Value setter'ı çağrıldığında, geçerli snapshot yazmayı kaydeder ve kayıtlı tüm ObservedScope'ları geçersiz olarak işaretler. Bu kapsamlar (genellikle Composable işlevleri) sonraki karede yeniden oluşturulacaktır. Lock-free snapshot mimarisi sayesinde tüm süreç eşzamanlı ve kilit olmadan gerçekleşir.
State vs MutableState: State, bileşenlerin genel API'leri için kullanılan salt okunur bir arayüzdür. Bir Composable işlev parametresini State<Int> olarak bildirdiğinizde, bileşenin durumu okuyabileceğini ancak değiştiremeyeceğini garanti edersiniz. MutableState, sahip bileşen içinde kullanılır. Bu ayrım, yetkisiz değişiklikleri önleyen temel Compose uygulamalarından biridir.
Compose'daki durum arayüzleri hiyerarşisinin birkaç seviyesi vardır. En üstte salt okunur value'ya sahip State<T> bulunur. Altında okuma-yazma value'ya sahip MutableState<T> bulunur. Daha aşağıda, ilkel boxing'den kaçınan özelleşmiş ilkel sürümler gelir: MutableIntState, MutableFloatState, MutableLongState, MutableBooleanState ve diğerleri.
MutableDoubleState ve MutableLongState daha az yaygındır ancak mevcuttur. Koleksiyon arayüzleri: MutableListState — bir liste içindeki değişiklikleri izlemek için, MutableStateMap — haritalar için. Bu arayüzlerin her biri belirli bir senaryo için optimize edilmiştir ve temel MutableState'i ek koleksiyon işleme yöntemleriyle genişletir.
SnapshotStateList ve SnapshotStateMap, snapshot'larla uyumlu değiştirilebilir liste ve haritaların uygulamalarıdır. Yalnızca değer değiştirmeyi değil, aynı zamanda iç değişiklikleri de izlemeye izin verirler: listeye öğe ekleme, silme, mevcut bir öğeyi değiştirme. Bu tür yapılar için, mutableStateListOf() ve mutableStateMapOf() ilgili gözlemlenebilir koleksiyonları oluşturur.
| Arayüz | Amaç | Oluşturma yöntemi |
|---|---|---|
| State<T> | Salt okunur kapsayıcı | — |
| MutableState<T> | Okuma-yazma kapsayıcı | mutableStateOf() |
| MutableIntState | Boxing olmadan ilkel Int | mutableIntStateOf() |
| MutableFloatState | Boxing olmadan ilkel Float | mutableFloatStateOf() |
| SnapshotStateList | Gözlemlenebilir liste | mutableStateListOf() |
| SnapshotStateMap | Gözlemlenebilir harita | mutableStateMapOf() |
SnapshotMutationPolicy, MutableState'deki bir değişikliğin ne zaman önemli kabul edildiğini belirleyen bir arayüzdür. mutableStateOf, ikinci argüman olarak policy'yi kabul eder. Standart uygulamalar: structuralEquality() (equals), referentialEquality() (===), neverEqualPolicy() (değişikliği her zaman önemli kabul eder). Özel mantık için kendi politikanızı uygulayabilirsiniz.
structuralEquality() — varsayılan davranış. Compose, yeni değeri eskisiyle equals() aracılığıyla karşılaştırır. Sonuç true ise, yeniden oluşturma TETİKLENMEZ. Bu, aynı alanlara sahip iki örneğin eşit kabul edildiği ilkeller ve data class'lar için kullanışlıdır. Sorun: bir data class bir List içeriyorsa, equals() derin bir karşılaştırma yapar ve bu büyük listeler için maliyetli olabilir.
referentialEquality() — === aracılığıyla referansları karşılaştırır. Yeniden oluşturma, içerik aynı olsa bile yalnızca farklı bir nesne atandığında tetiklenir. Bu, her yeni örneğin bir değişikliği garanti ettiği değiştirilemez data class'lar için idealdir. neverEqualPolicy() — karşılaştırma yapmadan değişikliği her zaman önemli kabul eder. Setter'ın nadiren çağrıldığı ve equals için zaman harcamaya gerek olmadığı durumlarda kullanışlıdır.
// Policy comparison in practice
data class User(val name: String, val age: Int)
@Composable
fun UserProfile() {
// structuralEquality: recomposition ONLY if data changed
var user1 by remember {
mutableStateOf(User("Alice", 30))
}
// referentialEquality: recomposition on ANY assignment
var user2 by remember {
mutableStateOf(User("Bob", 25),
SnapshotMutationPolicy.referentialEquality())
}
// user1: copy() with same fields does NOT trigger recomposition
// user2: even user2.copy() == user2 triggers recomposition (new ref)
}
İlkel MutableIntState ve benzerleri, boxing olmadan ilkelleri depolayan özelleşmiş arayüzlerdir. Normal bir MutableState<Int>, Int'i Integer olarak depolar ve her yazmada yığında bir nesne oluşturur. MutableIntState, int (ilkel) depolar ve boxing yükünü tamamen ortadan kaldırır. Bu, yüksek frekanslı güncellemeler için özellikle önemlidir — sayaçlar, kaydırma konumları, animasyon değerleri.
mutableIntStateOf(), mutableFloatStateOf(), mutableLongStateOf() — ilkel MutableState oluşturan işlevler. Arayüzler MutableIntState, MutableFloatState, MutableLongState olarak adlandırılır. Sırasıyla MutableState<Int>, MutableState<Float> ve MutableState<Long>’i genişletir ve ilkel hızlı erişim için intValue özelliğini ekler. İç uygulamaları, kilitsiz okuma/yazma için AtomicInteger kullanır.
Kullanım: sayaçlar (Int), kaydırma konumları (Float offset), zaman damgaları (Long). Çoğu günlük senaryoda performans farkı fark edilmez, ancak binlerce öğe ve geçiş animasyonuna sahip LazyList'te ilkel State, belirgin bir iyileştirme sağlar. Google, evrensel mutableStateOf yerine tipik senaryolar için ilkel State kullanılmasını önerir.
@Composable
fun ScrollCounter() {
// Bad: boxing on every update
var badCount by remember { mutableStateOf(0) }
// Good: no boxing, primitive storage
var goodCount by remember { mutableIntStateOf(0) }
// Usage is identical
Button(onClick = { goodCount++ }) {
Text("Count: $goodCount")
}
}
Bir TodoList bileşenini düşünün. Burada MutableState iki biçimde kullanılır: giriş durumu için ayrı değişkenler ve dinamik bir görev listesi için SnapshotStateList olarak. Her ikisi de kod kısalığı için yetkilendirme kullanır.
data class TodoItem(val id: Int, val text: String, val isDone: Boolean = false)
@Composable
fun TodoScreen() {
var inputText by remember { mutableStateOf("") }
val items = remember { mutableStateListOf() }
Column(modifier = Modifier.padding(16.dp)) {
Row {
TextField(
value = inputText,
onValueChange = { inputText = it }
)
Button(onClick = {
if (inputText.isNotBlank()) {
items.add(TodoItem(items.size, inputText))
inputText = ""
}
}) { Text("Add") }
}
LazyColumn {
items(items) { item ->
Row(modifier = Modifier.fillMaxWidth().clickable {
val idx = items.indexOf(item)
items[idx] = item.copy(isDone = !item.isDone)
}) {
Checkbox(checked = item.isDone, onCheckedChange = null)
Text(item.text)
}
}
}
}
}
mutableStateListOf bir SnapshotStateList oluşturur — tek tek öğelerdeki değişiklikleri izleyen değiştirilebilir bir liste. items.add() ve items[n] = newValue çağrıldığında, Compose mutasyonu görür ve yalnızca değişen LazyColumn öğelerini yeniden oluşturur. inputText, normal bir MutableState<String>’dir. İki MutableState türünün (tek ve koleksiyon) kombinasyonu, formlar ve listeler içeren ekranlar için tipik bir kalıptır.
Sıkça sorulan sorular
MutableState remember olmadan her yeniden oluşturmada yeniden oluşturulur. mutableStateOf'a yapılan her yeni çağrı yeni bir nesne oluşturur ve eski değer kaybolur. State, Composable dışında (örneğin ViewModel'de) oluşturulmadıkça, yeniden oluşturmalar arasında State'i korumak için her zaman remember kullanın.
Snapshot { } aracılığıyla bir snapshot dışında .value'yu bir kez okuyun. Ancak bu tepkiselliği devre dışı bırakır — değişiklikler artık yeniden oluşturmayı tetiklemez. Abonelik olmadan tek seferlik okuma için, okuma yapmadan bir snapshot içinde currentValue() kullanın.
mutableIntStateOf daha hızlıdır çünkü int'i Integer'a boxing yapmayı gerektirmez. Saniyede binlerce güncelleme (animasyon, kaydırma) ile fark, ayırma süresinde %30-50'ye ulaşabilir. Seyrek güncellemeler (tıklamalar, metin girişi) için fark ihmal edilebilir düzeydedir.
Mümkündür ancak önerilmez. MutableState yerine, State (salt okunur) + bir onValueChange lambda'sı geçirin. Bu, State Hoisting kalıbını uygular ve bileşeni yeniden kullanılabilir hale getirir. MutableState kabul eden bileşenler, tek yönlü veri akışını ihlal eder.
MutableState arayüzünü uygulayın ve bir getter ve setter ile override var value sağlayın. Setter'da doğrulama veya günlükleme ekleyebilirsiniz. Compose Runtime ile geriye dönük uyumluluk için, özel uygulamanızı snapshotFlow içine sarın veya snapshotIncrement kullanın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun