remember — это функция в Jetpack Compose, которая позволяет сохранять значение между рекомпозициями, предотвращая сброс состояния при каждом перезапуске Composable-функции. Без remember все переменные, объявленные внутри функции, обнулялись бы при каждом обновлении UI, делая состояние нестабильным и бесполезным. По данным Google Android Developers, 2026, правильное использование remember является основой корректного управления состоянием в декларативном UI.
Главное
remember — это встроенная функция Jetpack Compose из пакета androidx.compose.runtime, которая создаёт объект, сохраняющий своё значение между последовательными вызовами Composable-функции. Технически remember работает со слотами — специальными ячейками памяти, выделенными для каждой позиции вызова функции в UI-дереве. Пока функция остаётся в составе композиции (не удалена через if-условие), remember будет возвращать сохранённое значение при каждой рекомпозиции.
Синтаксис remember прост: внутри фигурных скобок указывается блок вычисления начального значения. Блок выполняется только один раз — при первой композиции функции. Все последующие рекомпозиции возвращают уже сохранённое значение, не вызывая блок заново. Однако если указан ключ (параметр key), блок перевыполняется при изменении ключа, что позволяет пересчитывать значение на основе входных данных.
remember не является магией Kotlin — это простая функция с параметром key и лямбдой вычисления. Её внутренняя реализация использует Composer для чтения и записи в слоты. Компилятор Kotlin, видя вызов remember, генерирует код, который обращается к текущему CompositionContext и менеджерити слоты. Это означает, что remember можно вызывать только внутри Composable-функции или другой функции в контексте композиции.
В Jetpack Compose существует три основных варианта remember, каждый со своим назначением. Базовый remember сохраняет значение только в памяти текущего процесса — при повороте экрана (config change) Compose пересоздаёт Composition, и все remember-значения сбрасываются. Для сохранения данных при конфигурационных изменениях и остановке процесса используется rememberSaveable.
rememberSaveable сериализует значение в Bundle через механизм SavedStateHandle или Parcelable. Это позволяет пережить поворот экрана, вызов "Не сохранять активности" и даже временное сворачивание приложения. Однако rememberSaveable накладывает ограничения на тип сохраняемых данных: они должны быть примитивами, Parcelable, Serializable или поддерживать Saver-конвертер.
derivedStateOf — это не механизм сохранения, а оптимизация. Он создаёт State, значение которого вычисляется на основе других State-объектов. derivedStateOf реагирует на изменения исходных состояний, но пересчитывает значение только при наличии подписчиков. Если текущая рекомпозиция не читает derivedStateOf, вычисление не выполняется, что экономит ресурсы при частых, но никому не нужных обновлениях.
@Composable
fun RememberVariants() {
// 1. Basic remember: persists until leaving composition
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable: survives screen rotation
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf: computed only when needed
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("Created: $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("Button will be shown")
}
}
Выбор между remember и rememberSaveable зависит от того, насколько критично сохранение данных при конфигурационных изменениях. Для временных состояний, которые неважно терять при повороте (анимация, текущая позиция скролла, состояние фокуса) — достаточно обычного remember. Для критичных данных (текст в форме, выбранные элементы, чекбоксы) — используйте rememberSaveable.
Производительность rememberSaveable ниже, чем у обычного remember, так как требует сериализации в Bundle. Делайте rememberSaveable только для данных, которые действительно должны пережить пересоздание Activity. Для всего остального — простой remember. Избыточное использование rememberSaveable приводит к замедлению при поворотах экрана и при переключении между приложениями.
Если вы работаете с классами, не поддерживающими Parcelable или Serializable, используйте Saver — конвертер, который определяет, как сохранять и восстанавливать объект. Saver описывается парой лямбд: save (преобразует объект в сохраняемый тип) и restore (восстанавливает объект из сохранённых данных). Стандартный Saver уже реализован для mutableStateListOf и mutableStateMapOf.
| Характеристика | remember | rememberSaveable |
|---|---|---|
| Сохранение при рекомпозиции | Да | Да |
| Сохранение при повороте | Нет | Да |
| Сохранение при остановке процесса | Нет | Да |
| Требования к типу | Любой | Parcelable, Serializable, Saver |
| Производительность | Высокая | Средняя |
Рассмотрим типовой сценарий — экран редактирования профиля, где remember используется для нескольких целей: хранения состояния полей формы, расчёта производных значений и кэширования вычислительных затратных операций.
data class ProfileUiState(
val name: String = "",
val bio: String = "",
val isSaving: Boolean = false
)
@Composable
fun ProfileEditor() {
var state by rememberSaveable { mutableStateOf(ProfileUiState()) }
val isValid = remember {
derivedStateOf { state.name.isNotBlank() && state.bio.length() <= 500 }
}
val bioWarning = remember(state.bio) {
if (state.bio.length() > 400) {
"${500 - state.bio.length} chars left"
} else null
}
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = state.name,
onValueChange = { state = state.copy(name = it) },
label = { Text("Name") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("About") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("Save")
}
}
}
В примере state сохраняется через rememberSaveable — при повороте экрана текст не пропадёт. isValid вычисляется через derivedStateOf, что предотвращает лишние вычисления при рекомпозициях. bioWarning использует remember с ключом bio — это дорогое вычисление (опционально, для демонстрации), которое пересчитывается только при изменении bio, а не при каждой рекомпозиции.
Производные состояния (derived state) — это значения, которые вычисляются из других State-объектов. Вместо того чтобы вычислять их при каждой рекомпозиции и тратить CPU на идентичные результаты, remember с derivedStateOf вычисляет значение только при изменении источников. Это особенно полезно для фильтрации, сортировки и агрегации данных.
remember с ключами (remember(key) { calculation }) — другой механизм оптимизации. Если ключ не изменился с прошлой рекомпозиции, блок вычисления не выполняется, и возвращается закэшированное значение. Это удобно для кэширования объектов, создание которых дорого: форматирование дат, парсинг JSON, создание больших неизменяемых коллекций.
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
// derivedStateOf: filter recomputed only when inputs change
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember with key: formatted stats recomputed only on query change
val statsText = remember(query) {
"Results for query \"$query\": ${filtered.value.size}"
}
Text(statsText)
LazyColumn {
items(filtered.value, key = { it.id }) { item ->
Text(item.title)
}
}
}
filtered — это derivedStateOf, который автоматически пересчитывается при изменении allItems или query. statsText использует remember(query) — дорогое строковое форматирование выполняется только при смене поискового запроса. Комбинация derivedStateOf и remember с ключами даёт максимальную производительность: производное состояние вычисляется только когда нужно, а сложные объекты кэшируются до изменения ключа.
Часто задаваемые вопросы
Нет, remember — это функция из пакета compose.runtime, которая требует CompositionContext. Она может быть вызвана только внутри @Composable-функции или внутри другой функции, вызванной из Composable. Для сохранения данных вне композиции используйте ViewModel.
Без ключа remember вычисляет блок только один раз — при первой композиции. Все последующие рекомпозиции возвращают сохранённое значение. Если нужно пересчитывать значение при изменении данных, обязательно укажите их в качестве ключа: remember(data) { compute(data) }.
Прямого сброса remember нет. Единственный способ — вывести функцию из состава композиции (например, скрыв через if-условие) и затем показать снова. При повторном входе блок remember выполнится заново, создав новое начальное значение.
remember хранит состояние в слоте Composable-функции и живёт, пока функция в композиции. ViewModel живёт, пока жив lifecycle экрана. ViewModel сохраняется при повороте и используется для бизнес-логики. remember — для локального UI-состояния, которое не нужно за пределами одной функции.
Да, remember корректно работает в @Preview Composable-функциях, так как Preview создаёт полноценный CompositionContext. Однако rememberSaveable может не сработать корректно в Preview, так как SavedStateHandle может отсутствовать в окружении превью.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также