remember — to funkcja w Jetpack Compose, która pozwala zachować wartość między rekompozycjami, zapobiegając resetowaniu stanu przy każdym ponownym uruchomieniu funkcji Composable. Bez remember wszystkie zmienne zadeklarowane wewnątrz funkcji byłyby zerowane przy każdej aktualizacji UI, czyniąc stan niestabilnym i bezużytecznym. Według danych Google Android Developers, 2026, prawidłowe użycie remember jest podstawą poprawnego zarządzania stanem w deklaratywnym UI.
Najważniejsze
remember — to wbudowana funkcja Jetpack Compose z pakietu androidx.compose.runtime, która tworzy obiekt zachowujący swoją wartość między kolejnymi wywołaniami funkcji Composable. Technicznie remember działa ze slotami — specjalnymi komórkami pamięci przydzielonymi dla każdej pozycji wywołania funkcji w drzewie UI. Dopóki funkcja pozostaje w składzie kompozycji (nie została usunięta przez warunek if), remember będzie zwracać zapisaną wartość przy każdej rekompozycji.
Składnia remember jest prosta: w nawiasach klamrowych podaje się blok obliczający wartość początkową. Blok wykonuje się tylko raz — przy pierwszej kompozycji funkcji. Wszystkie kolejne rekompozycje zwracają już zapisaną wartość, nie wywołując bloku ponownie. Jeśli jednak podano klucz (parametr key), blok jest wykonywany ponownie przy zmianie klucza, co pozwala przeliczać wartość na podstawie danych wejściowych.
remember nie jest magią Kotlina — to prosta funkcja z parametrem key i lambdą obliczeniową. Jej wewnętrzna implementacja używa Composer do odczytu i zapisu w slotach. Kompilator Kotlin, widząc wywołanie remember, generuje kod, który odwołuje się do bieżącego CompositionContext i zarządza slotami. Oznacza to, że remember można wywoływać tylko wewnątrz funkcji Composable lub innej funkcji w kontekście kompozycji.
W Jetpack Compose istnieją trzy główne warianty remember, każdy z własnym przeznaczeniem. Podstawowy remember przechowuje wartość tylko w pamięci bieżącego procesu — przy obrocie ekranu (config change) Compose odtwarza Composition i wszystkie wartości remember są resetowane. Do przechowywania danych przy zmianach konfiguracji i zatrzymaniu procesu używa się rememberSaveable.
rememberSaveable serializuje wartość do Bundle przez mechanizm SavedStateHandle lub Parcelable. Pozwala to przetrwać obrót ekranu, wywołanie „Nie zachowuj aktywności” i nawet tymczasowe minimalizowanie aplikacji. Jednak rememberSaveable nakłada ograniczenia na typ przechowywanych danych: muszą to być prymitywy, Parcelable, Serializable lub obsługiwać konwerter Saver.
derivedStateOf — to nie mechanizm przechowywania, ale optymalizacja. Tworzy State, którego wartość jest obliczana na podstawie innych obiektów State. derivedStateOf reaguje na zmiany stanów źródłowych, ale przelicza wartość tylko w obecności subskrybentów. Jeśli bieżąca rekompozycja nie czyta derivedStateOf, obliczenie nie jest wykonywane, co oszczędza zasoby przy częstych, ale nikomu niepotrzebnych aktualizacjach.
@Composable
fun RememberVariants() {
// 1. Podstawowy remember: trwa do opuszczenia kompozycji
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable: przetrwa obrót ekranu
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf: obliczane tylko gdy potrzebne
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("Utworzono: $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("Przycisk zostanie pokazany")
}
}
Wybór między remember a rememberSaveable zależy od tego, jak krytyczne jest przechowywanie danych przy zmianach konfiguracji. Dla tymczasowych stanów, których utrata przy obrocie nie jest istotna (animacja, bieżąca pozycja przewijania, stan fokusu) — wystarczy zwykły remember. Dla krytycznych danych (tekst w formularzu, wybrane elementy, checkboxy) — użyj rememberSaveable.
Wydajność rememberSaveable jest niższa niż zwykłego remember, ponieważ wymaga serializacji do Bundle. Używaj rememberSaveable tylko dla danych, które rzeczywiście muszą przetrwać odtworzenie Activity. Dla wszystkiego innego — zwykły remember. Nadmierne użycie rememberSaveable prowadzi do spowolnienia przy obrotach ekranu i przełączaniu między aplikacjami.
Jeśli pracujesz z klasami, które nie obsługują Parcelable ani Serializable, użyj Saver — konwertera, który określa, jak zapisywać i przywracać obiekt. Saver jest opisany parą lambd: save (przekształca obiekt do zapisywalnego typu) i restore (przywraca obiekt z zapisanych danych). Standardowy Saver jest już zaimplementowany dla mutableStateListOf i mutableStateMapOf.
| Cecha | remember | rememberSaveable |
|---|---|---|
| Zachowanie przy rekompozycji | Tak | Tak |
| Zachowanie przy obrocie | Nie | Tak |
| Zachowanie przy zatrzymaniu procesu | Nie | Tak |
| Wymagania dotyczące typu | Dowolny | Parcelable, Serializable, Saver |
| Wydajność | Wysoka | Średnia |
Rozważmy typowy scenariusz — ekran edycji profilu, gdzie remember jest używany do kilku celów: przechowywania stanu pól formularza, obliczania wartości pochodnych i buforowania operacji wymagających dużych obliczeń.
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("Nazwa") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("O nas") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("Zapisz")
}
}
}
W przykładzie stan jest przechowywany przez rememberSaveable — przy obrocie ekranu tekst nie zniknie. isValid jest obliczane przez derivedStateOf, co zapobiega zbędnym obliczeniom przy rekompozycjach. bioWarning używa remember z kluczem bio — to kosztowne obliczenie (opcjonalnie, dla demonstracji), które jest przeliczane tylko przy zmianie bio, a nie przy każdej rekompozycji.
Stany pochodne (derived state) to wartości, które są obliczane z innych obiektów State. Zamiast obliczać je przy każdej rekompozycji i marnować CPU na identyczne wyniki, remember z derivedStateOf oblicza wartość tylko przy zmianie źródeł. Jest to szczególnie przydatne do filtrowania, sortowania i agregacji danych.
remember z kluczami (remember(key) { calculation }) — to inny mechanizm optymalizacji. Jeśli klucz nie zmienił się od poprzedniej rekompozycji, blok obliczeniowy nie jest wykonywany i zwracana jest buforowana wartość. Jest to wygodne do buforowania obiektów, których tworzenie jest kosztowne: formatowanie dat, parsowanie JSON, tworzenie dużych niezmiennych kolekcji.
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
// derivedStateOf: filtr przeliczany tylko gdy zmienią się dane wejściowe
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember z kluczem: sformatowane statystyki przeliczane tylko przy zmianie zapytania
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 — to derivedStateOf, który automatycznie przelicza się przy zmianie allItems lub query. statsText używa remember(query) — kosztowne formatowanie ciągu znaków jest wykonywane tylko przy zmianie zapytania wyszukiwania. Kombinacja derivedStateOf i remember z kluczami daje maksymalną wydajność: stan pochodny jest obliczany tylko gdy potrzebny, a złożone obiekty są buforowane do zmiany klucza.
Często zadawane pytania
Nie, remember — to funkcja z pakietu compose.runtime, która wymaga CompositionContext. Może być wywołana tylko wewnątrz funkcji @Composable lub wewnątrz innej funkcji wywołanej z Composable. Do przechowywania danych poza kompozycją użyj ViewModel.
Bez klucza remember oblicza blok tylko raz — przy pierwszej kompozycji. Wszystkie kolejne rekompozycje zwracają zapisaną wartość. Jeśli potrzebujesz przeliczać wartość przy zmianie danych, koniecznie podaj je jako klucz: remember(data) { compute(data) }.
Nie ma bezpośredniego resetu remember. Jedynym sposobem jest usunięcie funkcji ze składu kompozycji (na przykład ukrywając przez warunek if) i ponowne pokazanie. Przy ponownym wejściu blok remember wykona się ponownie, tworząc nową wartość początkową.
remember przechowuje stan w slocie funkcji Composable i żyje, dopóki funkcja jest w kompozycji. ViewModel żyje, dopóki żyje lifecycle ekranu. ViewModel jest zachowywany przy obrocie i używany do logiki biznesowej. remember — do lokalnego stanu UI, który nie jest potrzebny poza jedną funkcją.
Tak, remember działa poprawnie w funkcjach @Preview Composable, ponieważ Preview tworzy pełnoprawny CompositionContext. Jednak rememberSaveable może nie działać poprawnie w Preview, ponieważ SavedStateHandle może być niedostępny w środowisku podglądu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również