mutableStateOf: креирање посматрачког стања и реактивност Compose-а

Аутор: IT Sectr Објављено: 2026-06-28 Време читања: 7 мин

mutableStateOf је функција у Jetpack Compose-у која креира контејнер променљивог посматрачког стања. Када се вредност унутар овог контејнера промени, Compose аутоматски покреће рекомпозицију свих компоненти које читају ово стање. Без mutableStateOf, UI не би могао реактивно да се ажурира при променама података. Према Google Android Developers, 2026, mutableStateOf је основни градивни блок за локално стање у Compose-у.

Главно

  • mutableStateOf креира контејнер MutableState који прати Compose Runtime
  • Рекомпозиција се аутоматски покреће при промени value овог State објекта
  • Делегирање преко var омогућава коришћење mutableStateOf без позивања .value
  • Кључеви у remember(mutableStateOf) нису потребни — State сам обавештава Compose о променама
  • Snapshot систем гарантује конзистентност читања у вишедимензионалном окружењу

Шта је mutableStateOf у Jetpack Compose-у

mutableStateOf је функција из пакета compose.runtime која креира објекат MutableState<T> који чува вредност и може да обавести Compose Runtime о променама. Сигнатура: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. Параметар policy одређује када се промена сматра значајном — при структурној једнакости, при референцијалној једнакости или никад.

MutableState је интерфејс са јединственим својством value: getter за читање и setter за писање. При позиву setter-а, Compose Runtime бележи промену у снимку (snapshot) и означава све Composable функције које читају ову State променљиву као оне које захтевају рекомпозицију. Овај процес се одвија синхроно у оквиру једног snapshot циклуса, што елиминише међустања при каскадним променама.

Parameter policy — други аргумент mutableStateOf, који одређује понашање при поређењу. structuralEquality() проверава equals() — ово је подразумевано понашање. referentialEquality() проверава === (референцијална једнакост). neverEqual() сматра свако додељивање променом. Избор policy утиче на то да ли ће се рекомпозиција покренути при додељивању исте вредности.

Синтакса и начини декларисања mutableStateOf

Најједноставнији начин да се декларише посматрачко стање је коришћење mutableStateOf са remember. Без remember, при свакој рекомпозицији би се креирао нови State и сва претходна промене би се изгубиле. remember гарантује да исти MutableState преживи низ рекомпозиција док Composable функција остаје у саставу композиције.

kotlin
@Composable
fun Counter() {
    // Без делегирања: читање/писање преко .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

@Composable
fun CounterDelegated() {
    // Са делегирањем: var + by = Property Delegation
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Count: $count")
    }
}

Разлика између две варијанте је синтаксна. Property Delegation (by) користи Kotlin конвенцију: компајлер генерише позиве getValue() и setValue() за читање и писање. Ово је еквивалентно директном приступу count.value, али изгледа као рад са обичном променљивом. Обе варијанте су функционално идентичне: Compose прати читање у getter-у и писање у setter-у без обзира на форму записа.

ФормаКодЧитањеПисање
Без делегирањаval count = mutableStateOf(0)count.valuecount.value = n
Са делегирањемvar count by mutableStateOf(0)countcount = n

Делегирана својства и var

Механизам делегираних својстава Kotlin-а није особина Compose-а, већ уграђена могућност језика. Било која класа може имплементирати операторе getValue(thisRef, property) и setValue(thisRef, property, value), након чега се њена инстанца може користити са кључном речи by. MutableState управо тако ради: getValue враћа тренутну вредност, а setValue додељује нову.

Важна разлика: val и var. mutableStateOf се може доделити и val и var. У случају val (val count = mutableStateOf(0)) сам MutableState објекат је непроменљив, али његово својство value се може мењати. У случају var (var count by mutableStateOf(0)) делегирање ствара илузију рада са примитивом, али у ствари setter позива setValue на MutableState. Избор између val и var је избор између експлицитног и имплицитног приступа .value.

State Delegation је синтаксни шећер који поједностављује код, али не мења механику. Kotlin компајлер преводи var x by mutableStateOf(0) у getter/setter који позивају mutableStateOf.getValue() и mutableStateOf.setValue(). У генерисаном бајткоду нема разлике између val и var са by — оба раде преко истог MutableState контејнера.

kotlin
    // Прилагођено делегирање за Compose State
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 систем: како mutableStateOf ради изнутра

Snapshot је механизам Compose Runtime-а који обезбеђује конзистентност читања State при паралелним променама. Када Composable функција чита mutableStateOf, снимак бележи тренутну вредност. Ако током композиције друга промена упише у исти State, снимак види упис, али не дозвољава читање неконзистентних података — читање увек враћа вредност актуелну у тренутку почетка снимка.

При позиву setter-а mutableStateOf.value = newValue, Compose Runtime не покреће одмах рекомпозицију. Уместо тога, промена се региструје у тренутном снимку. Када се снимак примени (на граници оквира), Compose пролази кроз листу измењених State и означава компоненте које читају као Invalid. Тек у следећем оквиру се покреће рекомпозиција. Ово гарантује да UI не буде прецртан десетине пута при каскадним променама.

Глобални и локални снимци: подразумевано mutableStateOf ради у глобалном снимку који се примењује аутоматски. Може се креирати локални снимак путем Snapshot.takeSnapshot() за изоловано читање без споредних ефеката. Ово се користи унутар Modifier-а, где је потребно прочитати State, али не и претплатити се на промене. Овај приступ оптимизује перформансе и спречава неочекиване рекомпозиције.

Примери коришћења mutableStateOf

Размотримо реалан сценарио — формулар за пријаву са три поља: емаил, лозинка и статус учитавања. Сва три поља користе mutableStateOf, али са различитим policy и различитим угњеждавањем. емаил користи делегирање, лозинка — директан приступ.

kotlin
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 за формулар, 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("Емаил") }
        )
        OutlinedTextField(
            value = formState.password,
            onValueChange = { formState = formState.copy(password = it) },
            label = { Text("Лозинка") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("Пријава")
        }
    }
}

У овом примеру mutableStateOf се користи са прилагођеном data класом LoginState и policy referentialEquality. То значи да ће се рекомпозиција покренути само при додељивању нове инстанце LoginState кроз copy(). isValid се израчунава на основу formState и прерачунава се само при његовој промени. Овај приступ даје јасну контролу над рекомпозицијама: свако поље форме се мења само креирањем нове копије.

Често постављана питања

По чему се mutableStateOf разликује од StateFlow-а?

mutableStateOf је контејнер специфичан за Compose који ради унутар снимака. StateFlow потиче из kotlinx.coroutines.flow и није везан за Compose. mutableStateOf аутоматски покреће рекомпозицију, StateFlow захтева collectAsState(). За UI стање унутар Composable-а пожељнији је mutableStateOf.

Може ли се mutableStateOf користити ван @Composable функција?

Да, mutableStateOf се може позвати ван Composable функције, али неће бити праћен. За рад реактивности у UI-ју потребно је читати State унутар Composable-а. Многе ViewModel класе користе MutableStateField (омотач око mutableStateOf) за пренос стања у UI кроз StateFlow.

Шта се дешава при истовременој промени State из две нити?

Snapshot систем гарантује конзистентност: свака рекомпозиција види усклађено стање у тренутку почетка снимка. Промене из различитих нити се примењују атомично на граници оквира, што елиминише race condition при читању у оквиру једне композиције.

Како ресетовати mutableStateOf на почетну вредност?

Доделите нову вредност: count.value = 0 (или count = 0 при делегирању). Ако је потребно потпуно поновно креирање State-а — користите remember са кључем: remember(key) { mutableStateOf(initial) } — при промени кључа State ће бити поново креиран.

Да ли mutableStateOf утиче на перформансе при честим променама?

Composer користи снимке који групишу промене: чак и са сто додељивања у једном оквиру, рекомпозиција се извршава само једном. За веома честа ажурирања (анимације) користите Animatable или animate*AsState — оптимизовани су за ажурирања по оквирима.

Закључак

  • mutableStateOf креира посматрачки контејнер MutableState који прати Compose Runtime
  • Делегирање преко by поједностављује код, али не мења механику рада State-а
  • Snapshot систем гарантује конзистентност читања и спречава непотребне рекомпозиције
  • Policy одређује када се промена сматра значајном — structuralEquality, referentialEquality, neverEqual
  • remember је обавезан за очување State између рекомпозиција унутар Composable-а
  • Препорука: користите mutableStateOf са делегирањем за UI стање и referentialEquality за data класе
  • Избегавајте: креирање mutableStateOf без remember — свако додељивање ће креирати нови објекат

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође