L’annotation @Composable est un élément fondamental de Jetpack Compose qui transforme une fonction Kotlin ordinaire en un bloc de construction déclaratif de l’interface utilisateur. Sans cette annotation, il est impossible de créer un écran dans le développement Android moderne. Selon Google Android Developers, 2026, plus de 80 % des nouveaux projets Kotlin utilisent Compose pour construire l’UI, et @Composable est l’annotation la plus fréquemment utilisée dans l’écosystème.
Points clés
@Composable est une annotation du langage Kotlin qui marque une fonction comme destinée à décrire l’interface utilisateur dans le framework Jetpack Compose. Lorsque le compilateur Kotlin rencontre cette annotation, il génère du code supplémentaire permettant à la fonction de fonctionner dans le contexte de composition — le système de gestion de l’arborescence UI.
L’annotation @Composable a été présentée par Google en 2021 avec la première version stable de Jetpack Compose 1.0. Avant son apparition, le développement d’interfaces Android se faisait exclusivement via le balisage XML et le système View. @Composable a radicalement changé l’approche : au lieu de décrire l’UI dans un fichier de balisage séparé, le développeur écrit l’interface directement en Kotlin.
La principale différence entre @Composable et les fonctions Kotlin ordinaires est la capacité de lire et de réagir aux changements d’état. Lorsqu’une variable lue par une fonction Composable change de valeur, le système planifie automatiquement un redémarrage (recomposition) de cette fonction. Cela libère le développeur de la mise à jour manuelle de l’UI via findViewById et setText.
La mécanique interne de @Composable repose sur le concept de slot — une zone mémoire spéciale allouée à chaque fonction au sein de la composition. Ce slot stocke les valeurs transmises à la fonction ainsi que les informations de service nécessaires à la comparaison lors des appels ultérieurs.
Pour déclarer une fonction Composable, il suffit d’ajouter l’annotation @Composable avant le mot-clé fun. La fonction doit se trouver dans un package important l’annotation depuis androidx.compose.runtime. Il est recommandé d’écrire le nom de la fonction avec une majuscule — c’est une convention largement acceptée dans la communauté Compose qui distingue visuellement les composants UI des fonctions ordinaires.
import androidx.compose.runtime.Composable
@Composable
fun Greeting(name: String) {
var count by remember { mutableStateOf(0) }
Column {
Text("Bonjour, $name !")
Button(onClick = { count++ }) {
Text("Cliqué $count fois")
}
}
}
Les paramètres d’une fonction Composable peuvent être n’importe quoi — types primitifs, chaînes, lambdas et même d’autres fonctions Composable transmises via la Slot API. Il est recommandé de rendre les paramètres immuables (val) pour éviter les effets secondaires pendant la recomposition. Toutes les données mutables doivent être gérées via les mécanismes d’état de Compose.
Les fonctions Composable ne peuvent pas retourner de valeurs arbitraires comme les fonctions régulières — leur seule tâche est de construire ou de mettre à jour un fragment de l’arborescence UI. Cependant, il existe des motifs spéciaux comme le State Hoisting, où une fonction Compose accepte l’état et les callbacks via les paramètres, restant ainsi pure et réutilisable.
Le système Compose impose plusieurs restrictions strictes sur l’apparence et le comportement des fonctions Composable. Première règle : une fonction Composable ne peut appeler que d’autres fonctions Composable ou des fonctions régulières sans effets secondaires. Cela garantit la prévisibilité de la composition et le bon fonctionnement des optimisations de Compose.
La deuxième règle concerne l’ordre d’exécution. Compose a le droit d’appeler les fonctions Composable dans n’importe quel ordre, donc le code dans le corps d’une telle fonction ne doit pas dépendre de la séquence d’appel des fonctions voisines. Chaque fonction Composable doit être autonome au niveau de sa position dans l’arborescence UI.
Troisième règle — interdiction des effets secondaires à l’intérieur du corps d’une fonction Composable. Les opérations comme l’écriture dans une base de données, l’envoi de requêtes réseau ou la modification de variables externes doivent être effectuées uniquement dans des effets spéciaux : LaunchedEffect, DisposableEffect ou SideEffect. La violation de cette règle entraîne un comportement imprévisible lors des recompositions.
Quatrième règle : les fonctions Composable doivent être idempotentes. Les rappeler avec les mêmes arguments doit produire la même UI. Cette exigence est nécessaire au bon fonctionnement de l’optimisation de saut, où Compose saute le redessin des fonctions dont les données d’entrée n’ont pas changé.
// Correct : fonction Composable pure sans effets secondaires
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
Card(modifier = Modifier.clickable { onClick() }) {
Text(text = user.name)
}
}
// Erreur : effet secondaire à l’intérieur du corps
@Composable
fun WrongCard(userId: String) {
// val result = viewModel.loadUser(userId) // PAS AUTORISÉ
Text("Chargement...")
}
Regardons un exemple pratique de création d’un écran de profil utilisant l’annotation @Composable. Nous démontrons ici la combinaison de plusieurs fonctions Composable, le travail avec l’état et les modificateurs — éléments clés de tout layout Compose.
@Composable
fun ProfileScreen(userId: String) {
var isFollowed by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
ProfileHeader(userId = userId)
Spacer(modifier = Modifier.height(16.dp))
StatsRow(posts = 42, followers = 1280)
Spacer(modifier = Modifier.height(24.dp))
FollowButton(
isFollowed = isFollowed,
onToggle = { isFollowed = !isFollowed }
)
}
}
@Composable
fun ProfileHeader(userId: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = "https://example.com/avatars/$userId",
contentDescription = "User avatar")
Spacer(modifier = Modifier.width(12.dp))
Text(text = "Utilisateur #$userId", style = MaterialTheme.typography.headlineMedium)
}
}
@Composable
fun StatsRow(posts: Int, followers: Int) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
StatItem("Posts", posts)
StatItem("Followers", followers)
}
}
@Composable
fun StatItem(label: String, value: Int) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "$value", style = MaterialTheme.typography.headlineSmall)
Text(text = label, style = MaterialTheme.typography.bodySmall)
}
}
Dans l’exemple, chaque fonction Composable est responsable de sa partie de l’écran : ProfileScreen gère l’état général et la composition des fonctions filles, ProfileHeader affiche l’avatar et le nom, et StatsRow montre un bloc de statistiques. Cette approche suit le principe de responsabilité unique et simplifie la réutilisation des composants.
Dans Jetpack Compose, il existe trois types principaux de fonctions Composable. Le premier type — les conteneurs (Row, Column, Box, LazyColumn) — déterminent la disposition des éléments enfants. Le deuxième type — les éléments d’affichage (Text, Image, Icon, Button) — rendent des composants UI spécifiques. Le troisième type — les fonctions Composable personnalisées — combinent des composants intégrés en blocs réutilisables.
Les conteneurs diffèrent des éléments réguliers en ce qu’ils acceptent un lambda content — le dernier paramètre de type @Composable () -> Unit. Ce mécanisme permet de construire des arborescences UI imbriquées : chaque conteneur génère une composition fille avec son propre contexte et sa propre zone mémoire.
Les fonctions Composable personnalisées se divisent en deux sous-types : intelligentes (smart) et simples (dumb). Les fonctions intelligentes gèrent l’état et la logique — elles contiennent des appels à remember, LaunchedEffect et d’autres API Compose. Les fonctions simples reçoivent toutes les données via les paramètres et les affichent seulement. La séparation en composants intelligents et simples améliore la testabilité et la réutilisation du code.
| Type | Exemple | Objectif |
|---|---|---|
| Conteneur | Column, Row, Box | Gestion de la disposition des éléments enfants |
| Élément | Text, Image, Button | Affichage du contenu et traitement des entrées |
| Personnalisé | ProfileCard, UserList | Combinaison de composants standards |
Le principal avantage de l’annotation @Composable est la capacité de créer des composants UI réutilisables sans héritage ni hiérarchies de classes complexes. Contrairement au système View, où chaque élément personnalisé nécessitait la création d’une classe Java avec des constructeurs, un composant Composable est simplement une fonction Kotlin avec des paramètres.
Pour garantir la réutilisation, on utilise le motif Slot API, où une fonction Composable accepte des lambdas content pour différentes zones de sa mise en page. Par exemple, un composant Card peut accepter un contenu séparé pour l’en-tête, le corps et le pied de page, ce qui le rend universel pour tout écran d’application.
Les modificateurs (Modifier) jouent un rôle clé dans la réutilisation : ils permettent de configurer les paddings, les tailles, les clics et les animations sans modifier le composant lui-même. Il est recommandé de toujours passer Modifier comme paramètre d’une fonction Composable avec une valeur par défaut : Modifier = Modifier — c’est une pratique standard adoptée dans les bibliothèques officielles de Google.
@Composable
fun SectionCard(
modifier: Modifier = Modifier,
title: String,
content: @Composable () -> Unit
) {
Card(modifier = modifier) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = title, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
content()
}
}
}
Grâce à la Slot API, le composant SectionCard peut être utilisé sur différents écrans avec différents contenus — formulaires, listes, blocs de texte. La combinaison des modificateurs et de la Slot API rend les composants Compose extrêmement flexibles sans perdre la sécurité de typage offerte par Kotlin.
Foire aux questions
Une fonction @Composable s’exécute dans le contexte de composition et peut lire l’état, redémarrant automatiquement lors de son changement. Les fonctions Kotlin ordinaires n’ont pas accès aux mécanismes de suivi d’état et ne participent pas à la construction de l’arborescence UI.
Non, les fonctions Composable ne peuvent être appelées que depuis d’autres fonctions Composable, car un contexte de composition spécial est requis. Pour intégrer du code Compose avec du Kotlin normal, on utilise la méthode setContent { } de l’Activity ou ComposeView dans le système View.
C’est une convention de nommage adoptée dans la communauté Compose. La majuscule distingue visuellement les composants UI des fonctions ordinaires, en suivant les règles de nommage des classes. Ce n’est pas une exigence du compilateur, mais une pratique recommandée dans la documentation de Google.
Il n’y a pas de limite sur le nombre. En pratique, un grand écran peut contenir 50–100 fonctions Composable, y compris les composants intégrés (Text, Button) et personnalisés. Compose optimise l’arbre de fonctions et n’exécute que celles dont les données d’entrée ont changé.
Habituellement, les fonctions Composable retournent Unit, car leur tâche est de construire l’UI. Cependant, il existe des fonctions spécialisées comme remember et derivedStateOf qui sont marquées @Composable et retournent des valeurs. C’est une exception, pas la règle.
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