Как обновить 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 и т.п.).

⚠️ Ошибки, которых стоит избегать

  1. Изменение состояния вне Compose (например, viewModel.someList.add(item) без вызова mutableStateOf или обновления StateFlow).

  2. Создание State в ViewModel, но без обёртки в collectAsState() в UI.

Изменение объекта по ссылке, не меняя саму ссылку:

<br/>val list = remember { mutableStateListOf&lt;Int&gt;() }
list.add(1) // UI не перерисуется, если вы используете обычный List
  1. 🔹 Используйте mutableStateListOf() или обновляйте State целиком.

📦 Обновление UI при изменении сложных структур

Для коллекций — используйте:

  • mutableStateListOf() / SnapshotStateList

  • mutableStateMapOf() / SnapshotStateMap

Или обновляйте весь список:

var items by remember { mutableStateOf(listOf&lt;String&gt;()) }
items = items + "New Item"

🧠 В двух словах

UI в Compose сам реагирует на изменения состояния, если:

  • Вы используете remember { mutableStateOf(...) } или by mutableStateOf(...)

  • Вы подписываетесь на StateFlow через collectAsState()

  • Вы не мутируете объект напрямую, а создаёте новый

Таким образом, чтобы UI в Compose обновился, достаточно правильно организовать хранение состояния, и Compose сам проследит, что нужно перерисовать.