陰影可從視覺上提升 UI,向使用者表明互動性,並針對使用者動作提供即時回饋。Compose 提供多種方式,可將陰影納入應用程式:
Modifier.shadow()
:在符合 Material Design 指南的可組合函式後方,建立以高度為準的陰影。Modifier.dropShadow()
:建立可自訂的陰影,顯示在可組合函式後方,讓函式看起來有立體感。Modifier.innerShadow()
:在可組合函式的邊界內建立陰影,使其看起來像是壓入後方表面。
Modifier.shadow()
適合建立基本陰影,而 dropShadow
和 innerShadow
修飾符則可更精細地控制陰影的算繪方式。
本頁說明如何實作這些修飾符,包括如何在使用者互動時製作陰影動畫,以及如何串連 innerShadow()
和 dropShadow()
修飾符來建立漸層陰影、擬物化陰影等。
建立基本陰影
Modifier.shadow()
會根據 Material Design 指南建立基本陰影,模擬來自上方的光源。陰影深度取決於 elevation
值,而投射陰影會裁剪為可組合函式的形狀。
@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
建立的陰影,以高度為準。實作投射陰影
使用 dropShadow()
修飾符在內容後方繪製精確的陰影,讓元素看起來有立體感。
您可以透過 Shadow
參數控制下列重要層面:
radius
:定義模糊效果的柔和度和擴散程度。color
:定義色調的顏色。offset
:沿著 x 軸和 y 軸放置陰影的幾何圖形。spread
:控制陰影幾何圖形的擴展或收縮。
此外,shape
參數會定義陰影的整體形狀。它可以採用 androidx.compose.foundation.shape
套件中的任何幾何圖形,以及 Material Expressive 形狀。
如要實作基本投射陰影,請在可組合函式鏈結上新增 dropShadow()
修飾符,並提供半徑、顏色和擴散。請注意,顯示在陰影頂端的 purpleColor
背景是在 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 ) } } }
程式碼重點
dropShadow()
修飾元會套用至內部Box
。陰影具有下列特徵:- 圓角矩形 (
RoundedCornerShape(20.dp)
) - 模糊半徑為
10.dp
,使邊緣柔和且擴散 - 擴散值
6.dp
,可擴大陰影大小,使陰影大於投射陰影的方塊 - Alpha 值為
0.5f
,使陰影呈現半透明狀態
- 圓角矩形 (
- 定義陰影後,已套用
background()
修飾符。Box
填滿白色。- 背景會裁剪成與陰影相同的圓角矩形。
結果

導入內陰影
如要建立 dropShadow
的反向效果,請使用 Modifier.innerShadow()
,這會產生元素凹陷或壓入底層表面的錯覺。
建立內陰影時,順序十分重要。內陰影會繪製在內容的頂端,因此您通常應執行下列操作:
- 繪製背景內容。
- 套用
innerShadow()
修飾符,即可建立凹面外觀。
如果 innerShadow()
放置在背景之前,系統會在陰影上繪製背景,完全遮蓋陰影。
以下範例顯示在 RoundedCornerShape
上套用 innerShadow()
的方式:
@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()
。在使用者互動時為陰影加上動畫效果
如要讓陰影回應使用者互動,可以將陰影屬性與 Compose 的動畫 API 整合。舉例來說,使用者按下按鈕時,陰影可以改變,提供即時的視覺回饋。
以下程式碼會建立帶有陰影的「按下」效果 (表面被推入螢幕的錯覺):
@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", // ... ) } } } }
程式碼重點
- 宣告參數的開始和結束狀態,以便在按下
transition.animateColor
和transition.animateFloat
時產生動畫。 - 使用
updateTransition
並提供所選targetState (targetState = isPressed)
,確認所有動畫都已同步。每當isPressed
變更時,轉場物件會自動管理所有子項屬性的動畫,從目前值變更為新的目標值。 - 定義
buttonPressAnimation
規格,可控制轉場效果的時間和緩和程度。這會指定tween
(中間的簡寫),時間長度為 400 毫秒,並使用EaseInOut
曲線,也就是說動畫開始時速度較慢,中間會加快,最後會減慢。 - 定義
Box
,並使用一連串的修飾符函式套用所有動畫屬性,以建立視覺元素,包括:- .
clickable()
:可讓Box
互動的修飾符。 .dropShadow()
:系統會先套用兩個外部投射陰影,顏色和 Alpha 屬性會連結至動畫值 (blueDropShadow
等),並建立初始的凸起外觀。.innerShadow()
:在背景上繪製兩個內陰影。 這些屬性會連結至另一組動畫值 (innerShadowColor1
等),並建立縮排外觀。
- .
結果
建立漸層陰影
陰影不限於實心顏色,陰影 API 會接受 Brush
,讓您建立漸層陰影。
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 ) }
程式碼重點
dropShadow()
會在方塊後方加上陰影。brush = Brush.sweepGradient(colors)
會使用漸層為陰影著色,並透過預先定義的colors
清單輪流套用顏色,營造彩虹般的效果。
結果
你可以使用筆刷做為陰影,建立具有「呼吸」動畫的漸層 dropShadow()
:
合併陰影
您可以組合及分層使用 dropShadow()
和 innerShadow()
修飾符,建立各種效果。以下各節將說明如何運用這項技術,製作新擬物、新粗獷主義和真實陰影。
建立擬物陰影
新擬物陰影的特色是柔和的外觀,會從背景自然浮現。如要建立擬物化陰影,請按照下列步驟操作:
- 使用與背景顏色相同的元素。
- 套用兩個相對的淺色投射陰影:一個角落套用淺色陰影,另一個角落套用深色陰影。
下列程式碼片段會疊加兩個 dropShadow()
修飾符,建立擬物化效果:
@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) ) }

建立新粗獷主義陰影
新粗獷主義風格的特色是高對比、方塊狀的版面配置、鮮豔的色彩和粗邊框。如要建立這項效果,請使用模糊程度為零的 dropShadow()
,並設定明顯的偏移,如以下程式碼片段所示:
@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 ) } } } }

建立逼真的陰影
真實陰影會模擬實體世界的陰影,看起來像是主要光源照亮,因此會產生直接陰影和較為擴散的陰影。您可以堆疊多個具有不同屬性的 dropShadow()
和 innerShadow()
執行個體,重現逼真的陰影效果,如下列程式碼片段所示:
@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 ) } } }
程式碼重點
- 套用兩個具有不同屬性的鏈結
dropShadow()
修飾符, 然後套用background
修飾符。 - 鏈結的
innerShadow()
修飾符會套用至元件邊緣,形成金屬邊框效果。
結果
上述程式碼片段會產生下列內容:
