Cienie wizualnie podnoszą poziom interfejsu, wskazują użytkownikom interaktywność i zapewniają natychmiastową reakcję na ich działania. Compose udostępnia kilka sposobów dodawania cieni do aplikacji:
Modifier.shadow()
: tworzy cień na podstawie wysokości za elementem kompozycyjnym, który jest zgodny z wytycznymi Material Design.Modifier.dropShadow()
: tworzy cień z możliwością dostosowania, który pojawia się za elementem kompozycyjnym, dzięki czemu wygląda on na podniesiony.Modifier.innerShadow()
: tworzy cień wewnątrz obramowania komponentu kompozycyjnego, dzięki czemu wygląda on, jakby był wciśnięty w powierzchnię za nim.
Modifier.shadow()
nadaje się do tworzenia podstawowych cieni, a modyfikatory dropShadow
i innerShadow
zapewniają większą kontrolę i precyzję renderowania cieni.
Na tej stronie opisujemy, jak wdrożyć każdy z tych modyfikatorów, w tym jak animować cienie w reakcji na interakcję użytkownika oraz jak łączyć modyfikatory innerShadow()
i dropShadow()
, aby tworzyć cienie gradientowe, cienie neumorficzne i inne.
Tworzenie podstawowych cieni
Modifier.shadow()
tworzy podstawowy cień zgodnie z wytycznymi Material Design, który symuluje źródło światła z góry. Głębokość cienia jest określana na podstawie wartości elevation
, a cień rzucany jest przycinany do kształtu komponentu.
@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
.Implementacja cieni
Użyj modyfikatora dropShadow()
, aby narysować dokładny cień za treścią, dzięki czemu element będzie wyglądał na podniesiony.
Za pomocą parametru Shadow
możesz kontrolować te kluczowe aspekty:
radius
: określa miękkość i rozproszenie rozmycia.color
: określa kolor odcienia.offset
: określa położenie geometrii cienia wzdłuż osi x i y.spread
: kontroluje rozszerzanie lub kurczenie geometrii cienia.
Dodatkowo parametr shape
określa ogólny kształt cienia. Może używać dowolnej geometrii z pakietu androidx.compose.foundation.shape
, a także wyrazistych kształtów Material.
Aby zaimplementować podstawowy cień, dodaj modyfikator dropShadow()
do łańcucha funkcji kompozycyjnych, podając promień, kolor i rozmiar. Pamiętaj, że tło
purpleColor
, które pojawia się na cieniu, jest rysowane po modyfikatorze
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 ) } } }
Najważniejsze informacje o kodzie
- Modyfikator
dropShadow()
jest stosowany do wewnętrznego elementuBox
. Cień ma te cechy:- Zaokrąglony prostokąt (
RoundedCornerShape(20.dp)
) - Promień rozmycia
10.dp
, dzięki czemu krawędzie są miękkie i rozproszone. - Rozmiar
6.dp
, który powiększa cień i sprawia, że jest on większy niż element, który go rzuca. - wartość alfa
0.5f
, dzięki czemu cień jest półprzezroczysty;
- Zaokrąglony prostokąt (
- Po zdefiniowaniu cienia .Zastosowano modyfikator
background()
.- Ikona
Box
jest wypełniona białym kolorem. - Tło jest przycięte do tego samego zaokrąglonego prostokąta co cień.
- Ikona
Wynik

Implementowanie cieni wewnętrznych
Aby uzyskać efekt odwrotny do dropShadow
, użyj Modifier.innerShadow()
, który sprawia, że element wydaje się wklęsły lub wciśnięty w powierzchnię.
Kolejność ma znaczenie podczas tworzenia wewnętrznych cieni. Cień wewnętrzny jest rysowany nad treścią, więc zwykle należy wykonać te czynności:
- Narysuj zawartość tła.
- Zastosuj modyfikator
innerShadow()
, aby uzyskać wklęsły wygląd.
Jeśli symbol innerShadow()
zostanie umieszczony przed tłem, tło zostanie narysowane nad cieniem, całkowicie go zasłaniając.
Poniższy przykład pokazuje zastosowanie innerShadow()
w przypadku 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()
na prostokącie z zaokrąglonymi rogami.Animowanie cieni podczas interakcji użytkownika
Aby cienie reagowały na interakcje użytkownika, możesz zintegrować właściwości cienia z interfejsami API animacji Compose. Gdy użytkownik naciśnie przycisk, cień może się zmienić, aby zapewnić natychmiastową informację wizualną.
Poniższy kod tworzy efekt „naciśnięcia” z cieniem (iluzja, że powierzchnia jest wciskana w ekran):
@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", // ... ) } } } }
Najważniejsze informacje o kodzie
- Deklaruje stany początkowy i końcowy parametrów, które mają być animowane po naciśnięciu, za pomocą
transition.animateColor
itransition.animateFloat
. - Używa
updateTransition
i przekazuje do niego wybranetargetState (targetState = isPressed)
, aby sprawdzić, czy wszystkie animacje są zsynchronizowane. GdyisPressed
się zmieni, obiekt przejścia automatycznie zarządza animacją wszystkich właściwości podrzędnych od ich bieżących wartości do nowych wartości docelowych. - Definiuje specyfikację
buttonPressAnimation
, która kontroluje czas i przebieg przejścia. Określatween
(skrót od „in-between”) o czasie trwania 400 milisekund i krzywejEaseInOut
, co oznacza, że animacja zaczyna się powoli, przyspiesza w środku i zwalnia na końcu. - Definiuje
Box
z ciągiem funkcji modyfikujących, które stosują wszystkie animowane właściwości do utworzenia elementu wizualnego, w tym:- .
clickable()
: modyfikator, który sprawia, że elementBox
jest interaktywny. .dropShadow()
: najpierw stosowane są 2 zewnętrzne cienie. Ich właściwości koloru i wartości alfa są połączone z wartościami animowanymi (blueDropShadow
itp.) i tworzą początkowy efekt wypukłości..innerShadow()
: na tle są rysowane 2 cienie wewnętrzne. Ich właściwości są połączone z innym zestawem animowanych wartości (innerShadowColor1
itp.) i tworzą wcięcie.
- .
Wynik
Tworzenie cieni gradientowych
Cienie nie muszą być jednolite. Interfejs Shadow API akceptuje wartość Brush
, która umożliwia tworzenie cieni gradientowych.
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 ) }
Najważniejsze informacje o kodzie
dropShadow()
dodaje cień za polem.brush = Brush.sweepGradient(colors)
koloruje cień gradientem, który obraca się w obrębie listy predefiniowanychcolors
, tworząc efekt tęczy.
Wynik
Możesz użyć pędzla jako cienia, aby utworzyć gradient dropShadow()
z animacją „oddechu”:
Łączenie cieni
Możesz łączyć i nakładać na siebie modyfikatory dropShadow()
i innerShadow()
, aby tworzyć różne efekty. Z sekcji poniżej dowiesz się, jak za pomocą tej techniki tworzyć cienie neumorficzne, neobrutalistyczne i realistyczne.
Tworzenie cieni w stylu neomorficznym
Cienie neumorficzne charakteryzują się miękkim wyglądem, który naturalnie wyłania się z tła. Aby utworzyć cienie neumorficzne:
- Używaj elementu, który ma takie same kolory jak tło.
- Zastosuj dwa słabe, przeciwstawne cienie: jasny w jednym rogu i ciemny w przeciwnym.
Poniższy fragment kodu zawiera 2 modyfikatory dropShadow()
, które tworzą efekt neomorficzny:
@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) ) }

Tworzenie cieni w stylu neobrutalizmu
Styl neobrutalistyczny charakteryzuje się układami blokowymi o wysokim kontraście, żywymi kolorami i grubymi obramowaniami. Aby uzyskać ten efekt, użyj dropShadow()
z zerowym rozmyciem i wyraźnym przesunięciem, jak pokazano w tym fragmencie:
@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 ) } } } }

Tworzenie realistycznych cieni
Realistyczne cienie imitują cienie w świecie fizycznym – wyglądają tak, jakby były oświetlone przez główne źródło światła, co powoduje powstanie zarówno bezpośredniego, jak i bardziej rozproszonego cienia. Możesz łączyć ze sobą kilka instancji dropShadow()
i innerShadow()
o różnych właściwościach, aby odtworzyć realistyczne efekty cienia, jak pokazano w tym fragmencie kodu:
@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 ) } } }
Najważniejsze informacje o kodzie
- Zastosowane są 2 połączone modyfikatory
dropShadow()
o różnych właściwościach, a po nich modyfikatorbackground
. - Połączone modyfikatory
innerShadow()
są stosowane w celu uzyskania efektu metalowej obręczy wokół krawędzi komponentu.
Wynik
Poprzedni fragment kodu generuje te dane:
