Compose Material3 Adaptive Navigation Suite を使ってみる

Compose Material3 Adaptive Navigation Suite の 1.0.0-alpha01 が 2023/11/15 にリリースされたので、まだ alpha ではあるもののどのようなものか使ってみたメモのようなものになります。

https://developer.android.com/jetpack/androidx/releases/compose-material3#adaptive-navigation-1.0.0-alpha01

## 使い方

以下のライブラリの依存を追加します。
```
androidx.compose.material3:material3-adaptive-navigation-suite：1.0.0-alpha01
```

`NavigationSuiteScaffold` を使って Navigation で使用するアイテムの実装を行います。

```kotlin
@Composable
fun SampleApp() {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route
    val routes = remember { RootDestination.entries.map { it.route } }

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            RootDestination.entries.forEach { destination ->
                val selected = currentRoute == destination.route
                item(
                    icon = { Icon(...) },
                    label = { Text(...) },
                    selected = selected,
                    onClick = { ... }
                )
            }
        }
    ) {
        // Screen content
    }
}
```

基本はこれだけで NavigationBar ↔︎ NavigationRail が自動で切り替わるようになります。
デフォルトでは以下のようなパターンで切り替わるようになっており、デフォルトには NavigationDrawer のパターンはありません。

|画面の状態|Type|
|:--:|:--:|
| フォルダブルで TableTop モードもしくは Phone 横向きなどの　HeightSizeClass == Compactの時 | NavigationBar　|
| WidthSizeClass >= Medium | NavigationRail |
| それ以外 | NavigationBar　|

※ちなみにここでの NavigationDrawer はハンバーガーメニューがない、Expanded な状態の NavigationDrawer を指しています。

以下のような見た目になります。

|||
|:--:|:--:|
|NavigationBar |<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/bb039e26-88f8-c5c6-6510-2b6ac1bec67c.png" width="300px" />|
|NavigationRail|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/2c32d402-d196-9b88-43c6-3e009ad4299d.png" width="300px" />|
|NavigationDrawer|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/3601ad70-53c8-cc55-c7ce-ff1b4ff1f7c1.png" width="300px" />|


## カスタマイズ
### 画面サイズごとに使用する Navigation の種類を変更したい
`NavigationSuiteScaffold` の引数にある `NavigationSuiteType` で使用する Navigation の種類を変更することができます。

例えば WidthSizeClass == Expanded の時に NavigationDrawer を使いたい場合は以下のようにして `NavigationSuiteType` を変えることで変更することが可能です。

```kotlin
val adaptiveInfo = currentWindowAdaptiveInfo()
val customNavSuiteType = if (windowSizeClass.widthSizeClass >= Expanded) {
    NavigationSuiteType.NavigationDrawer
} else {
    NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(adaptiveInfo)
}

NavigationSuiteScaffold(
    layoutType = customNavSuiteType
) { ... }
```

### Navigation のアイテムをカスタマイズしたい

`NavigationBarItem` や `NavigationRailItem` のカスタムと同じ API の範囲であればラベルの常に表示や色などはカスタマイズ可能です。
色に関しては `NavigationSuiteScaffold` の引数から手軽にまとめて変更可能です。

```kotlin
fun item(
    selected: Boolean,
    onClick: () -> Unit,
    icon: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    label: @Composable (() -> Unit)? = null,
    alwaysShowLabel: Boolean = true,
    badge: (@Composable () -> Unit)? = null,
    colors: NavigationSuiteItemColors? = null,
    interactionSource: MutableInteractionSource = MutableInteractionSource()
)
```

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation-suite/NavigationSuiteScaffold.kt;l=311-322

※ `NavigationBarItem` や `NavigationRailItem` の標準の API でカスタムできること以上のことはできない

### 特定の条件で NavigationSuite を隠したい

`NavigationSuiteScaffold` ではなくて `NavigationSuiteScaffoldLayout` と `NavigationSuite` を自分で組み合わせることで `NavigationSuite` を隠す処理を実装できます。　
(`NavigationSuiteScaffold` の内部実装は `NavigationSuiteScaffoldLayout` と `NavigationSuite` を組み合わせたものになっている)

例えば MultibackStack を使わない、遷移した先で `NavigationSuite` を非表示にしたい場合は以下のようなコードで非表示にすることが可能です。

```kotlin
@Composable
fun SampleApp() {
    ...
    val navSuiteType = NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(currentWindowAdaptiveInfo())
    
    NavigationSuiteScaffoldLayout(
        layoutType = navSuiteType,
        navigationSuite = {
            if (isRootScreen) {
                NavigationSuite(
                    layoutType = navSuiteType
                ) {
                    RootDestination.entries.forEach { destination ->
                        item( ... )
                    }
                }
            }
        }
    ) {
        Surface(
            color = MaterialTheme.colorScheme.background,
            contentColor = MaterialTheme.colorScheme.onBackground
        ) {
            // Screen content
        }
    }
}
```

* * * 

Compose Material3 Adaptive Navigation Suite は NavigationBar ↔︎ NavigationRail ↔︎ NavigationDrawer を少ない実装で切り替えてくれるライブラリになっています。  
これまでだと Navigation のアイテムがそれぞれ別の Composable fun になっていて、共通のコードに落とし込むことが面倒だった部分が改善されていたり、大画面かどうかのロジックがライブラリで提供されるようになり、大画面対応がこれまで以上に対応しやすくなりそうですね。