Les ombres élèvent visuellement votre UI, indiquent l'interactivité aux utilisateurs et fournissent un retour immédiat sur les actions de l'utilisateur. Compose propose plusieurs façons d'intégrer des ombres dans votre application :
Modifier.shadow()
: crée une ombre basée sur l'élévation derrière un composable conforme aux consignes Material Design.Modifier.dropShadow()
: crée une ombre personnalisable qui apparaît derrière un composable, le faisant apparaître comme surélevé.Modifier.innerShadow()
: crée une ombre à l'intérieur des bordures d'un composable, le faisant apparaître comme enfoncé dans la surface derrière lui.
Modifier.shadow()
convient à la création d'ombres de base, tandis que les modificateurs dropShadow
et innerShadow
offrent un contrôle et une précision plus précis sur le rendu des ombres.
Cette page explique comment implémenter chacun de ces modificateurs, y compris comment animer les ombres lors de l'interaction de l'utilisateur et comment enchaîner les modificateurs innerShadow()
et dropShadow()
pour créer des ombres dégradées, des ombres neumorphiques et plus encore.
Créer des ombres de base
Modifier.shadow()
crée une ombre de base suivant les consignes de Material Design qui simule une source de lumière provenant du dessus. La profondeur de l'ombre est basée sur une valeur elevation
, et l'ombre portée est découpée selon la forme du composable.
@Composable fun ElevationBasedShadow() { Box( modifier = Modifier.aspectRatio(1f).fillMaxSize(), contentAlignment = Alignment.Center ) { Box( Modifier .size(100.dp, 100.dp) .shadow(10.dp, RectangleShape) .background(Color.White) ) } }

Modifier.shadow
.Implémenter des ombres projetées
Utilisez le modificateur dropShadow()
pour dessiner une ombre précise derrière votre contenu, ce qui donne l'impression que l'élément est surélevé.
Vous pouvez contrôler les aspects clés suivants grâce à son paramètre Shadow
:
radius
: définit la douceur et la diffusion de votre flou.color
: définit la couleur de la teinte.offset
: positionne la géométrie de l'ombre le long des axes x et y.spread
: contrôle l'expansion ou la contraction de la géométrie de l'ombre.
En outre, le paramètre shape
définit la forme globale de l'ombre. Il peut utiliser n'importe quelle géométrie du package androidx.compose.foundation.shape
, ainsi que les formes expressives Material.
Pour implémenter une ombre portée de base, ajoutez le modificateur dropShadow()
à votre chaîne de composables, en fournissant le rayon, la couleur et l'étendue. Notez que l'arrière-plan purpleColor
qui apparaît au-dessus de l'ombre est dessiné après le modificateur dropShadow()
:
@Composable fun SimpleDropShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(300.dp) .dropShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 6.dp, color = Color(0x40000000), offset = DpOffset(x = 4.dp, 4.dp) ) ) .align(Alignment.Center) .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) ) { Text( "Drop Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
Points clés concernant le code
- Le modificateur
dropShadow()
est appliqué auBox
interne. L'ombre présente les caractéristiques suivantes :- Forme rectangulaire arrondie (
RoundedCornerShape(20.dp)
) - Rayon de flou de
10.dp
, ce qui rend les bords doux et diffus - Une valeur d'étalement de
6.dp
, qui augmente la taille de l'ombre et la rend plus grande que la boîte qui la projette - Un alpha de
0.5f
, qui rend l'ombre semi-transparente
- Forme rectangulaire arrondie (
- Une fois l'ombre définie, le .Le modificateur
background()
est appliqué.Box
est rempli en blanc.- L'arrière-plan est découpé en forme de rectangle arrondi comme l'ombre.
Résultat

Implémenter des ombres intérieures
Pour créer un effet inverse à dropShadow
, utilisez Modifier.innerShadow()
, qui donne l'illusion qu'un élément est en retrait ou enfoncé dans la surface sous-jacente.
L'ordre est important lors de la création d'ombres intérieures. L'ombre intérieure est dessinée au-dessus du contenu. Vous devez donc généralement procéder comme suit :
- Dessinez le contenu de votre arrière-plan.
- Appliquez le modificateur
innerShadow()
pour créer l'apparence concave.
Si innerShadow()
est placé avant l'arrière-plan, celui-ci est dessiné au-dessus de l'ombre, la masquant complètement.
L'exemple suivant montre une application de innerShadow()
sur un RoundedCornerShape
:
@Composable fun SimpleInnerShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) // note that the background needs to be defined before defining the inner shadow .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) .innerShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 2.dp, color = Color(0x40000000), offset = DpOffset(x = 6.dp, 7.dp) ) ) ) { Text( "Inner Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }

Modifier.innerShadow()
sur un rectangle à coins arrondis.Animer les ombres lors d'une interaction utilisateur
Pour que vos ombres réagissent aux interactions des utilisateurs, vous pouvez intégrer des propriétés d'ombre aux API d'animation de Compose. Lorsqu'un utilisateur appuie sur un bouton, par exemple, l'ombre peut changer pour fournir un retour visuel instantané.
Le code suivant crée un effet "appuyé" avec une ombre (l'illusion que la surface est enfoncée dans l'écran) :
@Composable fun AnimatedColoredShadows() { SnippetsTheme { Box(Modifier.fillMaxSize()) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() // Create transition with pressed state val transition = updateTransition( targetState = isPressed, label = "button_press_transition" ) fun <T> buttonPressAnimation() = tween<T>( durationMillis = 400, easing = EaseInOut ) // Animate all properties using the transition val shadowAlpha by transition.animateFloat( label = "shadow_alpha", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) 0f else 1f } // ... val blueDropShadow by transition.animateColor( label = "shadow_color", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) Color.Transparent else blueDropShadowColor } // ... Box( Modifier .clickable( interactionSource, indication = null ) { // ** ...... **// } .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = blueDropShadow, offset = DpOffset(x = 0.dp, -(2).dp), alpha = shadowAlpha ) ) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = darkBlueDropShadow, offset = DpOffset(x = 2.dp, 6.dp), alpha = shadowAlpha ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color(0xFFFFFFFF), shape = RoundedCornerShape(70.dp) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 8.dp, spread = 4.dp, color = innerShadowColor2, offset = DpOffset(x = 4.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 20.dp, spread = 4.dp, color = innerShadowColor1, offset = DpOffset(x = 4.dp, 0.dp), alpha = innerShadowAlpha ) ) ) { Text( "Animated Shadows", // ... ) } } } }
Points clés concernant le code
- Déclare les états de début et de fin des paramètres à animer lors de l'appui avec
transition.animateColor
ettransition.animateFloat
. - Utilise
updateTransition
et lui fournit letargetState (targetState = isPressed)
choisi pour vérifier que toutes les animations sont synchronisées. Chaque fois queisPressed
change, l'objet de transition gère automatiquement l'animation de toutes les propriétés enfants, de leurs valeurs actuelles aux nouvelles valeurs cibles. - Définit la spécification
buttonPressAnimation
, qui contrôle le timing et l'adoucissement de la transition. Il spécifie untween
(abréviation de "in-between") d'une durée de 400 millisecondes et une courbeEaseInOut
, ce qui signifie que l'animation commence lentement, s'accélère au milieu et ralentit à la fin. - Définit un
Box
avec une chaîne de fonctions de modificateur qui appliquent toutes les propriétés animées pour créer l'élément visuel, y compris les suivantes :- .
clickable()
: modificateur qui rendBox
interactif. .dropShadow()
: deux ombres projetées extérieures sont appliquées en premier. Leurs propriétés de couleur et alpha sont liées aux valeurs animées (blueDropShadow
, etc.) et créent l'apparence surélevée initiale..innerShadow()
: deux ombres intérieures sont dessinées au-dessus de l'arrière-plan. Leurs propriétés sont liées à l'autre ensemble de valeurs animées (innerShadowColor1
, etc.) et créent l'apparence en retrait.
- .
Résultat
Créer des ombres en dégradé
Les ombres ne sont pas limitées aux couleurs unies. L'API Shadow accepte un Brush
, qui vous permet de créer des ombres dégradées.
Box( modifier = Modifier .width(240.dp) .height(200.dp) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = animatedSpread.dp, brush = Brush.sweepGradient( colors ), offset = DpOffset(x = 0.dp, y = 0.dp), alpha = animatedAlpha ) ) .clip(RoundedCornerShape(70.dp)) .background(Color(0xEDFFFFFF)), contentAlignment = Alignment.Center ) { Text( text = breathingText, color = Color.Black, style = MaterialTheme.typography.bodyLarge ) }
Points clés concernant le code
dropShadow()
ajoute une ombre derrière la boîte.brush = Brush.sweepGradient(colors)
colore l'ombre avec un dégradé qui parcourt une liste decolors
prédéfinies, créant ainsi un effet arc-en-ciel.
Résultat
Vous pouvez utiliser un pinceau comme ombre pour créer un dégradé dropShadow()
avec une animation "respirante" :
Combiner des ombres
Vous pouvez combiner et superposer les modificateurs dropShadow()
et innerShadow()
pour créer différents effets. Les sections suivantes vous expliquent comment produire des ombres néomorphiques, néobrutalistes et réalistes avec cette technique.
Créer des ombres neumorphiques
Les ombres neumorphiques se caractérisent par une apparence douce qui émerge naturellement de l'arrière-plan. Pour créer des ombres neumorphiques :
- Utilisez un élément dont les couleurs sont identiques à celles de son arrière-plan.
- Appliquez deux ombres portées opposées et légères : une ombre claire sur un angle et une ombre foncée sur l'angle opposé.
L'extrait suivant superpose deux modificateurs dropShadow()
pour créer l'effet neumorphique :
@Composable fun NeumorphicRaisedButton( shape: RoundedCornerShape = RoundedCornerShape(30.dp) ) { val bgColor = Color(0xFFe0e0e0) val lightShadow = Color(0xFFFFFFFF) val darkShadow = Color(0xFFb1b1b1) val upperOffset = -10.dp val lowerOffset = 10.dp val radius = 15.dp val spread = 0.dp Box( modifier = Modifier .fillMaxSize() .background(bgColor) .wrapContentSize(Alignment.Center) .size(240.dp) .dropShadow( shape, shadow = Shadow( radius = radius, color = lightShadow, spread = spread, offset = DpOffset(upperOffset, upperOffset) ), ) .dropShadow( shape, shadow = Shadow( radius = radius, color = darkShadow, spread = spread, offset = DpOffset(lowerOffset, lowerOffset) ), ) .background(bgColor, shape) ) }

Créer des ombres néobrutalistes
Le style néobrutaliste se caractérise par des mises en page en blocs très contrastées, des couleurs vives et des bordures épaisses. Pour créer cet effet, utilisez un dropShadow()
avec un flou nul et un décalage distinct, comme indiqué dans l'extrait suivant :
@Composable fun NeoBrutalShadows() { SnippetsTheme { val dropShadowColor = Color(0xFF007AFF) val borderColor = Color(0xFFFF2D55) Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(0.dp), shadow = Shadow( radius = 0.dp, spread = 0.dp, color = dropShadowColor, offset = DpOffset(x = 8.dp, 8.dp) ) ) .border( 8.dp, borderColor ) .background( color = Color.White, shape = RoundedCornerShape(0.dp) ) ) { Text( "Neobrutal Shadows", modifier = Modifier.align(Alignment.Center), style = MaterialTheme.typography.bodyMedium ) } } } }

Créer des ombres réalistes
Les ombres réalistes imitent les ombres du monde physique. Elles semblent éclairées par une source de lumière principale, ce qui donne lieu à une ombre directe et à une ombre plus diffuse. Vous pouvez empiler plusieurs instances dropShadow()
et innerShadow()
avec différentes propriétés pour recréer des effets d'ombre réalistes, comme indiqué dans l'extrait suivant :
@Composable fun RealisticShadows() { Box(Modifier.fillMaxSize()) { val dropShadowColor1 = Color(0xB3000000) val dropShadowColor2 = Color(0x66000000) val innerShadowColor1 = Color(0xCC000000) val innerShadowColor2 = Color(0xFF050505) val innerShadowColor3 = Color(0x40FFFFFF) val innerShadowColor4 = Color(0x1A050505) Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 40.dp, spread = 0.dp, color = dropShadowColor1, offset = DpOffset(x = 2.dp, 8.dp) ) ) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 0.dp, color = dropShadowColor2, offset = DpOffset(x = 0.dp, 4.dp) ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color.Black, shape = RoundedCornerShape(100.dp) ) // // .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 3.dp, color = innerShadowColor1, offset = DpOffset(x = 6.dp, 6.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 1.dp, color = Color.White, offset = DpOffset(x = 5.dp, 5.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 5.dp, color = innerShadowColor2, offset = DpOffset(x = (-3).dp, (-12).dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 10.dp, color = innerShadowColor3, offset = DpOffset(x = 0.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 9.dp, color = innerShadowColor4, offset = DpOffset(x = 1.dp, 1.dp) ) ) ) { Text( "Realistic Shadows", modifier = Modifier.align(Alignment.Center), fontSize = 24.sp, color = Color.White ) } } }
Points clés concernant le code
- Deux modificateurs
dropShadow()
chaînés avec des propriétés distinctes sont appliqués, suivis d'un modificateurbackground
. - Des modificateurs
innerShadow()
chaînés sont appliqués pour créer l'effet de bordure métallique autour du bord du composant.
Résultat
L'extrait de code précédent produit le résultat suivant :
