Jetpack Compose Navigation(Navigation Arguments, Nested Navigation)

#目次

1. Navigation Arguments, Nested Navigationとは
2. サンプルコード
3. まとめ

##1. Navigation Arguments, Nested Navigationとは

``Navigation Arguments``‥画面遷移時に、画面間で共有できる値。この値は、**{ } (プレースホルダ)**を用い、**arguments**を**型を指定してlist**で管理できます。また、SavedStateHandleを使って、画面遷移時に必要なargumentsを取得できますが、今回は説明を省きます。

**以下の実装を行います(UIの詳細については説明を省きます。)**

![navigation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/c71d4ece-6d36-fa0b-f634-51d726af14d8.gif)


``Nested Navigation``‥デスティネーションを、モジュール化すること。**NavigationHost**以下に、``NavGraph``を作成することで、Navigation間の依存関係を切り離す役割を持っています。(``NavHost``で、呼び出すcomposableをさらにグループで分けることで実装を行います。)

**以下の実装を行います(UIの詳細については説明を省きます。)**

![navigation2 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/71680ec9-a3c8-26a7-4bb9-09a4facd6274.gif)



##2. サンプルコード

Navigation Arguments
---

あるUI要素のアイテムをクリックしたときに、その要素の**iteId=1**を遷移後の画面に表示する実装を仮定しました。

まず、構成画面として以下の2つを仮定しました。

```kotlin
sealed class Screen(val route: String){

    object HomeScreen: Screen("home_screen")
    object DetailScreen: Screen("selected_screen")
}
```

次に、``navgraph``の作成を**NavHost**を用いて以下のように行います。

```kotlin
@Composable
fun NavigationSetUp(
    navController: NavHostController
){

    NavHost(
        navController = navController,
        startDestination = Screen.HomeScreen.route
    ){
        composable(
            route = Screen.HomeScreen.route
        ){
            HomeScreen(navController = navController)
        }
        composable(
            route = Screen.DetailScreen.route + "/{itemId}",
            arguments = listOf(navArgument("itemId") {
                type = NavType.StringType
            })
        ){
            val itemId = it.arguments?.get("itemId").toString()
            DetailScreen(itemId = itemId)
        }
    }
}
```

``DetailScreen``‥このコンポーザブルを呼び出している``NavHost``の``composable``を確認すると、**route**を**{ itemId }というプレースホルダ**とともに取り扱っていることがわかります。そして、**arguments**内で、**arguments**のタイプを決定することができ、``StringType``としてることがわかります。

注意点として、``HomeScreen``でオブジェクトを指定して画面を遷移する際に、**/**の記述がないと正しく遷移が行われないので注意が必要です。

```
navController.navigate(route = Screen.DetailScreen.route + "/1")
```

以上のコードを実行すると、以下のようにできます。

![navigation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/c71d4ece-6d36-fa0b-f634-51d726af14d8.gif)

Nested Navigation
---

```kotlin
NavHost(
        navController = navController,
        startDestination = Screen.HomeScreen.route
    ){
        composable(
            route = Screen.HomeScreen.route
        ){
            HomeScreen(navController = navController)
        }
        composable(
            route = Screen.DetailScreen.route + "/{itemId}",
            arguments = listOf(navArgument("itemId") {
                type = NavType.StringType
            })
        ){
            val itemId = it.arguments?.get("itemId").toString()
            DetailScreen(itemId = itemId)
        }
        composable(
            route = 
        ){}
        composable(
            route = 
        ){}
        composable(
            route = 
        ){}
        composable(
            route = 
        ){}
}
```

上記のように、画面構成が大きくなるにつれ1つの``NavHost``内で遷移を管理することはとても大変になります。そこで、意味のある区分で遷移先を区分することを、**Nested Navigation**といいます。

それでは、サンプルコードに移ります。

```kotlin
@Composable
fun NavigationSetUp(
    navController: NavHostController
){
    NavHost(
        navController = navController,
        startDestination = Screen.HomeScreen.route
    ){
        composable(
            route = Screen.HomeScreen.route
        ){
            HomeScreen(navController = navController)
        }
        composable(
            route = Screen.DetailScreen.route
        ){
            DetailScreen()
        }
        composable(
            route = Screen.LogInScreen.route
        ){
            LogInScreen(navController = navController)
        }
    }
}
```

上記のコードを、意味のあるくくりで分割するには、``navigation``を使用します。``navigation``で、新たに**startDestination, route**を定義でき以下のように区分を行いました。

```kotlin
        navigation(
            startDestination = Screen.LogInScreen.route,
            route = AUTHENTICATE_ROUTE,
        ){
            composable(
                route = Screen.LogInScreen.route
            ){
                LogInScreen(navController = navController)
            }
        }
```

ここで、注意する点は``NavHost``で作成する``navgraph``の関係です。
``navigation``では、``navgraph``において**Route**を作成するので、呼び出しもとの``NavHost``に、どんな**Route**が定義されたかを教える必要があり、``NavHost``で、**route = route名**を定義することに注意が必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/f932e191-188c-e8e5-d861-3fafb0da257e.png)
[参考にした記事](https://hitherejoe.medium.com/nested-navigation-graphs-in-jetpack-compose-dc0ada1d4726)

また、上記のコードでは意味のある区分分けを行ってもコードのまとまりがわかりにくいので、``NavGraphBuilder``を拡張させ``NavHost``内で呼び出します。

```kotlin
fun NavGraphBuilder.authenticateNav(
    navController: NavHostController
){
    navigation(
        startDestination = Screen.LogInScreen.route,
        route = AUTHENTICATE_ROUTE,
    ){
        composable(
            route = Screen.LogInScreen.route
        ){
            LogInScreen(navController = navController)
        }
    }
}
```

これを同様に、**HomeScreen, DetailScreen**でも行うと``NavHost``は以下のように非常にコンパクトになります。

```kotlin
@Composable
fun NavigationSetUp(
    navController: NavHostController
){
    NavHost(
        navController = navController,
        startDestination = AUTHENTICATE_ROUTE,
        route = ROOT_ROUTE
    ){
        homeScreenNav(navController = navController)
        authenticateNav(navController = navController)
    }
}
```

以上のサンプルコードを実行すると、以下のようになります。

![navigation2 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/71680ec9-a3c8-26a7-4bb9-09a4facd6274.gif)

##3. まとめ
[こちら](https://qiita.com/Ryosuke-Android/items/54768e3400022611c73c)も併せて読んでいただくと、Navigationの実装が一通りわかると思います。
管理する画面構成が多い時ほど、意味のある区分分けを行って、画面遷移を行うことの大切さを知れました。