Composeでシンプルな画面遷移の実装

シンプルな画面遷移の実装録です。

参考

https://developer.android.com/guide/navigation

# TODO
- Routeの定義
- NavControllerの作成
- NavHostの作成
- 画面遷移の実装

# Setup

- Navigation Composeの導入
- Serialization の導入

```libs.versitions.toml
[versions]
navigationCompose = "2.8.5"
kotlinSerialization = "1.9.0"

[libraries]
androidx-navigation-compose = {group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose"}
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }

[plugins]

kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinSerialization" }
```

# Routeの定義
- ルートを型で表現することで、存在しない画面への遷移ミスを防ぐ
- 内部的にはルートを文字列として扱っているためシリアライズ処理が必要


```App.kt
object Route {
    @Serializable
    data object FirstPage

    @Serializable
    data object SecondPage

    @Serializable
    data object ThirdPage
}
```



 # NavControllerの作成

- [rememberNavController()](https://developer.android.com/reference/kotlin/androidx/navigation/compose/package-summary#rememberNavController(kotlin.Array))を使う
- fun App()の引数など、Composable 階層の高い位置で作成する、これはすべての画面ここを参照して画面遷移するため
- 状態をより高位のComposableで管理する状態ホイスティングの考え方を使う
 
 ```App.kt
@Composable
fun App(
    navController: NavHostController = rememberNavController()
) {
    // code //
}
 ```

# NavHostの作成

- NavHostの役割は、バックスタック最上位の画面表示と、ナビゲーショングラフの定義
- 引数のnavControllerに先ほど定義したnavControllerを、startDestinationにナビゲーショングラフの起点となるルートを設定する
- ナビゲーショングラフの実態はNavHostのラムダ内で定義する（composable<ルート>）

```App.kt
@Composable
fun App(
    navController: NavHostController = rememberNavController()
) {
    NavHost(
    navController = navController,
    startDestination = Route.FirstScreen
    ) {
        composable<Route.FirstPage> { FirstScreen(
            onNavigateToSecondScreen = { navController.navigate(Route.SecondPage) }
        ) }

        composable<Route.SecondPage> { SecondScreen(
            onNavigateToThirdScreen = { navController.navigate(Route.ThirdPage) }
        ) }

        composable<Route.ThirdPage> { ThirdScreen(
            onNavigateToThirdScreen = { navController.navigate(Route.ThirdPage) }
        ) }
    }
}
```

# 画面遷移の実装

```FirstScreen.kt
@Composable
fun FirstScreen(
    onNavigateToSecondScreen: () -> Unit
) {
    Scaffold(
        bottomBar = {
            BottomAppBar(
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                contentColor = MaterialTheme.colorScheme.primary,
            ) {
                Text(
                    modifier = Modifier
                        .fillMaxWidth(),
                    textAlign = TextAlign.Center,
                    text = "Bottom app bar",
                )
            }
        },
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding),
        ) {
            Image(
                painter = painterResource(id = R.drawable.sample_icon),
                contentDescription = "サンプルアイコン",
                Modifier.clickable { onNavigateToSecondScreen() } // タップ時にラムダを実行してイベントを通知
            )
        }
    }
}
```

# 補足

## 状態ホイスティングとは

:::note info
状態を定義する場所を上位のコンポーザブルに移動することを状態ホイスティングと呼びます。上位のコンポーザブルから下位のコンポーザブルに状態を渡し、下位のコンポーザブルから上位のコンポーザブルにイベントを渡します。
これにより状態を一元管理することができます。
:::