MutableState — pozorovatelný stav a mechanismus aktualizace v Compose

Autor: IT Sectr Publikováno: 2026-06-28 Doba čtení: 7 min

MutableState — je rozhraní v Jetpack Compose, které představuje kontejner pro měnitelnou pozorovatelnou hodnotu. Je základem reaktivního systému Compose: pokaždé, když se hodnota MutableState změní přes setter, Compose Runtime upozorní všechny čtoucí komponenty a spustí rekompozici. Podle Google Android Developers, 2026 je porozumění MutableState povinné pro správnou práci se stavem v deklarativním UI.

Hlavní body

  • MutableState — rozhraní compose.runtime s jednou vlastností value (getter + setter)
  • State — nadřazené rozhraní pouze pro čtení, MutableState přidává možnost zápisu
  • Rekompozice se spouští při volání setteru value uvnitř aktivního snapshot cyklu
  • SnapshotMutationPolicy určuje, kdy je změna považována za významnou pro rekompozici
  • MutableIntState a analogy — optimalizované primitivní verze MutableState

Co je MutableState v Jetpack Compose

MutableState — je rozhraní z balíčku androidx.compose.runtime, které deklaruje jednu vlastnost: override var value: T. Getter vrací aktuální hodnotu, setter zapisuje novou a informuje Compose Runtime o změně. Rozhraní dědí od State<T>, kde je value přístupný pouze pro čtení. Taková dvouvrstvá architektura umožňuje oddělení přístupu: komponenta, která potřebuje pouze číst hodnotu, dostává State<T>, a komponenta-vlastník — MutableState<T>.

Výchozí implementací MutableState je vnitřní třída SnapshotMutableStateImpl, která využívá mechanismus snapshotů ke sledování změn. Když je setter value zavolán, aktuální snapshot zaznamená zápis a označí všechny registrované ObservedScope (oblasti pozorování) jako neplatné. Tyto oblasti (obvykle Composable funkce) budou rekomponovány v dalším snímku. Celý proces probíhá synchronně a bez blokování díky architektuře Lock-free snapshot.

State vs MutableState: State — je rozhraní pouze pro čtení, používané pro veřejná API komponent. Když deklarujete parametr Composable funkce jako State<Int>, garantujete, že komponenta může číst, ale neměnit stav. MutableState se používá uvnitř komponenty-vlastníka. Takové oddělení — jeden ze základních postupů Compose — zabraňuje neautorizovaným změnám.

Hierarchie State, MutableState a odvozených rozhraní

Hierarchie rozhraní stavu v Compose má několik úrovní. Na vrcholu — State<T> s value pouze pro čtení. Níže — MutableState<T> s value pro čtení i zápis. Dále následují specializované primitivní verze: MutableIntState, MutableFloatState, MutableLongState, MutableBooleanState a další, které se vyhýbají autoboxingu primitiv.

MutableDoubleState a MutableLongState — méně rozšířené, ale existující typy. Rozhraní kolekcí: MutableListState — pro sledování změn uvnitř seznamu, MutableStateMap — pro mapy. Každé z těchto rozhraní je optimalizováno pro konkrétní scénář a rozšiřuje základní MutableState o další metody pro práci s kolekcí.

SnapshotStateList a SnapshotStateMap — jsou implementace měnitelných seznamů a map kompatibilní se snapshoty. Umožňují sledovat nejen nahrazení hodnoty, ale i vnitřní změny: přidání prvku do seznamu, smazání, změnu existujícího prvku. Pro takové struktury vytvářejí mutableStateListOf() a mutableStateMapOf() odpovídající pozorovatelné kolekce.

RozhraníÚčelMetoda vytvoření
State<T>Kontejner pouze pro čtení
MutableState<T>Kontejner pro čtení a zápismutableStateOf()
MutableIntStatePrimitivní Int bez boxingumutableIntStateOf()
MutableFloatStatePrimitivní Float bez boxingumutableFloatStateOf()
SnapshotStateListPozorovatelný seznammutableStateListOf()
SnapshotStateMapPozorovatelná mapamutableStateMapOf()

SnapshotMutationPolicy: kdy spustit rekompozici

SnapshotMutationPolicy — je rozhraní určující, kdy je změna MutableState považována za významnou. mutableStateOf přijímá policy jako druhý argument. Standardní implementace: structuralEquality() (equals), referentialEquality() (===), neverEqualPolicy() (vždy považuje změnu za významnou). Pro vlastní logiku můžete implementovat svůj vlastní policy.

structuralEquality() — výchozí chování. Compose porovnává novou hodnotu se starou přes equals(). Pokud je výsledek true — rekompozice se NESPOUŠTÍ. To je výhodné pro primitivy a data classy, kde jsou dvě instance se stejnými poli považovány za stejné. Problém: pokud data class obsahuje List, equals() provádí hluboké porovnání, což může být nákladné pro velké seznamy.

referentialEquality() — porovnává reference přes ===. Rekompozice se spouští pouze při přiřazení jiného objektu, i když je obsah identický. To je optimální pro neměnné data classy, kde každá nová instance zaručeně znamená změnu. neverEqualPolicy() — vždy považuje změnu za významnou, aniž by prováděla porovnání. Užitečné, když je setter volán zřídka a není třeba ztrácet čas equals.

kotlin
    // Porovnání politik v praxi
data class User(val name: String, val age: Int)

@Composable
fun UserProfile() {
    // structuralEquality: rekompozice POUZE pokud se data změnila
    var user1 by remember {
        mutableStateOf(User("Alice", 30))
    }

    // referentialEquality: rekompozice při KAŽDÉM přiřazení
    var user2 by remember {
        mutableStateOf(User("Bob", 25),
            SnapshotMutationPolicy.referentialEquality())
    }

    // user1: copy() se stejnými poli NESPOUŠTÍ rekompozici
    // user2: i user2.copy() == user2 spouští rekompozici (nová ref)
}

Primitivní State: MutableIntState, MutableFloatState, MutableLongState

Primitivní MutableIntState a analogy — jsou specializovaná rozhraní, která ukládají primitivy bez autoboxingu. Běžný MutableState<Int> ukládá Int jako Integer, což při každém zápisu vytváří objekt na haldě. MutableIntState ukládá int (primitiv), zcela eliminující režii balení. To je důležité zejména při vysokofrekvenčních aktualizacích — čítače, pozice posouvání, animační hodnoty.

mutableIntStateOf(), mutableFloatStateOf(), mutableLongStateOf() — funkce vytvářející primitivní MutableState. Rozhraní se nazývají MutableIntState, MutableFloatState, MutableLongState. Rozšiřují MutableState<Int>, MutableState<Float> a MutableState<Long> příslušně, přidávají vlastnost intValue jako rychlý přístup k primitivu. V jejich vnitřní implementaci se používá AtomicInteger pro lock-free čtení/zápis.

Použití: čítače (Int), pozice posouvání (Float offset), časová razítka (Long). Ve většině každodenních scénářů je rozdíl ve výkonu nepostřehnutelný, ale v LazyList s tisíci prvky a přechodovými animacemi poskytují primitivní State znatelné zlepšení. Google doporučuje používat primitivní State pro typické scénáře místo univerzálního mutableStateOf.

kotlin
@Composable
fun ScrollCounter() {
    // Špatně: balení při každé aktualizaci
    var badCount by remember { mutableStateOf(0) }

    // Dobře: žádné balení, primitivní úložiště
    var goodCount by remember { mutableIntStateOf(0) }

    // Použití je identické
    Button(onClick = { goodCount++ }) {
        Text("Count: $goodCount")
    }
}

Praktické příklady práce s MutableState

Podívejme se na komponentu TodoList, kde je MutableState použit ve dvou formách: jako samostatné proměnné pro stav vstupu a jako SnapshotStateList pro dynamický seznam úkolů. Obě používají delegování pro stručnost kódu.

kotlin
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("Přidat") }
        }

        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 vytváří SnapshotStateList — měnitelný seznam, který sleduje změny jednotlivých prvků. Při volání items.add() a items[n] = newValue Compose vidí mutaci a rekomponuje pouze ty prvky LazyColumn, které se změnily. inputText — běžný MutableState<String>. Kombinace dvou typů MutableState (jednotlivý a kolekce) — typický vzor pro obrazovky s formuláři a seznamy.

Často kladené otázky

Lze použít MutableState bez remember?

MutableState bez remember bude při každé rekompozici vytvořen znovu. Každé nové volání mutableStateOf vytváří nový objekt a stará hodnota se ztrácí. Vždy používejte remember pro uchování State mezi rekompozicemi, pokud není State vytvořen mimo Composable (např. ve ViewModelu).

Jak převést MutableState na běžnou proměnnou?

Přečtěte .value jednou mimo snapshot přes snapshot { }. To však vypíná reaktivitu — změny již nebudou spouštět rekompozici. Pro jednorázové čtení bez odběru použijte currentValue() uvnitř snapshot bez čtení.

Co je rychlejší: mutableStateOf nebo mutableIntStateOf?

mutableIntStateOf je rychlejší, protože nevyžaduje balení int do Integer. Při tisících aktualizací za sekundu (animace, posouvání) může rozdíl dosáhnout 30-50 % času alokace. Při vzácných aktualizacích (kliknutí, zadávání textu) je rozdíl zanedbatelný.

Lze použít MutableState jako argument Composable funkce?

Lze, ale nedoporučuje se. Místo MutableState předejte State (pouze pro čtení) + lambdu onValueChange. To implementuje vzor State Hoisting a činí komponentu znovupoužitelnou. Komponenty přijímající MutableState porušují jednosměrný tok dat.

Jak vytvořit vlastní implementaci MutableState?

Implementujte rozhraní MutableState a poskytněte override var value s getterem a setterem. V setteru můžete přidat validaci nebo logování. Pro zpětnou kompatibilitu s Compose Runtime zabalte svou implementaci do snapshotFlow nebo použijte snapshotIncrement.

Shrnutí

  • MutableState — základní rozhraní pro měnitelný pozorovatelný stav v Compose
  • State — verze pouze pro čtení pro přenos dat bez práva změny
  • SnapshotMutationPolicy řídí podmínky spuštění rekompozice při změně
  • Primitivní State (MutableIntState atd.) eliminují režii autoboxingu
  • SnapshotStateList a SnapshotStateMap sledují vnitřní změny kolekcí
  • Rekompozice se spouští automaticky při každém volání setteru value
  • Doporučení: používejte MutableState pro vlastnictví stavu a State pro předávání dolů

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také