Material 3 Expressive実装例 : Navigation rail

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

## Material 3 Expressive での変更点
- これまでの Navigaton rail は非推奨になり、Collapsed と Expanded の 2 パターンが追加され、Collapsed と Expanded の切り替えが可能
  - これまでの Navigation rail の置き換え先には Collapsed
  - Navigation drawer の置き換え先として Expanded
- Expanded な Navigation rail には Non-modal と Modal の 2 パターン
- 選択中のアイテムのラベルの色が On surface variant から Secondary に変更

https://m3.material.io/components/navigation-rail/overview

## 実装

### Collapsed な Navigation rail

Material 3 Expressive では `NavigationRail` ではなくて `WideNavigationRail` を使用します。

```kotlin
WideNavigationRail {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            icon = {
                Icon(
                    painter = painterResource(item.icon),
                    contentDescription = null
                )
            },
            label = { Text(item.label) },
            selected = selectedItem == index,
            onClick = { selectedItem = index }
        )
    }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/ea573ef0-1435-42a4-ad2c-8d6a37dd3ab9.png" width="400px" />


### Expanded で Non-modal な Navigation rail

`WideNavigationRailState` に `WideNavigationRailValue.Expanded` を設定しつつ、`WideNavigationRailItem` にも `railExpanded = true` を設定することで Expanded な Navigation rail が表示できます。

```kotlin
WideNavigationRail(
    state = rememberWideNavigationRailState(initialValue = WideNavigationRailValue.Expanded)
) {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            railExpanded = true,
            icon = {
                Icon(
                    painter = painterResource(item.icon),
                    contentDescription = null
                )
            },
            label = { Text(item.label) },
            selected = selectedItem == index,
            onClick = { selectedItem = index }
        )
    }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/88a1f68f-6abc-48bb-8226-fd43d4e50dbc.png" width="400px" />

### Collapsed と Expanded のシームレスな切り替え

`WideNavigationRailState` の `collapse()` / `expand()` を呼び出すことで Navigation rail のシームレスな開閉を実装することができます。


```kotlin
val state = rememberWideNavigationRailState()
val scope = rememberCoroutineScope()

Row {
    WideNavigationRail(
        state = state,
        header = {
            IconButton(
                modifier = Modifier
                    .padding(start = 24.dp)
                    .semantics {
                        stateDescription =
                            if (state.currentValue == WideNavigationRailValue.Expanded) {
                                "Expanded"
                            } else {
                                "Collapsed"
                            }
                    },
                onClick = {
                    scope.launch {
                        if (state.targetValue == WideNavigationRailValue.Expanded) {
                            state.collapse()
                        } else {
                            state.expand()
                        }
                    }
                }
            ) {
                if (state.targetValue == WideNavigationRailValue.Expanded) {
                    Icon(painterResource(R.drawable.ic_menu_open), "Collapse rail")
                } else {
                    Icon(painterResource(R.drawable.ic_menu), "Expand rail")
                }
            }
        }
    ) {
        items.forEachIndexed { index, item ->
            WideNavigationRailItem(
                railExpanded = state.targetValue == WideNavigationRailValue.Expanded,
                icon = {
                    Icon(
                        painter = painterResource(item.icon),
                        contentDescription = null
                    )
                },
                label = { Text(item.label) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
    ...
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/354168b9-1ef2-4a36-a63d-e5d857a73d44.gif" width="400px" />


### Modal な Navigation rail

Modal な Navigation rail を使う時は `WideNavigationRail` の代わりに `ModalWideNavigationRail` を使用します。

```kotlin
val state = rememberWideNavigationRailState()
val scope = rememberCoroutineScope()

Row {
    ModalWideNavigationRail(
        state = state,
        header = {
            IconButton(
                modifier = Modifier
                    .padding(start = 24.dp)
                    .semantics {
                        stateDescription =
                            if (state.currentValue == WideNavigationRailValue.Expanded) {
                                "Expanded"
                            } else {
                                "Collapsed"
                            }
                    },
                onClick = {
                    scope.launch {
                        if (state.targetValue == WideNavigationRailValue.Expanded) {
                            state.collapse()
                        } else {
                            state.expand()
                        }
                    }
                }
            ) {
                if (state.targetValue == WideNavigationRailValue.Expanded) {
                    Icon(painterResource(R.drawable.ic_menu_open), "Collapse rail")
                } else {
                    Icon(painterResource(R.drawable.ic_menu), "Expand rail")
                }
            }
        }
    ) {
        items.forEachIndexed { index, item ->
            WideNavigationRailItem(
                railExpanded = state.targetValue == WideNavigationRailValue.Expanded,
                icon = {
                    Icon(
                        painter = painterResource(item.icon),
                        contentDescription = null
                    )
                },
                label = { Text(item.label) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
    ...
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/7926a4f1-278f-4978-8292-4220a7fedee3.gif" width="400px" />