Le ombre migliorano visivamente la tua UI, indicano l'interattività agli utenti e forniscono un feedback immediato sulle azioni degli utenti. Compose offre diversi modi per incorporare le ombre nella tua app:
Modifier.shadow()
: crea un'ombra basata sull'elevazione dietro un elemento componibile che rispetta le linee guida di Material Design.Modifier.dropShadow()
: crea un'ombra personalizzabile che appare dietro a un componente, facendolo sembrare sollevato.Modifier.innerShadow()
: crea un'ombra all'interno dei bordi di un componente componibile, facendolo sembrare premuto sulla superficie dietro.
Modifier.shadow()
è adatto per creare ombre di base, mentre i modificatori
dropShadow
e innerShadow
offrono un controllo più granulare e
precisione nel rendering delle ombre.
Questa pagina descrive come implementare ciascuno di questi modificatori, incluso come
animare le ombre in base all'interazione dell'utente e come concatenare i modificatori
innerShadow()
e dropShadow()
per
creare ombre sfumate,
ombre neumorfiche e altro ancora.
Creare ombre di base
Modifier.shadow()
crea un'ombra di base che segue le linee guida di Material Design che simula una sorgente luminosa dall'alto. La profondità
dell'ombra si basa su un valore elevation
e l'ombra proiettata viene ritagliata in base
alla forma del componente componibile.
@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
.Implementare le ombre
Utilizza il modificatore dropShadow()
per disegnare un'ombra precisa dietro i tuoi
contenuti, in modo che l'elemento appaia sollevato.
Puoi controllare i seguenti aspetti chiave tramite il parametro Shadow
:
radius
: Definisce la morbidezza e la diffusione della sfocatura.color
: definisce il colore della tinta.offset
: posiziona la geometria dell'ombra lungo gli assi X e Y.spread
: controlla l'espansione o la contrazione della geometria dell'ombra.
Inoltre, il parametro shape
definisce la forma complessiva dell'ombra. Può
utilizzare qualsiasi geometria dal pacchetto androidx.compose.foundation.shape
, nonché
le forme espressive di Material.
Per implementare un'ombra di base, aggiungi il modificatore dropShadow()
alla catena di componenti, fornendo raggio, colore e diffusione. Tieni presente che lo sfondo
purpleColor
che appare sopra l'ombra viene disegnato dopo il modificatore
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 ) } } }
Punti chiave del codice
- Il modificatore
dropShadow()
viene applicato alBox
interno. L'ombra ha le seguenti caratteristiche:- Una forma rettangolare arrotondata (
RoundedCornerShape(20.dp)
) - Un raggio sfocatura di
10.dp
, che rende i bordi morbidi e diffusi - Una diffusione di
6.dp
, che espande le dimensioni dell'ombra e la rende più grande della casella che la proietta - Un valore alfa di
0.5f
, che rende l'ombra semitrasparente
- Una forma rettangolare arrotondata (
- Dopo aver definito l'ombra, il .Viene applicato il modificatore
background()
.- Il
Box
è riempito di bianco. - Lo sfondo viene ritagliato nella stessa forma rettangolare arrotondata dell'ombra.
- Il
Risultato

Implementare le ombre interne
Per creare un effetto inverso a dropShadow
, utilizza Modifier.innerShadow()
,
che crea l'illusione che un elemento sia incassato o premuto nella
superficie sottostante.
L'ordine è importante quando si creano ombre interne. L'ombra interna viene disegnata sopra i contenuti, quindi in genere devi:
- Disegna i contenuti di sfondo.
- Applica il modificatore
innerShadow()
per creare l'aspetto concavo.
Se innerShadow()
viene posizionato prima dello sfondo, quest'ultimo viene disegnato
sopra l'ombra, nascondendola completamente.
L'esempio seguente mostra un'applicazione di innerShadow()
su 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()
su un rettangolo con angoli arrotondati.Animare le ombre in base all'interazione dell'utente
Per fare in modo che le ombre rispondano alle interazioni degli utenti, puoi integrare le proprietà dell'ombra con le API di animazione di Compose. Quando un utente preme un pulsante, ad esempio, l'ombra può cambiare per fornire un feedback visivo immediato.
Il seguente codice crea un effetto "premuto" con un'ombra (l'illusione che la superficie venga spinta verso il basso dello schermo):
@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", // ... ) } } } }
Punti chiave del codice
- Dichiara gli stati iniziale e finale dei parametri da animare alla pressione
con
transition.animateColor
etransition.animateFloat
. - Utilizza
updateTransition
e fornisce iltargetState (targetState = isPressed)
scelto per verificare che tutte le animazioni siano sincronizzate. Ogni volta cheisPressed
cambia, l'oggetto di transizione gestisce automaticamente l'animazione di tutte le proprietà secondarie dai valori correnti ai nuovi valori di destinazione. - Definisce la specifica
buttonPressAnimation
, che controlla la tempistica e l'accelerazione della transizione. Specifica untween
(abbreviazione di in-between) con una durata di 400 millisecondi e una curvaEaseInOut
, il che significa che l'animazione inizia lentamente, accelera a metà e rallenta alla fine. - Definisce un
Box
con una catena di funzioni modificatrici che applicano tutte le proprietà animate per creare l'elemento visivo, tra cui:- .
clickable()
: un modificatore che rendeBox
interattivo. .dropShadow()
: vengono applicate prima due ombre esterne. Le proprietà di colore e alfa sono collegate ai valori animati (blueDropShadow
e così via) e creano l'aspetto iniziale in rilievo..innerShadow()
: due ombre interne vengono disegnate sopra lo sfondo. Le loro proprietà sono collegate all'altro insieme di valori animati (innerShadowColor1
e così via) e creano l'aspetto rientrato.
- .
Risultato
Creare ombre sfumate
Le ombre non sono limitate ai colori pieni. L'API Shadow accetta un Brush
, che
consente di creare ombre sfumate.
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 ) }
Punti chiave del codice
dropShadow()
aggiunge un'ombra dietro la casella.brush = Brush.sweepGradient(colors)
colora l'ombra con una sfumatura che ruota in un elenco dicolors
predefiniti, creando un effetto arcobaleno.
Risultato
Puoi utilizzare un pennello come ombra per creare una sfumatura dropShadow()
con un'animazione
"pulsante":
Combina ombre
Puoi combinare e sovrapporre i modificatori dropShadow()
e innerShadow()
per
creare una serie di effetti. Le sezioni seguenti mostrano come produrre ombre
neumorfiche, neobrutaliste e realistiche con questa tecnica.
Crea ombre neumorfiche
Le ombre neumorfiche sono caratterizzate da un aspetto morbido che emerge in modo organico dallo sfondo. Per creare ombre neumorfiche:
- Utilizza un elemento che condivida gli stessi colori dello sfondo.
- Applica due ombre sfumate e opposte: un'ombra chiara a un angolo e un'ombra scura all'angolo opposto.
Lo snippet seguente sovrappone due modificatori dropShadow()
per creare l'effetto
neumorfico:
@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) ) }

Creare ombre neobrutaliste
Lo stile neobrutalista mette in mostra layout a blocchi ad alto contrasto, colori vivaci
e bordi spessi. Per creare questo effetto, utilizza un dropShadow()
con sfocatura pari a zero
e un offset distinto, come mostrato nel seguente snippet:
@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 ) } } } }

Creare ombre realistiche
Le ombre realistiche imitano le ombre del mondo fisico: appaiono illuminate da una
sorgente luminosa primaria, con un'ombra diretta e una più diffusa. Puoi impilare più istanze di dropShadow()
e innerShadow()
con
proprietà diverse per ricreare effetti ombra realistici, come mostrato nel
seguente snippet:
@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 ) } } }
Punti chiave del codice
- Vengono applicati due modificatori
dropShadow()
concatenati con proprietà distinte, seguiti da un modificatorebackground
. - I modificatori
innerShadow()
concatenati vengono applicati per creare l'effetto di bordo metallico attorno al bordo del componente.
Risultato
Lo snippet di codice precedente produce quanto segue:
