Modifier est un objet immuable dans Jetpack Compose qui définit les propriétés d’un composant d’interface : taille, remplissage, fond, gestion des gestes et comportement. Les modificateurs sont combinés en une chaîne par des appels séquentiels, et l’ordre de leur application affecte de manière critique le résultat. Selon Google Android Developers, 2026, une utilisation correcte de Modifier est la base pour construire des interfaces flexibles et performantes en UI déclarative.
Points clés
Modifier est une interface du paquetage androidx.compose.ui qui implémente le patron Composite. Chaque modificateur est un élément de chaîne qui enveloppe le précédent et ajoute son propre comportement. Modifier est immuable — toute modification crée un nouvel objet par copie avec un nouvel élément ajouté à la chaîne. Cela permet de partager en toute sécurité un seul Modifier entre plusieurs composants.
Les fonctions de modificateur de base sont appelées via l’objet compagnon Modifier (par exemple, Modifier.padding(), Modifier.fillMaxWidth()). Chaque fonction retourne un nouveau Modifier avec l’élément ajouté. S’il y a plusieurs modificateurs, ils sont combinés en une chaîne : Modifier.padding(16.dp).fillMaxWidth().background(Color.Blue). L’ordre va de l’extérieur vers l’intérieur par rapport à l’élément d’interface.
Contrairement aux Views traditionnelles où les propriétés étaient définies via des setters (view.setPadding(...), view.setBackground(...)), dans Compose Modifier est une description déclarative. Le composant n’applique pas les modificateurs à l’exécution — LayoutNode parcourt la chaîne Modifier pendant la composition et construit une liste de Modifier.Element qui sont ensuite traités pendant les phases de mesure et de disposition.
L’ordre des modificateurs est l’une des erreurs les plus fréquentes dans Compose. Chaque modificateur enveloppe le précédent, et les opérations sont appliquées de l’extérieur vers l’intérieur. Par exemple, padding(16.dp).clickable { } : d’abord le remplissage est ajouté autour de l’élément, puis la zone de clic inclut le remplissage. clickable { }.padding(16.dp) : la zone de clic est d’abord égale à la taille de l’élément, puis le remplissage est ajouté autour — cliquer sur le remplissage ne fonctionnera pas.
Règle mémotechnique : lisez la chaîne de gauche à droite et appliquez de l’extérieur vers l’intérieur. Le premier modificateur est le plus externe, appliqué à la zone autour de l’élément. Le dernier est le plus interne, appliqué directement au contenu. Les modificateurs de taille (size, fillMaxWidth) doivent venir après le remplissage si le remplissage est nécessaire du parent, ou avant le remplissage si le contenu doit d’abord être contraint puis centré.
Exemple : size(100.dp).padding(10.dp) — élément de taille fixe 100dp, puis remplissage de 10dp à l’extérieur (taille finale 120dp). padding(10.dp).size(100.dp) — le remplissage de 10dp réduit l’espace disponible à (parent - 20dp), puis size(100dp) peut déborder du parent. Réfléchissez toujours à l’ordre délibérément, en utilisant des tests d’affichage pour vérifier le résultat.
| Ordre | Résultat |
|---|---|
| padding → clickable | Le clic fonctionne aussi sur la zone de remplissage |
| clickable → padding | Le clic fonctionne seulement sur le contenu, le remplissage est zone morte |
| size → padding | Élément size(100), remplissage extérieur → 100+2*pad |
| padding → size | Le remplissage réduit l’espace, size peut dépasser les limites |
| background → padding | Le fond remplit tout l’élément y compris la zone externe |
| padding → background | Le fond seulement à l’intérieur du remplissage (zone externe transparente) |
La bibliothèque standard de Compose inclut ~50+ modificateurs divisés en catégories. Taille et positionnement : Modifier.size(), width(), height(), fillMaxSize(), fillMaxWidth(), fillMaxHeight(), defaultMinSize(), requiredSize(). Remplissage et bordures : padding(), offset(), margin (défini via le remplissage du parent ou Layout). Décoration : background(), border(), clip(), alpha(), shadow(), blur().
Comportement et gestes : clickable(), combinedClickable(), pointerInput(), draggable(), swipeable(). Disposition dans le conteneur : weight() (pour Row/Column), align(), alignBy(), matchParentSize(). Sémantique et accessibilité : semantics(), testTag(), clearAndSetSemantics(). Dessin : drawBehind(), drawWithContent(), drawModifier() — modificateurs permettant le dessin personnalisé sur le canevas.
Les modificateurs sémantiques sont une catégorie spéciale. Modifier.semantics {} définit comment l’élément sera représenté dans l’arbre d’accessibilité. Compose remplit automatiquement la sémantique à partir du texte, mais les composants personnalisés nécessitent des rôles, états et actions définis manuellement. Ceci est essentiel pour la conformité WCAG 2.2 et le bon fonctionnement de TalkBack (Android) et VoiceOver (iOS).
@Composable
fun ModifierDemo() {
// Chaîne de modificateurs avec ordre correct
Box(
modifier = Modifier
.size(150.dp)
.padding(8.dp)
.border(2.dp, Color.Gray)
.background(Color(0xFFE3F2FD))
.clickable { /* handle click */ }
.semantics {
contentDescription = "Demo card with click action"
role = Role.Button
}
) {
Text("Touchez-moi")
}
}
Modifier.composed est une méthode d’usine qui permet de créer des modificateurs composites pouvant utiliser d’autres modificateurs, LocalComposition et l’état local. Contrairement à une fonction d’extension régulière, composed crée une instance à chaque application, permettant au modificateur d’avoir son propre état.
Quand utiliser composed : combinaisons récurrentes de modificateurs (par exemple, style de carte standard : padding + background + border + clickable) ; modificateurs avec état (changement de fond animé lors de l’appui) ; accès aux CompositionLocals (schéma de couleurs MaterialTheme, densité de pixels). Pour les cas ordinaires, une fonction d’extension régulière sans composed suffit.
Performances de composed : chaque appel crée un nouvel objet modificateur, ce qui peut entraîner des allocations supplémentaires lors de la recomposition. Pour l’éviter, enveloppez composed dans remember. Google recommande d’utiliser composed seulement lorsque l’état ou CompositionLocal est vraiment nécessaire à l’intérieur. Pour les combinaisons statiques, utilisez des fonctions d’extension régulières.
// Modificateur personnalisé via composed avec état
fun Modifier.cardStyle(
elevation: Dp = 4.dp,
isSelected: Boolean = false
): Modifier = this.composed {
val backgroundColor = if (isSelected)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surface
this
.fillMaxWidth()
.padding(12.dp)
.background(backgroundColor, RoundedCornerShape(8.dp))
.shadow(elevation, RoundedCornerShape(8.dp))
}
// Exemple d’utilisation
@Composable
fun CardList() {
Column {
Box(Modifier.cardStyle()) { Text("Élément 1") }
Box(Modifier.cardStyle(isSelected = true)) { Text("Sélectionné") }
}
}
// Version statique (sans composed) — plus rapide
fun Modifier.simpleCardStyle(): Modifier =
this.fillMaxWidth().padding(8.dp).clip(RoundedCornerShape(4.dp))
Évitez de recréer Modifier à chaque recomposition. Si le modificateur ne dépend pas de données mutables — déplacez-le dans une constante ou remember. Chaque appel à Modifier.padding().background() crée de nouveaux objets Modifier.Element. Dans un composant isolé, c’est négligeable, mais dans un LazyColumn avec des centaines d’éléments, les allocations supplémentaires provoquent un ralentissement notable du défilement.
Règle : si la chaîne de modificateurs ne dépend pas des paramètres de la fonction Composable — déclarez-la comme val en dehors de la fonction (au niveau du fichier ou Companion). Si elle dépend — utilisez remember(dépendance) { ... }. Pour les modificateurs qui sont toujours identiques, val en dehors du Composable est le plus efficace : ces objets sont créés une fois pour toute la durée de vie de l’application.
Bonnes pratiques d’ordre de Modifier : placez les modificateurs dans un ordre logique : d’abord taille/remplissage (disposition), puis décoration (background, border), puis comportement (clickable, pointerInput). Cela améliore non seulement la lisibilité mais aide également Compose Runtime à optimiser la chaîne pendant la mesure. Évitez aussi les éléments Box imbriqués excessifs avec différents Modifier — souvent un seul Modifier sur le conteneur parent peut remplacer 2-3 imbriqués.
// ✅ Bon : constante en dehors du Composable
private val cardModifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clip(RoundedCornerShape(8.dp))
@Composable
fun CardContent() {
Box(cardModifier.background(Color.White)) { ... }
}
// ❌ Mauvais : recréation à chaque recomposition
@Composable
fun BadCard() {
Box(Modifier.fillMaxWidth().padding(16.dp)) { ... }
}
// ✅ Bon : remember pour Modifier dynamique
@Composable
fun DynamicCard(color: Color) {
val modifier = remember(color) {
Modifier.fillMaxWidth().background(color)
}
Box(modifier) { ... }
}
Foire aux questions
Oui, Modifier est immuable, donc un objet peut être utilisé en toute sécurité à plusieurs endroits. Cependant, si vous utilisez un modificateur composed, chaque appel crée une nouvelle instance. Pour les chaînes statiques, une constante ou val en dehors du Composable est la solution optimale.
Utilisez Layout Inspector dans Android Studio — il montre visuellement les limites de chaque Modifier. Pour le débogage programmatique, ajoutez Modifier.border() avec différentes couleurs à chaque étape de la chaîne pour voir les limites de chaque modificateur.
Modifier.then(other) attache la chaîne other à this. Les appels séquentiels (Modifier.a().b()) sont équivalents à Modifier.then(a()).then(b()). Il n’y a pas de différence — c’est le même mécanisme de chaîne. then() est utile lorsque vous devez attacher une chaîne prête depuis une variable.
Modifier.semantics {} définit comment l’élément sera décrit à un lecteur d’écran. Modifier.clickable() ajoute automatiquement le rôle Bouton et Action(OnClick). Pour les gestes personnalisés, vous devez spécifier explicitement semantics. Sans modificateurs sémantiques, les utilisateurs de TalkBack ne pourront pas interagir avec les composants personnalisés.
Modifier.background(color, shape) fonctionne avec les coins, mais clip() doit venir AVANT background pour que les coins soient coupés. Ordre correct : clip(shape).background(color). Si vous devez également couper le contenu intérieur, utilisez clipToBounds() sur le parent.
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