Как обновить UI на Compose, если данные изменились?
В Jetpack Compose обновление UI происходит автоматически, если вы используете правильный тип данных и механизм наблюдения. Compose построен вокруг идеи декларативного UI, где интерфейс — это функция от состояния, и если состояние меняется, UI перерисовывается.
✅ Основные способы обновления UI при изменении данных
1. State и remember { mutableStateOf() }
Для локального состояния внутри Composable-функции:
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
Column {
Text(text = "Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
🔹 Когда count изменяется, Text() автоматически перерисовывается — UI обновляется.
2. ViewModel + MutableStateFlow / LiveData
Для глобального или screen-level состояния, которое хранится в ViewModel, используйте StateFlow или LiveData.
Пример с StateFlow:
class MyViewModel : ViewModel() {
private val \_userName = MutableStateFlow("Guest")
val userName: StateFlow<String> = \_userName
fun updateName(newName: String) {
\_userName.value = newName
}
}
В Compose:
@Composable
fun ProfileScreen(viewModel: MyViewModel = viewModel()) {
val name by viewModel.userName.collectAsState()
Column {
Text("Hello, $name")
Button(onClick = { viewModel.updateName("Alice") }) {
Text("Change Name")
}
}
}
🔹 collectAsState() подписывается на StateFlow, и как только значение изменяется — Composable перерисовывается.
3. derivedStateOf для вычисляемых значений
Если новое состояние зависит от другого, и вы хотите избежать лишних пересозданий:
val input = remember { mutableStateOf("") }
val isValid = remember {
derivedStateOf { input.value.length > 3 }
}
🔹 isValid обновляется только при изменении input.value.
4. rememberUpdatedState — для лямбд, чтобы не захватывать устаревшие значения
val onClickState = rememberUpdatedState(onClick)
LaunchedEffect(Unit) {
delay(1000)
onClickState.value() // всегда актуальный onClick
}
🚫 Что не обновит UI
Если вы используете обычную переменную:
var count = 0
count++ // UI не узнает, что count изменился
🔺 Так делать нельзя — Compose отслеживает только специальные типы состояния (State, StateFlow, LiveData и т.п.).
⚠️ Ошибки, которых стоит избегать
-
Изменение состояния вне Compose (например, viewModel.someList.add(item) без вызова mutableStateOf или обновления StateFlow).
-
Создание State в ViewModel, но без обёртки в collectAsState() в UI.
Изменение объекта по ссылке, не меняя саму ссылку:
<br/>val list = remember { mutableStateListOf<Int>() }
list.add(1) // UI не перерисуется, если вы используете обычный List
- 🔹 Используйте mutableStateListOf() или обновляйте State целиком.
📦 Обновление UI при изменении сложных структур
Для коллекций — используйте:
-
mutableStateListOf() / SnapshotStateList
-
mutableStateMapOf() / SnapshotStateMap
Или обновляйте весь список:
var items by remember { mutableStateOf(listOf<String>()) }
items = items + "New Item"
🧠 В двух словах
UI в Compose сам реагирует на изменения состояния, если:
-
Вы используете remember { mutableStateOf(...) } или by mutableStateOf(...)
-
Вы подписываетесь на StateFlow через collectAsState()
-
Вы не мутируете объект напрямую, а создаёте новый
Таким образом, чтобы UI в Compose обновился, достаточно правильно организовать хранение состояния, и Compose сам проследит, что нужно перерисовать.