Material 3 Expressive実装例 : Button

Material 3 Expressive の実装例の紹介シリーズです。
今回は Button を紹介します。
(`androidx.compose.material3:material3:1.4.0-alpha15` 時点での内容になります。)

## Material 3 Expressive での変更点

- スタイルや Shape の多様化
  - デフォルトと toggle
  - Elevated、Filled、Tonal、Outlined、Text のスタイル
  - Round と Square の Shape
  - 押下時の Shape のモーション
  - Extra small、Small、Medium、Large、Extra large のサイズ
- Button のもつ padding は 24dp ではなく 16dp が推奨されるように変更

https://m3.material.io/components/buttons/overview

## 実装

### 押下時の Shape アニメーション

`ButtonDefaults.shapes()` を `Button` の `shapes` に設定します。

```kotlin
Button(
    onClick = {},
    shapes = ButtonDefaults.shapes()
) {
    Text("Button")
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/9f7e7678-d019-4048-aec4-62e43f105c92.gif" width="300px" />

### Round と Square の Shape

Button の `shape` が未設定もしくは `ButtonDefaults.shape` を設定すると Round、`ButtonDefaults.squareShape` を設定すると Square になります。

```kotlin
// Round
Button(
    onClick = {},
) {
    Text("Button")
}

// Square
Button(
    onClick = {},
    shape = ButtonDefaults.squareShape,
) {
    Text("Button")
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/bc04f502-5b5e-4c47-a41b-c8b66f31cb94.png" width="300px" />

### Padding を 16dp に変更

`ButtonDefaults.SmallContentPadding` を `contentPadding` に設定することで使用される 16dp の padding が設定できます。 

```kotlin
Button(
    onClick = {},
    contentPadding = ButtonDefaults.SmallContentPadding
) {
    Text("Button")
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/c049c98b-c8e4-487d-823b-76da23bf04a2.png" width="300px" />

### Elevated、Filled、Tonal、Outlined、Text のスタイル

```kotlin
// Elevated
ElevatedButton(
    onClick = {},
) {
    Text("Button")
}

// Filled
Button(
    onClick = {},
) {
    Text("Button")
}

// Tonal
FilledTonalButton(
    onClick = {},
) {
    Text("Button")
}

// Outlined
OutlinedButton(
    onClick = {},
) {
    Text("Button")
}

// Text
TextButton(
    onClick = {},
) {
    Text("Button")
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/9e05170b-53c8-43a0-88d3-93ba2fbe8ee1.png" width="300px" />


### Toggle 

Text を除くそれぞれのスタイルに対して Toggle button が用意されています。

```kotlin
// Elevated 
ElevatedToggleButton(
    checked = checked,
    onCheckedChange = { checked = it },
) {
    Text("Button")
}

// Filled
ToggleButton(
    checked = checked,
    onCheckedChange = { checked = it },
) {
    Text("Button")
}

// Tonal
TonalToggleButton(
    checked = checked,
    onCheckedChange = { checked = it },
) {
    Text("Button")
}

// Outlined
OutlinedToggleButton(
    checked = checked,
    onCheckedChange = { checked = it },
) {
    Text("Button")
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/f83fa3e2-6820-4062-835c-1596048fae27.png" width="300px" />

(左側が `checked = false`)


### Extra small、Small、Medium、Large、Extra large のサイズ

デフォルトでない Small 以外の Button の高さが標準で用意されており、アイコンのサイズや Padding 等が高さによって自動で適切な値になるように実装する形になります。

```kotlin
// Extra small
val height = ButtonDefaults.ExtraSmallContainerHeight
Button(
    onClick = { },
    modifier = Modifier.heightIn(height),
    contentPadding = ButtonDefaults.contentPaddingFor(height)
) {
    Icon(
        painter = painterResource(R.drawable.ic_edit),
        contentDescription = null,
        modifier = Modifier.size(ButtonDefaults.iconSizeFor(height))
    )
    Spacer(Modifier.size(ButtonDefaults.iconSpacingFor(height)))
    Text("Button")
}

// Small(default)
Button(
    onClick = { },
    contentPadding = ButtonDefaults.SmallContentPadding
) {
    Icon(
        painter = painterResource(R.drawable.ic_edit),
        contentDescription = null,
        modifier = Modifier.size(ButtonDefaults.SmallIconSize)
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Button")
}

// Medium
val height = ButtonDefaults.MediumContainerHeight
Button(
    onClick = { },
    modifier = Modifier.heightIn(height),
    contentPadding = ButtonDefaults.contentPaddingFor(height)
) {
    Icon(
        painter = painterResource(R.drawable.ic_edit),
        contentDescription = null,
        modifier = Modifier.size(ButtonDefaults.iconSizeFor(height))
    )
    Spacer(Modifier.size(ButtonDefaults.iconSpacingFor(height)))
    Text("Button")
}

// Large
val height = ButtonDefaults.LargeContainerHeight
Button(
    onClick = { },
    modifier = Modifier.heightIn(height),
    contentPadding = ButtonDefaults.contentPaddingFor(height)
) {
    Icon(
        painter = painterResource(R.drawable.ic_edit),
        contentDescription = null,
        modifier = Modifier.size(ButtonDefaults.iconSizeFor(height))
    )
    Spacer(Modifier.size(ButtonDefaults.iconSpacingFor(height)))
    Text("Button")
}

// Extra large    
val height = ButtonDefaults.ExtraLargeContainerHeight
Button(
    onClick = { },
    modifier = Modifier.heightIn(height),
    contentPadding = ButtonDefaults.contentPaddingFor(height)
) {
    Icon(
        painter = painterResource(R.drawable.ic_edit),
        contentDescription = null,
        modifier = Modifier.size(ButtonDefaults.iconSizeFor(height))
    )
    Spacer(Modifier.size(ButtonDefaults.iconSpacingFor(height)))
    Text("Button")
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/1ff1d5fe-3816-46a2-acc8-06b7fd7c7e9d.png" width="300px" />