Поток состояния, представленный интерфейсом StateFlow, применяется как способ наблюдения за изменением состояния в приложении. Каждый объект StateFlow используется для хранения одного значения, которое может меняться со временем, а также для уведомления системы об изменении этого значения. StateFlow ведет себя так же, как LiveData, за исключением того, что LiveData учитывает жизненный цикл и не требует начального значения.
Для создания объекта StateFlow можно использовать встроенную функцию MutableStateFlow(), в которую передается обязательное начальное значение и которая возвращает объект одноименного типа:
private val _stateFlow = MutableStateFlow(0)
Далее эта переменная будет использоваться для изменения текущего значения состояния из кода приложения. Затем для преобразование в StateFlow у объекта MutableStateFlow вызывается метод asStateFlow():
val stateFlow = _stateFlow.asStateFlow()
Для изменения состояния измненеия вносятся через свойство value объекта MutableStateFlow:
_stateFlow.value += 1
Когда поток активен, состояние можно использовать с помощью метода collectAsState() или напрямую с помощью функции collection()
или collectLatest()
.
Рассмотрим небольшой пример:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Button import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import androidx.compose.material3.Text import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.sp import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class MainActivity : ComponentActivity() { private val _stateFlow = MutableStateFlow(0) val stateFlow = _stateFlow.asStateFlow() fun increase() { _stateFlow.value += 1 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Main(flow=stateFlow, onIncrease = ::increase) } } } @Composable fun Main(flow: StateFlow<Int>, onIncrease: ()->Unit) { val count by flow.collectAsState() Column(Modifier.fillMaxSize()) { Text("$count", fontSize = 44.sp) Button(onClick = { onIncrease() }) { Text("Increase", fontSize = 22.sp) } } }
Здесь определяем состояние потока StateFlow в виде переменных _stateFlow и stateFlow, а также определяет функцию increase() для изменения состояния.
Компонент Main получает текущее значение потока в переменную count с помощью метода stateFlow.collectAsState()
. Для вывода текущего значения определен компонент Text. А компонент
Button позволяет по нажатию запустить функциюincrease()
, тем самым изменяя значение в потоке.
И в принципе мы могли бы все то же самое сделать и без StateFlow, ограничившись просто переменной состояния. Но StateFlow позволяет нам потреблять новые значения именно как поток. Например, изменим приложение следующим образом:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Button import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class MainActivity : ComponentActivity() { private val _stateFlow = MutableStateFlow(0) val stateFlow = _stateFlow.asStateFlow() fun increase() { _stateFlow.value += 1 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Main(flow=stateFlow, onIncrease = ::increase) } } } @Composable fun Main(flow: StateFlow<Int>, onIncrease: ()->Unit) { val count by flow.collectAsState() val messages = remember { mutableStateListOf<Int>()} LaunchedEffect(Unit) { flow.collect { messages.add(it) } } Column(Modifier.fillMaxSize()) { Text("$count", fontSize = 44.sp) Button(onClick = { onIncrease() }) { Text("Increase", fontSize = 22.sp) } LazyColumn { items(messages) { Text("Collected Value = $it",Modifier.padding(5.dp)) } } } }
Теперь в компоненте Main также определена еще одна переменная состояния - messages, которая хранит некоторый набор сообщений, связанных с состоянием stateFlow:
val messages = remember { mutableStateListOf<Int>()}
С помощью дополнительного компонента LaunchedEffect запускаем метод collect(), который собирает все новые зиначения из потока и добавляет их в список messages
LaunchedEffect(Unit) { flow.collect { messages.add(it) } }
В итоге при изменения значения по нажатию на кнопку также будет пополняться список messages, который выводится в LazyColumn: