remember est une fonction dans Jetpack Compose qui permet de conserver une valeur entre les recompositions, empêchant la réinitialisation de l'état à chaque redémarrage d'une fonction Composable. Sans remember, toutes les variables déclarées à l'intérieur d'une fonction seraient réinitialisées à chaque mise à jour de l'interface utilisateur, rendant l'état instable et inutile. Selon Google Android Developers, 2026, l'utilisation correcte de remember est le fondement d'une gestion d'état appropriée dans une UI déclarative.
Points Clés
remember est une fonction intégrée de Jetpack Compose du package androidx.compose.runtime qui crée un objet conservant sa valeur entre les appels successifs d'une fonction Composable. Techniquement, remember fonctionne avec des slots — des cellules mémoire spéciales allouées pour chaque position d'appel de fonction dans l'arbre UI. Tant que la fonction reste dans la composition (non supprimée par une condition if), remember retournera la valeur sauvegardée à chaque recomposition.
La syntaxe de remember est simple : entre accolades, on spécifie le bloc de calcul de la valeur initiale. Le bloc s'exécute une seule fois — lors de la première composition de la fonction. Toutes les recompositions suivantes retournent la valeur déjà sauvegardée sans réexécuter le bloc. Cependant, si un paramètre clé est spécifié, le bloc est réexécuté lorsque la clé change, permettant de recalculer la valeur en fonction des données d'entrée.
remember n'est pas de la magie Kotlin — c'est une fonction simple avec un paramètre clé et un lambda de calcul. Son implémentation interne utilise Composer pour lire et écrire dans les slots. Le compilateur Kotlin, voyant un appel à remember, génère du code qui accède au CompositionContext actuel et gère les slots. Cela signifie que remember ne peut être appelé qu'à l'intérieur d'une fonction Composable ou d'une autre fonction dans le contexte de composition.
Dans Jetpack Compose, il existe trois variantes principales de remember, chacune avec son objectif. Le remember de base conserve la valeur uniquement dans la mémoire du processus actuel — lors de la rotation de l'écran (changement de configuration), Compose recrée la Composition et toutes les valeurs de remember sont réinitialisées. Pour conserver les données lors des changements de configuration et de l'arrêt du processus, on utilise rememberSaveable.
rememberSaveable sérialise la valeur dans un Bundle via le mécanisme SavedStateHandle ou Parcelable. Cela permet de survivre à la rotation de l'écran, au paramètre "Ne pas conserver les activités" et même à la minimisation temporaire de l'application. Cependant, rememberSaveable impose des restrictions sur le type de données pouvant être stockées : elles doivent être des primitives, Parcelable, Serializable ou prendre en charge un convertisseur Saver.
derivedStateOf n'est pas un mécanisme de conservation mais une optimisation. Il crée un State dont la valeur est calculée à partir d'autres objets State. derivedStateOf réagit aux changements des états sources mais ne recalcule la valeur que lorsqu'il y a des abonnés. Si la recomposition actuelle ne lit pas derivedStateOf, le calcul n'est pas effectué, économisant des ressources sur les mises à jour fréquentes mais inutiles.
@Composable
fun RememberVariants() {
// 1. Remember de base : persiste jusqu'à la sortie de la composition
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable : survit à la rotation de l'écran
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf : calculé uniquement quand nécessaire
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("Créé : $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("Le bouton sera affiché")
}
}
Le choix entre remember et rememberSaveable dépend de l'importance de conserver les données lors des changements de configuration. Pour les états temporaires dont la perte n'est pas importante lors de la rotation (animation, position de défilement actuelle, état du focus) — remember normal suffit. Pour les données critiques (texte dans un formulaire, éléments sélectionnés, cases à cocher) — utilisez rememberSaveable.
Performances : rememberSaveable est plus lent que remember normal car il nécessite une sérialisation dans un Bundle. Utilisez rememberSaveable uniquement pour les données qui doivent vraiment survivre à la recréation de l'Activity. Pour tout le reste — remember simple. L'utilisation excessive de rememberSaveable entraîne des ralentissements lors des rotations d'écran et du changement d'applications.
Si vous travaillez avec des classes qui ne supportent pas Parcelable ou Serializable, utilisez Saver — un convertisseur qui définit comment sauvegarder et restaurer l'objet. Saver est décrit par une paire de lambdas : save (convertit l'objet en un type stockable) et restore (restaure l'objet à partir des données sauvegardées). Un Saver standard est déjà implémenté pour mutableStateListOf et mutableStateMapOf.
| Caractéristique | remember | rememberSaveable |
|---|---|---|
| Conservation pendant la recomposition | Oui | Oui |
| Conservation lors de la rotation | Non | Oui |
| Conservation lors de l'arrêt du processus | Non | Oui |
| Exigences de type | N'importe | Parcelable, Serializable, Saver |
| Performances | Élevées | Moyennes |
Considérons un scénario typique — un écran d'édition de profil où remember est utilisé à plusieurs fins : stocker l'état des champs du formulaire, calculer des valeurs dérivées et mettre en cache des opérations coûteuses en calcul.
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("Nom") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("À propos") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("Enregistrer")
}
}
}
Dans l'exemple, l'état est sauvegardé via rememberSaveable — le texte ne sera pas perdu lors de la rotation de l'écran. isValid est calculé via derivedStateOf, ce qui évite des calculs inutiles pendant les recompositions. bioWarning utilise remember avec la clé bio — c'est un calcul coûteux (optionnel, pour démonstration) qui n'est recalculé que lorsque bio change, pas à chaque recomposition.
Les états dérivés sont des valeurs calculées à partir d'autres objets State. Au lieu de les calculer à chaque recomposition et de gaspiller du CPU sur des résultats identiques, remember avec derivedStateOf calcule la valeur uniquement lorsque les sources changent. C'est particulièrement utile pour le filtrage, le tri et l'agrégation de données.
remember avec clés (remember(key) { calculation }) est un autre mécanisme d'optimisation. Si la clé n'a pas changé depuis la dernière recomposition, le bloc de calcul n'est pas exécuté et la valeur mise en cache est retournée. C'est pratique pour mettre en cache des objets dont la création est coûteuse : formatage de dates, analyse JSON, création de grandes collections immuables.
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
// derivedStateOf : filtre recalculé uniquement quand les entrées changent
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember avec clé : statistiques formatées recalculées uniquement sur changement de requête
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 est un derivedStateOf qui se recalcule automatiquement lorsque allItems ou query changent. statsText utilise remember(query) — le formatage coûteux de chaînes est effectué uniquement lorsque la requête de recherche change. La combinaison de derivedStateOf et remember avec clés offre des performances maximales : l'état dérivé est calculé uniquement lorsque nécessaire et les objets complexes sont mis en cache jusqu'à ce que la clé change.
Foire Aux Questions
Non, remember est une fonction du package compose.runtime qui nécessite un CompositionContext. Elle ne peut être appelée qu'à l'intérieur d'une fonction @Composable ou à l'intérieur d'une autre fonction appelée depuis une Composable. Pour stocker des données en dehors de la composition, utilisez ViewModel.
Sans clé, remember exécute le bloc de calcul une seule fois — lors de la première composition. Toutes les recompositions suivantes retournent la valeur sauvegardée. Si vous devez recalculer la valeur lorsque les données changent, assurez-vous de les spécifier comme clé : remember(data) { compute(data) }.
Il n'y a pas de moyen direct de réinitialiser remember. La seule façon est de retirer la fonction de la composition (par exemple, en la cachant avec une condition if) puis de la réafficher. À la réentrée, le bloc remember s'exécute à nouveau, créant une nouvelle valeur initiale.
remember stocke l'état dans un slot de fonction Composable et vit tant que la fonction est dans la composition. ViewModel vit tant que dure le cycle de vie de l'écran. ViewModel est conservé lors de la rotation et est utilisé pour la logique métier. remember est destiné à l'état UI local qui n'est pas nécessaire en dehors d'une seule fonction.
Oui, remember fonctionne correctement dans les fonctions @Preview Composable car Preview crée un CompositionContext complet. Cependant, rememberSaveable peut ne pas fonctionner correctement dans Preview car SavedStateHandle peut être absent dans l'environnement de prévisualisation.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi