Compose Material3 1.4.0に追加予定のコンポーネントのメモ

Jetpack Compose の Material 3 のライブラリ 1.4.0 に追加されそうなコンポーネントのメモです。
これを書いている時点 (2024/9/7) では 1.4.0 は [Snapshot](https://androidx.dev/) でのみ使用可能で、実際に Stable のタイミングでは変更されている可能性があるので、使用前には公式のドキュメントを参照してください。(気が向いたら更新しておきますが)

## Button Group

Segmented Button に近しい、ボタンをグループ化するコンポーネントです。
Segmented Button との使い分けは不明ですが、Segmented Button と同様に Single select or Multi select の実装が可能です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/6530c40f-40e8-8e4e-016e-5d313e8b5b06.gif" width="300px" />


```kotlin
ButtonGroup {
    val options = listOf("A", "B", "C", "D")
    val checked = remember { mutableStateListOf(false, false, false, false) }
    val modifiers =
        listOf(
            Modifier.weight(1.5f),
            Modifier.weight(1f),
            Modifier.width(90.dp),
            Modifier.weight(1f)
        )
    options.fastForEachIndexed { index, label ->
        ToggleButton(
            checked = checked[index],
            onCheckedChange = { checked[index] = it },
            modifier = modifiers[index]
        ) {
            Text(label)
        }
    }
}
```

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ButtonGroupSamples.kt

## Floating Action Button Menu

Floating Action Button から複数のアクションを展開するボタンです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/f483e4e4-10cb-87e9-2051-060977703ad8.gif" width="300px" />


```kotlin
val items = listOf(
    Icons.AutoMirrored.Filled.Message to "Reply",
    Icons.Filled.People to "Reply all",
    Icons.Filled.Contacts to "Forward",
    Icons.Filled.Snooze to "Snooze",
    Icons.Filled.Archive to "Archive",
    Icons.AutoMirrored.Filled.Label to "Label",
)

var fabMenuExpanded by rememberSaveable { mutableStateOf(false) }

BackHandler(fabMenuExpanded) { fabMenuExpanded = false }

FloatingActionButtonMenu(
    expanded = fabMenuExpanded,
    button = {
        ToggleFloatingActionButton(
            modifier =
                Modifier.animateFloatingActionButton(
                    visible = fabVisible || fabMenuExpanded,
                    alignment = Alignment.BottomEnd
                ),
            checked = fabMenuExpanded,
            onCheckedChange = { fabMenuExpanded = !fabMenuExpanded }
        ) {
            val imageVector by remember {
                derivedStateOf {
                    if (checkedProgress > 0.5f) Icons.Filled.Close else Icons.Filled.Add
                }
            }
            Icon(
                painter = rememberVectorPainter(imageVector),
                contentDescription = null,
                modifier = Modifier.animateIcon({ checkedProgress })
            )
        }
    }
) {
    items.forEachIndexed { i, item ->
        FloatingActionButtonMenuItem(
            onClick = { fabMenuExpanded = false },
            icon = { Icon(item.first, contentDescription = null) },
            text = { Text(text = item.second) },
        )
    }
}
```

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonMenuSamples.kt


## Floating App Bar

Bottom App Bar の Floating バージョンです。(not Navigation Bar,| Navigation Rail)


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/4b05bd1f-8df7-a467-84b4-7f9e7264367a.png" width="300px" />

おそらく大画面用?の縦バージョンもあります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/bed82e2c-969f-b468-3659-d8793b80fa16.png" width="500px" />


また、表示・非表示のアニメーションも折りたたむパターンと非表示にするパターンがあります。

|Expandable|Scrollable|
|:--:|:--:|
| ![ezgif-3-e356720b7d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/165d9c1e-8f74-6f2c-0c75-59d8b1afedf2.gif)| ![ezgif-3-ced7afb2da.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/dde618f4-be11-3014-9764-dc8b3706ba96.gif)|




```kotlin
HorizontalFloatingAppBar(
    expanded = expanded || isTouchExplorationEnabled,
    leadingContent = { 
        IconButton(onClick = { /* doSomething() */ }) {
            Icon(Icons.Filled.Check, contentDescription = "Localized description")
        }
        IconButton(onClick = { /* doSomething() */ }) {
            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
        }
    },
    trailingContent = { 
        IconButton(onClick = { /* doSomething() */ }) {
            Icon(Icons.Filled.Download, contentDescription = "Localized description")
        }
        IconButton(onClick = { /* doSomething() */ }) {
            Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
        }
    },
    content = {
        FilledIconButton(
            modifier = Modifier.width(64.dp),
            onClick = { /* doSomething() */ }
        ) {
            Icon(Icons.Filled.Add, contentDescription = "Localized description")
        }
    },
)

```


https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingAppBarSamples.kt


## ContainedLoadingIndicator

MaterialShapes をアニメーションさせる Loading Indicator です。
デフォルトでは以下の Shapes がループするようになっています。
  - MaterialShapes.SoftBurst
  - MaterialShapes.Cookie9Sided
  - MaterialShapes.Pentagon
  - MaterialShapes.Pill
  - MaterialShapes.Sunny
  - MaterialShapes.Cookie4Sided
  - MaterialShapes.Oval

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/fec381e9-48a6-2cc0-4e6c-6969cd66f156.gif" width="300px" />


```kotlin
ContainedLoadingIndicator()
```

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/LoadingIndicatorSamples.kt;l=75

## WavyProgressIndicator

波のようなエフェクトのインジケータです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/515216cb-cf8b-d5ea-0e70-7f6ec6dfc124.gif" width="300px" />


```kotlin
CircularWavyProgressIndicator()
LinearWavyProgressIndicator()
```


## WideNavigationRail

NavigationRail を拡張したバージョンです。
タブレットやデスクトップのような大画面で使用します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/161a767b-e5a4-a299-a175-ea7d0da2907b.png" width="300px" />

WideNavigationRail にはレスポンシブに拡張/折りたたむパターンと、Modal で表示するパターンが存在します。

|Responsive|Modal|
|:--:|:--:|
| ![ezgif-3-7b84576d3c.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/49f48f75-c6c6-6d63-fdc1-6059e59d0e27.gif)|![ezgif-3-bce9fc1f8b.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/45cbb65a-a689-800d-ef79-5ce0dbce0d66.gif) |


```kotlin
var selectedItem by remember { mutableIntStateOf(0) }
val items = listOf("Home", "Search", "Settings")
val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
val unselectedIcons =
    listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
WideNavigationRail(expanded = true) {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            railExpanded = true,
            icon = {
                Icon(
                    if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
                    contentDescription = null
                )
            },
            label = { Text(item) },
            selected = selectedItem == index,
            onClick = { selectedItem = index }
        )
    }
}
```


https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationRailSamples.kt


## Split Button

先頭にメインのアクション、末尾にメインのアクションに関連する別のアクションを配置できるボタンです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/5198684e-bea7-8abe-65ac-162f3c346503.png" width="300px" />


```kotlin
var checked by remember { mutableStateOf(false) }
SplitButton(
    leadingButton = {
        SplitButtonDefaults.LeadingButton(
            onClick = { /* Do Nothing */ },
        ) {
            Icon(
                Icons.Filled.Edit,
                modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
                contentDescription = "Localized description",
            )
            Spacer(Modifier.size(ButtonDefaults.IconSpacing))
            Text("My Button")
        }
    },
    trailingButton = {
        SplitButtonDefaults.TrailingButton(
            onClick = { checked = !checked },
            checked = checked,
            modifier = Modifier.semantics {
                stateDescription = if (checked) "Checked" else "Unchecked"
                contentDescription = "Toggle Button"
            },
        ) {
            Icon(
                Icons.Filled.KeyboardArrowDown,
                modifier = Modifier.size(SplitButtonDefaults.TrailingIconSize),
                contentDescription = "Localized description"
            )
        }
    }
)
```

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt


* * *

多くのコンポーネントが追加されてきており、近々 Material Design 3 のガイドラインの方にも大きなアップデートがありそうですね。