シャドウは、UI を視覚的に引き立て、ユーザーにインタラクティブ性を示し、ユーザーのアクションに対するフィードバックを即座に提供します。Compose には、アプリにシャドウを組み込むための方法がいくつか用意されています。
Modifier.shadow()
: マテリアル デザイン ガイドラインに準拠したコンポーザブルの背面に、高度に基づく影を作成します。Modifier.dropShadow()
: コンポーザブルの背後に表示されるカスタマイズ可能な影を作成し、コンポーザブルが浮き上がって見えるようにします。Modifier.innerShadow()
: コンポーザブルの境界線の内側に影を作成し、背後のサーフェスに押し込まれているように見せます。
Modifier.shadow()
は基本的なシャドウの作成に適していますが、dropShadow
修飾子と innerShadow
修飾子は、シャドウ レンダリングのよりきめ細かい制御と精度を提供します。
このページでは、ユーザー操作時に影をアニメーション表示する方法や、innerShadow()
修飾子と dropShadow()
修飾子を連結してグラデーションの影やニューモーフィックの影などを作成する方法など、これらの修飾子を実装する方法について説明します。
基本的なシャドウを作成する
Modifier.shadow()
は、上からの光源をシミュレートする マテリアル デザイン ガイドラインに沿った基本的なシャドウを作成します。影の深さは 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
パッケージの任意のジオメトリと、マテリアル エクスプレッシブ シェイプを使用できます。
基本的なドロップ シャドウを実装するには、コンポーザブル チェーンに 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
の広がり。影のサイズを拡大し、影を投影するボックスよりも大きくします。- アルファ値が
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
仕様を定義します。400 ミリ秒の期間とEaseInOut
カーブのtween
(中間)を指定します。これは、アニメーションがゆっくりと始まり、中盤で加速し、終盤で減速することを意味します。 - 次のものを含む、すべてのアニメーション プロパティを適用してビジュアル要素を作成する修飾子関数のチェーンを含む
Box
を定義します。- .
clickable()
:Box
をインタラクティブにする修飾子。 .dropShadow()
: 2 つの外側のドロップ シャドウが最初に適用されます。色とアルファ プロパティはアニメーション値(blueDropShadow
など)にリンクされ、最初の隆起した外観を作成します。.innerShadow()
: 2 つのインナー シャドウが背景の上に描画されます。これらのプロパティは、アニメーション値の別のセット(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()
修飾子を組み合わせて重ねることで、さまざまな効果を作成できます。以降のセクションでは、この手法を使用してニューモーフィック、ネオブルータリスト、リアルなシャドウを作成する方法について説明します。
ニューモーフィズムのシャドウを作成する
ニューモーフィズムのシャドウは、背景から有機的に現れるソフトな外観が特徴です。ニューモーフィズムのシャドウを作成する手順は次のとおりです。
- 背景と同じ色を使用する要素を使用します。
- 2 つの薄いドロップ シャドウを反対方向に適用します。1 つの角に明るいシャドウ、反対の角に暗いシャドウを適用します。
次のスニペットでは、2 つの 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 ) } } }
コードに関する主なポイント
- 異なるプロパティを持つ 2 つのチェーンされた
dropShadow()
修飾子が適用され、その後にbackground
修飾子が適用されます。 - 連鎖した
innerShadow()
修飾子が適用され、コンポーネントの端に金属製のリム効果が生成されます。
結果
上記のコード スニペットでは、次の結果が生成されます。
