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. Базовий remember: існує до виходу з композиції
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable: переживає поворот екрана
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf: обчислюється тільки коли потрібно
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("Створено: $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("Кнопка буде показана")
}
}
Вибір між 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("Ім'я") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("Про") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("Зберегти")
}
}
}
У прикладі стан зберігається через 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: фільтр перераховується тільки при зміні вхідних даних
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember з ключем: форматована статистика перераховується тільки при зміні запиту
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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також