Android Jetpack Composeの画面遷移（NavController）

# Jetpack Composeの画面遷移

NavControllerを使用して、Composable間の画面遷移を行います。

## 環境
AndroidStudio：Flamingo | 2022.2.1 Patch 2
OS：macOS

## Step1.ライブラリの追加
執筆時点（2023/6/28）での最新は2.6.0です。
navigation-composeを追加します。

```
dependencies {
    implementation platform('androidx.compose:compose-bom:2022.10.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.ui:ui-graphics'
    implementation 'androidx.compose.ui:ui-tooling-preview'
    implementation 'androidx.compose.material3:material3'
    implementation 'androidx.navigation:navigation-compose:2.6.0'
}
```

## Step2.トップ画面と遷移先画面を作成
トップ画面には、遷移先画面へ移動するためのボタンを一つ作ります。
遷移先画面には、[JetpackComposeのチュートリアル](https://qiita.com/jozuko_dev/items/03c79fc9a0b1b9d990fc)で作成したメッセージ一覧画面を流用します。

- トップ画面
ボタンを画面の真ん中に表示しています。
[IconButton](https://github.com/jozuko/ComposeTutorial/blob/main/app/src/main/java/com/jozu/jetpack/compose/tutorial/screen/composable/IconButton.kt)は、カスタムComposeです。
```kotlin:TopScreen.kt
@Composable
fun TopScreen(onNavigateToConversation: () -> Unit) {
    Surface(modifier = Modifier.fillMaxSize()) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier.padding(8.dp),
            verticalArrangement = Arrangement.Center,
        ) {
            IconButton(label = "Greeting", onClick = onNavigateToConversation)
        }
    }
}
```
- 遷移先画面
```kotlin:ConversationScreen.kt
@Composable
fun ConversationScreen() {
    Surface(modifier = Modifier.fillMaxSize()) {
        Conversation(SampleData.conversationSample)
    }
}
```
## Step3.MainActivityに遷移情報を追加する
画面遷移は、navController.navigation("画面名")で行います。
画面名は、文字列直書きでいいんですが、個人的に、重複と文字列直書きがいやなので、enum classを作成して、そのnameプロパティを指定しています。
```kotlin:MainActivity.kt
setContent {
    ComposeTutorialTheme {
        val navController = rememberNavController()
        NavHost(navController = navController, startDestination = Nav.TopScreen.name) {
            composable(route = Nav.TopScreen.name) {
                TopScreen(
                    onNavigateToConversation = { navController.navigate(Nav.ConversationScreen.name) },
                )
            }
            composable(route = Nav.ConversationScreen.name) { ConversationScreen() }
        }
    }
}
```
```kotlin:Nav.kt
enum class Nav {
    TopScreen,
    ConversationScreen,
}
```

## いざ実行
最初にボタン一つの画面が表示され、ボタンを押すと遷移先画面が表示されます。[コードはこちら](https://github.com/jozuko/ComposeTutorial/tree/d6fb10fa5779298853cc44982d007b0299415645)
![navcontroller_AdobeExpress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491965/eba01a20-45cc-3c14-a4ca-cb4b67d3b74c.gif)

## Step4.引数を渡す
トップ画面から遷移先画面に移動する際に、引数を渡します。
例として、タイトルとして表示する文字列と日時のミリ秒を渡し、遷移先画面の一番上に表示させてみます。

トップ画面から遷移先画面を呼び出す際、navController.navigateに引数として使用するroute名に「route名＋"/"+引数＋"/"+引数...」を指定します。(この値はURIと同じエスケープが必要です)

遷移先画面では、route名の変更（引数の追加）、引数の定義を行います。
型定義を省略した場合、すべて文字列として扱われます。

```kotlin:MainActivity.kt
composable(route = Nav.TopScreen.name) {
    val title = URLEncoder.encode("/\\ Conversationタイトル /\\", "UTF-8")
    val timeInMillis = System.currentTimeMillis()
    TopScreen(
        onNavigateToConversation = {
            navController.navigate("${Nav.ConversationScreen.name}/$title/$timeInMillis")
        },
    )
}
composable(
    route = "${Nav.ConversationScreen.name}/{title}/{time}",
    arguments = listOf(
        navArgument("title") { type = NavType.StringType },
        navArgument("time") { type = NavType.LongType },
    ),
) { backStackEntry ->
    val title = URLDecoder.decode(backStackEntry.arguments?.getString("title") ?: "", "UTF-8")
    val timeInMillis = backStackEntry.arguments?.getLong("time")
    ConversationScreen(title, timeInMillis)
}
```

## いざ実行
遷移先画面の一番上に、文字列と数値（日時のミリ秒）が表示されました。[コードはこちら](https://github.com/jozuko/ComposeTutorial/tree/a351010ac6c209ca97d475300304b10c324b8d91)
![Screenshot_nav_args.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491965/397d338c-2942-fc39-d4f5-9ad3a3756a62.png)