Compose Materialを使う場合の注意点

# 注意点

Compose Materialが提供するComposableはMaterial Designに準拠するように実装されているため、デフォルトの実装のまま使用すると意図したレイアウトにならないことがあります。

# 例

具体的な例を示して説明します。

以下の例は`android.widget.Button`と`androidx.compose.material.Button`を縦に並べたものです。

上のボタンが`android.widget.Button`、下のボタンが`androidx.compose.material.Button`を使って実装したものです。

<img width=280 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617574/532e15f9-3691-4dfc-5421-fe3961fa8dfe.png>


以下のように2つのボタンには横幅、フォントサイズ、フォントウェイト、表示するテキストを同じ値で指定しています。

```xml
<Button
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    android:text="BUTTON"
    android:textSize="12sp"
    android:textStyle="bold" />
```


```kotlin
Button(
    modifier = Modifier.padding(horizontal = 16.dp),
    onClick = {}
) {
    Text(
        text = "BUTTON",
        fontSize = 12.sp,
        fontWeight = FontWeight.Bold
    )
}
```

しかし、2つのボタンをよく見比べてみると`android.widget.Button`よりも`androidx.compose.material.Button`の方が表示されている「BUTTON」の文字間隔が広がっていることがわかると思います。

# 原因

この原因を調べるために`androidx.compose.material.Button`のソースコードを見てみます。


https://androidx.tech/artifacts/compose.material/material/1.4.3-source/androidx/compose/material/Button.kt.html

```kotlin
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    elevation: ButtonElevation? = ButtonDefaults.elevation(),
    shape: Shape = MaterialTheme.shapes.small,
    border: BorderStroke? = null,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    content: @Composable RowScope.() -> Unit
) {
    val contentColor by colors.contentColor(enabled)
    Surface(
        onClick = onClick,
        modifier = modifier.semantics { role = Role.Button },
        enabled = enabled,
        shape = shape,
        color = colors.backgroundColor(enabled).value,
        contentColor = contentColor.copy(alpha = 1f),
        border = border,
        elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
        interactionSource = interactionSource,
    ) {
        CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
            ProvideTextStyle(
                value = MaterialTheme.typography.button
            ) {
                Row(
                    Modifier
                        .defaultMinSize(
                            minWidth = ButtonDefaults.MinWidth,
                            minHeight = ButtonDefaults.MinHeight
                        )
                        .padding(contentPadding),
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically,
                    content = content
                )
            }
        }
    }
}
```

`androidx.compose.material.Button`の内部では`CompositionLocalProvider`を使って`TextStyle`が`MaterialTheme.typography.button`に上書きされています。

次に`MaterialTheme.typography.button`の値を見てみます。

https://androidx.tech/artifacts/compose.material/material/1.4.3-source/androidx/compose/material/Typography.kt.html

```kotlin
button: TextStyle = TextStyle(
    fontWeight = FontWeight.Medium,
    fontSize = 14.sp,
    letterSpacing = 1.25.sp
),
```

`MaterialTheme.typography.button`では`letterSpacing = 1.25.sp`が設定されています。

この設定によって`android.widget.Button`よりも`androidx.compose.material.Button`の方が文字間隔が広げられていることがわかります。

そして、Material Designのガイドラインを見てみると`letterSpacing = 1.25.sp`という値はMaterial Designのガイドラインに従った値であることもわかります。

https://m2.material.io/design/typography/the-type-system.html#type-scale

**`Button`というComposableの関数名だけを見ていると忘れてしまいがちですが、Compose MaterialはデフォルトでMaterial Designに従うように実装されています。**

# 調整方法

`androidx.compose.material.Button`で`android.widget.Button`と同じ文字間隔を実現したい場合は`letterSpacing = 0.sp`を設定すればよいです。

```kotlin
Button(
    modifier = Modifier.padding(horizontal = 16.dp),
    onClick = {}
) {
    Text(
        text = "BUTTON",
        fontSize = 12.sp,
        fontWeight = FontWeight.Bold,
        letterSpacing = 0.sp,
    )
}
```

<img width=280 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617574/565f6d57-85ad-f0f3-e0a7-76be78829077.png>

# まとめ

今回紹介したようにCompose Materialが提供するComposableはMaterial Designに準拠するように実装されているため、デフォルトの実装のまま使用すると意図しないレイアウトになってしまうことがあります。

更にCompose Materialのバージョンアップによって内部の実装が変わり見た目が変わってしまう可能性もあります。

Material Designに準拠するのではなく、独自のデザインに合わせたい場合はCompose Materialが提供するComposableを調整して使うか、Compose Materialを使わずにComposableを自作する必要があります。

またComposeはAndroid Studioでソースコードを簡単に見ることができるので、ライブラリが提供するComposableを使う場合は内部の実装を理解してから使うようにすることも重要だと思います。