Состояние вне remember и его последствия

Разбираем классическую ошибку новичков: состояние без remember и без rememberSaveable, и чем это грозит.

rememberSaveable — версия remember, которая дополнительно сохраняет значение при пересоздании Activity (поворот экрана, восстановление процесса).

Три уровня «памяти» состояния

Важно различать три варианта объявления состояния и понимать, что выживает в каждом:

// 1) Сбросится при ЛЮБОЙ перерисовке
var a = mutableStateOf(0)

// 2) Переживёт recomposition, но НЕ поворот экрана
var b by remember { mutableStateOf(0) }

// 3) Переживёт и recomposition, и поворот экрана
var c by rememberSaveable { mutableStateOf(0) }
СпособПереживает recompositionПереживает поворот
без rememberнетнет
rememberданет
rememberSaveableдада

Что ломается без remember

Состояние без remember пересоздаётся при каждой перерисовке, возвращаясь к начальному значению. Счётчик, объявленный так, никогда не вырастет — UI будет упорно показывать ноль. Это одна из самых частых ошибок при первом знакомстве с Compose.

Когда нужен rememberSaveable

Поворот экрана пересоздаёт Activity и саму композицию — обычный remember при этом теряет значение. Для пользовательского ввода (текст в поле, выбранная вкладка, позиция) используйте rememberSaveable: он сохраняет данные в Bundle и восстанавливает после пересоздания.

@Composable
fun Form() {
    var name by rememberSaveable { mutableStateOf("") }
    TextField(value = name, onValueChange = { name = it })
}

Без rememberSaveable пользователь повернул бы телефон — и весь введённый текст исчез. С ним — текст на месте.

Как работает под капотом

rememberSaveable кладёт значение в механизм сохранённого состояния (saved instance state), который система использует при пересоздании Activity. Поэтому туда можно класть только сохраняемые типы (примитивы, строки, Parcelable); для сложных объектов нужен кастомный Saver. Долгоживущее же состояние экрана (загруженные данные) обычно держат не здесь, а в ViewModel.

Частые ошибки

  • Объявить состояние без remember и не понимать, почему счётчик «не считает».
  • Использовать remember там, где данные обязаны пережить поворот — после поворота ввод теряется.
  • Класть в rememberSaveable крупные несохраняемые объекты вместо ViewModel.

Итог

  • Состояние без remember сбрасывается на каждой перерисовке — частая причина «несчитающего» счётчика.
  • remember переживает recomposition, но не поворот экрана.
  • rememberSaveable переживает и поворот; долгоживущие данные держите в ViewModel.
Проверьте себя
1. Что произойдёт со счётчиком, объявленным как var count = mutableStateOf(0) без remember?
AОн будет работать нормально
BОн будет сбрасываться к нулю при каждой перерисовке
CПроизойдёт ошибка компиляции
DОн сохранится даже после поворота экрана
2. Чем rememberSaveable отличается от remember?
AНичем, это синонимы
BrememberSaveable дополнительно переживает поворот экрана и пересоздание Activity
Cremember сохраняет данные на диск
DrememberSaveable работает только с числами
3. Где лучше держать долгоживущие загруженные данные экрана?
AВ rememberSaveable
BВ ViewModel
CВ обычной переменной без remember
DВ TopAppBar