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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също