ListDetailPaneScaffoldを触ってアダプティブ時代に備えよう

# はじめに
近年のAndroidは、タブレットや折りたたみ式スマホ、車載カーナビ、スマートテレビなど色んな画面サイズが出てきました。
すべての画面サイズに対応しているアプリは中々無いと思いますが、Googleさん的には「対応してほしい！」と思わせるような発表やリリースが相次いでいます。
いずれリジェクト要因として扱われたとき困らないよう、色んな画面サイズに適応できる技術を紹介します！

# Material3 Adaptive
Material3配下にMaterial3 Adaptiveというライブラリが追加されました。ここにアダプティブレイアウトとして機能するComposeが入っています。
[公式のブログ](https://medium.com/androiddevelopers/new-apis-for-adaptive-layouts-in-jetpack-compose-f27cace48bcd)からは`NavigationSuiteScaffold`と`ListDetailPaneScaffold`の２つが紹介されていましたが、今回は`ListDetailPaneScaffold`の使い方をご紹介します。

# ListDetailPaneScaffold
ListDetailPaneScaffoldは、リスト用のコンポーザブルと詳細表示用のコンポーザブルを受け取り、一度に1つずつ表示するか、もしくは両方を並べて表示するかという判断ロジックを処理する新しいコンポーザブルです。
正直説明だけだと伝わらないと思うので、とりあえず動かしてみましょう！

## まずは導入
以下のimprementionをappモジュールのgradle.buildに追加してください。
```
implementation 'androidx.compose.material3:material3-adaptive:1.0.0-alpha01'
```
なお、2023年11月29日に1.0.0-alpha02バージョンが出ていますが、こっちを使うと`rememberListDetailPaneScaffoldState`がimportできなくなるので、注意してください。
まぁ、alphaなので細かいことは気にせず進めていきましょう。

## compileSdkを変更
最近のAndroidStudioはデフォルトでcompileSdkが33になっていると思いますが、material3-adaptive-android:1.0.0-alpha01は34を要求してきます。
```
compileSdk 34
```
ここも変更しておきましょう。


## コードを書いて動かしてみる
ありがたいことにシンプルなサンプルコードを公式が提供してくれています。

https://github.com/android/snippets/blob/87948ac8ef0173a1562ed43d58801d76e8387f99/compose/snippets/src/main/java/com/example/compose/snippets/adaptivelayouts/SampleListDetailPaneScaffold.kt

こちらのコードを使ってビルドすると、こんな感じのリストが出てきました。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323027/bdccf160-57b5-254d-6405-1f65cb84eed1.png">

ちなみにバグなのかわかりませんが、縦長のレイアウトだとタップしてもなにも起きません。
画面サイズに応じて勝手にレイアウトを調整してくれるコンポーザブルなので、詳細表示画面に遷移してほしいところですね。
まぁ、alphaなので細かいことは気にせず進めていきましょう。（２回目）

気を取り直して横長のレイアウトにして、試しに「Cupcake」をタップしてみると？

![20231212_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323027/a17a49c4-89cb-47f0-abee-56c91450a76e.png)

![20231212_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323027/072124e2-1447-0179-61aa-f50bf232af76.png)

おお〜！詳細画面的なものが右側に出てきましたね！
このように、画面サイズに応じてリスト表示だけにするか、詳細表示も合わせて表示するかを勝手に判断してくれます。

スマートフォンでは画面遷移していたけど、タブレットや横持ちスマートフォンでは画面遷移せずに１画面で表示したい！といった要望を叶えてくれる優れものです。安定版が楽しみですね。

# 細かい解説
なんとなくListDetailPaneScaffoldの挙動がわかったところで細かい解説です。

## まずはこの２行
```kotlin
val state = rememberListDetailPaneScaffoldState()
var selectedItem: MyItem? by rememberSaveable { mutableStateOf(null) }
```

ListDetailPaneScaffoldStateはまずはListDetailPaneScaffoldの状態を司るものです。
List、Detail、Extraの３つが用意されていて、初期状態はListになっています。
タップされたらDetailに更新して、詳細表示へと移行します。

２行目はタップしたアイテムが何かを通知するためのmutableStateです。

## メインどころ
```kotlin
ListDetailPaneScaffold(
    scaffoldState = state,
    listPane = {
        MyList(
            onItemClick = { id ->
                // Set current item
                selectedItem = id
                // Display the details pane
                state.navigateTo(ListDetailPaneScaffoldRole.Detail)
            }
        )
    },
    detailPane = {
        // Show the details pane content if selected item is available
        selectedItem?.let { item ->
            MyDetails(item)
        }
    },
)
```
さっき作っておいたListDetailPaneScaffoldStateをstateに入れます。
listPaneにはリスト表示のコンポーザブルを、detailPaneには詳細表示のコンポーザブルをそれぞれ入れます。

リスト表示コンポーザブルのonItemClickでListDetailPaneScaffoldState.navigateTo()を使って、詳細表示へと状態遷移するようにしていますね。
シンプルで実装も簡単です！


これで縦長画面サイズの時に、詳細画面遷移が機能してくれたらもっと遊べるんですが。。。
まぁ、alphaなので（略）（n回目）

## どうしても縦長画面サイズで詳細表示画面に遷移させたい。。。！
自前の実装でどうにかしている人がいました。
詳しく見ていないですが、安定版が待てない方はチェックしてみてください。

https://github.com/jakepurple13/OtakuWorld/blob/5099018e6bffefe331be301a04a1df37c13f60b1/UIViews/src/main/java/com/programmersbox/uiviews/lists/OtakuListScreen.kt#L89C1-L107C20

# 終わりに
明日も@umsysがお送りします。

当日の22時になんとか書き上げたということは。。。？
はい。ネタはこれから考えます。
仕事も普通にありますが、自らを追い込んでいくスタイルで頑張ります。よろしくお願いいたします。