Composable Function est une unité fondamentale de l’interface utilisateur dans Jetpack Compose qui définit à quoi une partie de l’écran doit ressembler et comment elle doit se comporter. Chacune de ces fonctions est marquée par l’annotation @Composable et exécutée dans un contexte spécial qui permet à Compose de suivre les dépendances et de reconstruire automatiquement l’UI lorsque les données changent. Selon Google Android Developers, 2026, la construction correcte des fonctions Composable affecte directement les performances de l’application et l’efficacité de la recomposition.
À retenir
Une Composable Function est une fonction en langage Kotlin, marquée par l’annotation @Composable, qui décrit une partie de l’interface utilisateur de manière déclarative. Au lieu de créer et configurer des objets View via du code Java ou du balisage XML, le développeur écrit simplement à quoi l’UI doit ressembler pour chaque état des données.
La principale différence entre une fonction Composable et le système View traditionnel d’Android réside dans le modèle de mise à jour. Dans l’approche classique, le développeur appelait manuellement findViewById, modifiait le texte via setText et gérait la visibilité via setVisibility. Composable Function vous libère de cette routine : lorsque les données changent, le système lui-même détermine quelles fonctions doivent être recomposées et n’exécute que celles-ci.
Le compilateur Kotlin, en traitant l’annotation @Composable, génère du code supplémentaire qui intègre la fonction dans le mécanisme de composition. Ce code inclut la lecture et l’écriture dans des slots — des cellules mémoire spéciales qui stockent l’état et les paramètres de chaque fonction Composable dans l’arbre UI actuel. Grâce à cette intégration, Compose sait quelles fonctions dépendent de quelles données.
La syntaxe d’une fonction Composable est extrêmement concise : il suffit d’ajouter @Composable avant le mot-clé fun. La fonction peut accepter n’importe quels paramètres, inclure d’autres appels Composable dans son corps et utiliser les constructions Kotlin — conditions, boucles, expressions when — pour le rendu conditionnel de l’UI.
@Composable
fun ProductItem(
product: Product,
modifier: Modifier = Modifier,
onAddToCart: () -> Unit
) {
Card(modifier = modifier.padding(8.dp)) {
Row(modifier = Modifier.fillMaxWidth().padding(12.dp),
verticalAlignment = Alignment.CenterVertically) {
Column(modifier = Modifier.weight(1f)) {
Text(text = product.name, style = MaterialTheme.typography.titleMedium)
Text(text = "${product.price}", color = MaterialTheme.colorScheme.primary)
}
Button(onClick = onAddToCart) {
Text("Ajouter au panier")
}
}
}
}
Dans cet exemple, la fonction Composable ProductItem accepte un objet Product, un modificateur et un callback. Les trois paramètres sont immutables, ce qui garantit un comportement prévisible lors de la recomposition. Le modificateur est passé comme paramètre avec une valeur par défaut — c’est une pratique standard qui permet à l’appelant de personnaliser les espacements et les tailles.
À l’intérieur d’une fonction Composable, des composants Material Design intégrés (Text, Button, Card, TextField) ou des primitives fondamentales (Canvas, Layout) sont utilisés. Chaque composant accepte des paramètres pour configurer l’apparence et le comportement, ainsi qu’un ou plusieurs modificateurs via le paramètre modifier.
Les modificateurs sont une chaîne de fonctions qui modifient la taille, la position, le traitement des événements et l’apparence d’un composant. L’ordre des modificateurs dans la chaîne compte : clickable.semantics fonctionne différemment de semantics.clickable, et padding.background peint à la fois la zone et l’arrière-plan incluant le padding, ce qui est crucial lors de la conception.
À l’intérieur d’une fonction Composable, vous pouvez utiliser des conditions if et when pour le rendu conditionnel de parties de l’UI, ainsi que des boucles for pour des listes dynamiques. Toutes ces constructions fonctionnent naturellement car Kotlin est un langage de programmation complet. Cependant, il est important de se rappeler : si une condition ou une boucle contient des appels de fonctions Composable, elles participent également à la recomposition.
@Composable
fun ProductList(
products: List<Product>,
modifier: Modifier = Modifier
) {
LazyColumn(modifier = modifier) {
items(products, key = { it.id }) { product ->
ProductItem(
product = product,
onAddToCart = { /* add to cart */ }
)
}
}
}
Considérons un exemple d’écran de recherche de produits utilisant plusieurs fonctions Composable. Des motifs typiques sont montrés ici : un champ de saisie avec état, le filtrage de liste, la gestion de résultats vides et le chargement.
data class Product(
val id: String,
val name: String,
val price: Double,
val category: String
)
@Composable
fun SearchScreen() {
var query by remember { mutableStateOf("") }
val products = remember(query) { getFilteredProducts(query) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
OutlinedTextField(
value = query,
onValueChange = { query = it },
label = { Text("Rechercher des produits") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
when (products) {
is Loading -> CircularProgressIndicator()
is Empty -> Text("Aucun résultat trouvé")
is Result -> LazyColumn {
items(products.items, key = { it.id }) { product ->
ProductItem(product = product, onAddToCart = {})
}
}
}
}
}
Cet exemple démontre plusieurs idiomes à la fois : remember pour préserver l’état de la requête de recherche, remember(query) pour filtrer avec une clé, when pour trois états d’UI et LazyColumn pour un rendu efficace de liste. Chacun de ces idiomes est le fruit de l’expérience pratique du développement d’applications Compose.
Les fonctions Composable acceptent des paramètres comme les fonctions Kotlin normales, mais avec une différence importante : un paramètre peut être une autre fonction Composable passée via une lambda avec l’annotation @Composable. Ce mécanisme s’appelle Slot API et est le motif principal pour créer des conteneurs réutilisables.
La Slot API résout le problème qui, dans le système View traditionnel, était résolu via ViewGroup et l’ajout programmatique de vues enfants. Au lieu des méthodes addView, Compose utilise des lambdas content — le dernier paramètre de type @Composable () -> Unit. L’appelant passe n’importe quelle UI dans cette lambda, et le conteneur définit seulement sa disposition.
Les paramètres des fonctions Composable peuvent avoir des valeurs par défaut, ce qui simplifie leur utilisation dans différents contextes. Il est recommandé de rendre obligatoires uniquement les paramètres sans lesquels la fonction ne peut pas accomplir sa tâche, et de fournir des valeurs par défaut raisonnables pour les autres.
| Paramètre | Type | Exemple |
|---|---|---|
| Obligatoire | N’importe quel type | name: String |
| Optionnel | Avec valeur par défaut | modifier: Modifier = Modifier |
| Content | @Composable () -> Unit | content: @Composable () -> Unit |
| Callback | Lambda sans @Composable | onClick: () -> Unit |
Plusieurs idiomes établis ont émergé dans la communauté Compose qui rendent les fonctions Composable plus lisibles et prévisibles. Le premier est State Hoisting : l’état est remonté à un niveau supérieur, et la fonction Composable le reçoit via des paramètres. Cela rend la fonction pure et réutilisable dans différents contextes.
Le deuxième idiome est les paramètres Event-driven. Au lieu de passer une ViewModel ou un useCase à une fonction Composable, seuls des callbacks spécifiques sont passés : onSave, onDelete, onNavigateToDetail. Cela réduit le couplage et simplifie les tests — un test pour ProductItem n’a pas besoin de ViewModel, seulement d’un stub lambda.
Le troisième idiome est CompositionLocal pour passer des données partagées à travers l’arbre de composition. Le thème, la densité de l’écran, la route actuelle — tout cela est passé via CompositionLocal, évitant des chaînes de paramètres à travers des dizaines de fonctions Composable. Cependant, il ne faut pas abuser de CompositionLocal : les paramètres explicites sont toujours préférables aux dépendances implicites.
// State Hoisting : état remonté à la fonction parente
@Composable
fun CounterDisplay(
count: Int,
onIncrement: () -> Unit
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Compteur : $count", style = MaterialTheme.typography.headlineLarge)
Button(onClick = onIncrement) {
Text("+1")
}
}
}
// Utilisation avec State Hoisting
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
CounterDisplay(
count = count,
onIncrement = { count++ }
)
}
Foire aux questions
Oui, return est autorisé, mais avec précaution. Compose optimise la recomposition au niveau des fonctions individuelles, et un return précoce peut briser cette optimisation. Il est préférable d’utiliser des opérateurs conditionnels if ou within du corps de la fonction.
En Kotlin, Unit est un objet singleton, pas un type vide. Les fonctions Composable retournent Unit, ce qui signifie techniquement qu’elles retournent l’objet Unit lui-même. Cependant, en pratique cela n’a pas d’importance — la valeur de retour est ignorée par le système de composition.
Passer des collections mutables est possible, mais c’est une mauvaise pratique. Si la collection change, Compose ne le saura pas car la référence d’objet reste la même. Utilisez des listes immutables ou mutableStateListOf pour les changements suivis.
Pour le débogage, utilisez Android Studio avec Layout Inspector, qui montre l’arbre actuel des fonctions Composable, les valeurs des paramètres et les raisons de la recomposition. Le débogueur Kotlin normal fonctionne également — les points d’arrêt dans les fonctions Composable se déclenchent correctement à chaque recomposition.
Une fonction Composable retourne toujours Unit, donc le type de retour n’est pas spécifié. Essayer de retourner un autre type provoquera une erreur de compilation car l’annotation @Composable est incompatible avec les types de retour non Unit.
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