Jetpack Compose の Button に無意識で確保されるスペース

Jetpack Compose で `Button` 系のコンポーネントをレイアウトに詰めていると
指定してないのに謎のスペースが空いてなぜだろうとなりませんか (ならないか……)

たとえば こういうレイアウトをしてみます:

```kotlin
Column {
    Button(onClick = { /*TODO*/ }) {
        Text(text = "Hello")
    }
    Button(onClick = { /*TODO*/ }) {
        Text(text = "World")
    }
}
```

なんか微妙にスペースがあいてることに気づくかもしれません

![M3 Buttons](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/c1f96fd3-7a0d-1ffa-4a61-2918d5728621.png)

[Layout Inspector](https://developer.android.com/studio/debug/layout-inspector?hl=ja) でみるとわかりやすいです:

![M3 Buttons -- Layout Inspector](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/dad43918-de07-5201-c4d5-993b797d24c4.png)


## 理由

そういえば 私たちはタッチしやすくなるように clickable な UI 要素には [タップ ターゲットサイズ](https://support.google.com/accessibility/android/answer/7101858?hl=ja) を考慮する必要があって、
Compose Material はバージョン 1.1.0 から 48 dp を確保してくれているからということですね:

https://m3.material.io/foundations/accessible-design/accessibility-basics

https://m2.material.io/develop/web/supporting/touch-target

> 利用可能なタップ ターゲットがないマテリアル コンポーネントに、最小サイズのタップ ターゲットを追加しました。十分な大きさのタップ ターゲットを確保するためにコンポーネント間のスペースを増やしているため、タップ ターゲットのサイズが考慮されず、コンポーネントのサイズが表示されるサイズのままであることを前提とする、既存の UI が変更される可能性があります。

https://developer.android.com/jetpack/androidx/releases/compose-material?hl=ja#1.1.0-alpha05


> ### タッチ ターゲットのサイズ調整
>
> Compose 1.0 と比較した場合、マテリアル コンポーネントのレイアウトでは、マテリアルのアクセシビリティ ガイドライン（英語）に記載されているタッチ ターゲット サイズ（英語）を満たすようにレイアウト領域が拡大されます。たとえば、RadioButton のタッチ ターゲットは、最小サイズの 48x48 dp に拡大されます。RadioButton のサイズをそれ以下に設定した場合も同様です。

https://android-developers-jp.googleblog.com/2022/03/jetpack-compose-11-now-stable.html

(ところでタップなのかタッチなのか表記が揺れていてどっちなのでしょうね)


## 比較

たしかに `androidx.compose.material:material` の `1.0.0` と `1.1.0` を使ってみて比較すると違いがわかります:

| 1.0.0 | 1.1.0 |
|-------|-------|
| ![androidx.compose.material:material:1.0.0](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/eec60bb4-90fb-f746-e708-2487fea64a79.png) | ![androidx.compose.material:material:1.1.0](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/a2149322-c6ba-0992-c404-381467267771.png) |

Material 3 では `androidx.compose.material3:material3:1.0.0-alpha01` からこの動作ですね。
(リリースがこの変更の後だからそうだろうという感じですね)


## どうしてもなくしたいとき

Composition Local の `LocalMinimumTouchTargetEnforcement` を `false` にすることで、この配慮をなくすことができるということです。

```kotlin
@OptIn(ExperimentalMaterial3Api::class)
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
    Column {
        Button(onClick = { /*TODO*/ }) {
            Text(text = "Hello")
        }
        Button(onClick = { /*TODO*/ }) {
            Text(text = "World")
        }
    }
}
```

![M3 Buttons -- LocalMinimumTouchTargetEnforcement](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/409c5500-cebe-50b3-811c-010db0061bd8.png)

それでも `Button` は通常最低 36 dp の高さが取られるということで、意識しないと異常に小さくすることはできないでしょう:

https://android.googlesource.com/platform/frameworks/support/+/4f2a25b8f8ee02053c57b524a1a6341e1edb9c06/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt#315

これも `Note that you can override it by applying Modifier.heightIn directly on [Button].` って書いてあるように `heightIn` でむりやり小さくはできる…

```kotlin
val buttonPaddingValues = PaddingValues(all = 0.dp)
val buttonModifier = Modifier.heightIn(min = 0.dp, max = 12.dp)

@OptIn(ExperimentalMaterial3Api::class)
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
    Column {
        Button(
            contentPadding = buttonPaddingValues,
            modifier = buttonModifier,
            onClick = { /*TODO*/ },
        ) {
            Text(text = "Hello")
        }
        Button(
            contentPadding = buttonPaddingValues,
            modifier = buttonModifier,
            onClick = { /*TODO*/ },
        ) {
            Text(text = "World")
        }
    }
}
```

![test20221116mintap_–_MainActivity_kt__test20221116mintap_app_main_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/1b0cb2c0-508b-c020-d99f-c53cf4fbf49c.png)

しかしながら…

> 試験運用版の `LocalMinimumTouchTargetEnforcement` コンポジションをローカルで使用し、階層全体でこの動作を無効にできます。ただしこれは、新しい最小サイズに対応して既存の UI を更新するまでの間、一時的なエスケープ ハッチとなることのみを目的としています。
> https://developer.android.com/jetpack/androidx/releases/compose-material?hl=ja#1.1.0-alpha05

脱出ハッチ用途ということで理由がなければありのままのほうがいいでしょう (アナと雪の女王)