Jetpack Composeで影(Shadow)を付ける方法と課題

Jetpack Composeでコンポーネントに影を付ける方法とその中で発見した問題点をメモしてます
一番簡単なMaterial Design3のCardコンポーネントを使用する方法とModifierで設定する方法を取り扱っています

::: note info
Cardを使用する方がおすすめです
:::
# 影のつけ方 - Cardコンポーネント
Cardには、Filled Card(塗りつぶしありカード)とOutlined Card(塗りつぶしなしカード)の2種類あります
以下では両方のUI紹介とパラメーターについてまとめています
## Filled Card(塗りつぶしありカード)のUI
```kotlin
Card(
    colors = CardDefaults.cardColors(
        containerColor = MaterialTheme.colorScheme.surfaceVariant,
    ),
    modifier = Modifier
        .size(width = 240.dp, height = 100.dp)
) {
    Text(
        text = "Filled",
        modifier = Modifier
            .padding(16.dp),
        textAlign = TextAlign.Center,
    )
}
```
<img width="300" src="https://developer.android.com/static/develop/ui/compose/images/components/card-filled.png">

## Outlined Card(塗りつぶしなしカード)のUI
```kotlin
OutlinedCard(
    colors = CardDefaults.cardColors(
        containerColor = MaterialTheme.colorScheme.surface,
    ),
    border = BorderStroke(1.dp, Color.Black),
    modifier = Modifier
        .size(width = 240.dp, height = 100.dp)
) {
    Text(
        text = "Outlined",
        modifier = Modifier
            .padding(16.dp),
        textAlign = TextAlign.Center,
    )
}
```
<img width="300" src="https://developer.android.com/static/develop/ui/compose/images/components/card-outlined.png">

## パラメーターについて
<table>
  <thead>
    <tr>
      <th>パラメーター名</th>
      <th>説明</th>
    </tr>
  </thead>
  <tr>
    <td>elevation</td>
    <td>カードの高さ(影の濃さ)の変更</td>
  </tr>
  <tr>
    <td>colors</td>
    <td>塗りつぶしの色の変更</td>
  </tr>
  <tr>
    <td>enabled</td>
    <td>
        Cardコンポーネントの使用可否の変更
        <br>
        (falseの場合はfalse用の色に自動的に変更される)
    </td>
  </tr>
  <tr>
    <td>onClick</td>
    <td>クリックされたときの動作の変更</td>
  </tr>
</table>

例えば影の色を変更したい場合は、elevationに適切な値を渡すことで好みのUIに変更できます
# 影のつけ方 - Modifier
```kotlin
val elevation = 4.dp

Box(
  modifier = modifier
      .wrapContentHeight()
      .shadow(
          elevation = elevation,
      ),
) {
  // Boxの中身
}
```
<img width="300" src="https://vegcale.com/home/wp-content/uploads/2024/07/image.png">

Modifier.shadow()で影を付けることができ、各引数で詳細を設定できます
設定必須の引数はelevationのみで、影の濃さ(Z方向での高さ)をpxで表しています

ちなみに下記はコンポーネントを丸角にして影を付与するコードです
shadow()のshape引数に設定することでコンポーネントの形を変更することができます
```kotlin
val elevation = 4.dp
val cornerRadius = 8.dp

Box(
  modifier = modifier
      .wrapContentHeight()
      .shadow(
          elevation = elevation,
          shape = RoundedCornerShape(
              size = cornerRadius
          ),
      ),
) {
  // Boxの中身
}
```
<img width="300" src="https://vegcale.com/home/wp-content/uploads/2024/07/image-1.png">

## 課題
上記2枚のキャプチャの両方で確認できますが、青枠と上下の影に注目すると、下の影だけグラデーションの部分が高さとして計算されていないように思えます
これを解決するには根本的に変更し、Material Design3のカードを使用するのが一番おすすめです
一応、使用しない場合の回避策(暫定対処)を下記に残しておきます
## 解決方法
```kotlin
val elevation = 4.dp
val cornerRadius = 8.dp
val offsetY = elevation / 2

Box(
  modifier = modifier
      .wrapContentHeight()
      .shadow(
          elevation = elevation,
          shape = RoundedCornerShape(
              size = cornerRadius
          ),
      )
      .offset(
          y = offsetY
      ),
) {
  content()
}
```
offsetを使用し、Boxの位置をY方向に移動させています
褒められる解決方法では全くありませんが、暫定的にはこれでしのげると思います
# おわりに
何かありましたらお気軽にご連絡ください！
「いいね」もお願いします！！

個人ブログではQiitaに載せきれていない内容もあるので、ご興味ありましたら是非みてください！！

https://vegcale.com/