Jetpack Compose Navigation(BottomNavigation)

#目次
1. Navigationとは
2. サンプルコード
3. まとめ

##紹介する実装内容
![bottomScreen.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/95727262-db9e-1117-8bb7-e16b9312cd21.gif)

##1. Navigationとは

この記事は、[Jetpack Compose Scaffold(BottomNavigation UI)](https://qiita.com/Ryosuke-Android/items/2a94d4fed2586c0ed31c)で構築したUIをもとに、Navigationを実装するので、UIの説明は省略させていただきます。

``NavHost``‥**rememberNavController**を渡し、**NavController**のサブクラスである**NavHostController**でステートを管理します。
また、``NavGraphBuilder``を用いて、**navgraph**に対し画面遷移情報を追加します。

#####ここで、``NavGraphBuilder``に、どのように**navgraph**をついかするの??

**composable**において、**route**を渡すことで**navgraph**を構成することができます。では、詳細を以下のコードでみてみましょう。

```kotlin
public fun NavGraphBuilder.composable(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    content: @Composable (NavBackStackEntry) -> Unit
) {
    addDestination(
        ComposeNavigator.Destination(provider[ComposeNavigator::class], content).apply {
            this.route = route
            arguments.forEach { (argumentName, argument) ->
                addArgument(argumentName, argument)
            }
            deepLinks.forEach { deepLink ->
                addDeepLink(deepLink)
            }
        }
    )
}
```

引数の**route**に注目し、addDestination以下に**this.route = route**とあり、渡したrouteをもとに画面遷移の情報を追加していることがわかります。
また、``content``スロットがあるので、コンポーザブルをラムダで呼び出すことができます。その他の引数についての説明は別記事で説明します。

では、どのタイミングで**route**を渡すかというと、以下のようになります。

```

```

ここで、sealed class にしている意図として網羅性を担保できることです。

それでは、サンプルコードを用いて実際に画面遷移を行っていきます。

##2. サンプルコード

``navgraph``を作成する。

```kotlin
@Composable
fun ScreenNavigation(
    navController: NavHostController
){
    NavHost(
        navController = navController,
        startDestination = Screen.MailScreen.route
    ){
        composable(Screen.MailScreen.route){
            MailScreen()
        }
        composable(Screen.ProfileScreen.route){
            ProfileScreen()
        }
    }
}
```

**NavHost**のスロットで、**composable**は上記のように呼び出します。

次に、``Scaffold``のスロット**bottomBar**で、``BottomAppBar``を呼び出し、**Scaffoldのcontent(スロット)**で、上記の``ScreenNavigation``を呼び出します。

Scaffoldは、マテリアルコンポーネントを対応する画面上に配置することができます。よって、**bottomBar**で呼び出している``BottomAppBar``のcontent(スロット)で以下のコンポーザブルを呼び出します。

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

    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentDestination = navBackStackEntry?.destination

    BottomNavigation(
        modifier = Modifier
            .padding(0.dp, 0.dp, 0.dp, 5.dp)
            .height(100.dp),
        elevation = 0.dp
    ) {
        screenItem.forEach { screen ->
            BottomItem(
                screen = screen,
                currentDestination = currentDestination,
                navController = navController
            )
        }
    }
}
```

ここで重要になってくるのは、**navgraph**で作成した画面遷移がどのように実行されるかを考えることです。

``NavHost``は、画面遷移を行うために**BackStack**を生成します。この**BackStack**というリストを利用することで、``NavHost``が再コンポーズされるたびにcomposableで記述したコンポーザブルを呼び出して画面を更新しています。

よって、現状の画面がどのコンポーザブルで構成されているかをBottomNavigationに伝える必要があるので、上記の``navBackStackEntry``をステートとして管理し、``currentDestination``で、BackStackから更新される画面を受け取ることができます。

最後に、``BottonNavigation``のスロットで呼び出されている。``BottomItem``について確認していきます。

```kotlin
@Composable
fun RowScope.BottomItem(
    screen: Screen,
    currentDestination: NavDestination?,
    navController: NavHostController
){
    BottomNavigationItem(
        icon = {
               Icon(
                   imageVector = screen.icon,
                   contentDescription = ""
               )
        },
        selected = currentDestination?.hierarchy?.any {
            it.route == screen.route
        } == true,
        unselectedContentColor = LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
        onClick = {
            navController.navigate(screen.route)
        }
    )
}
```

``BottonNavigation``内で、コンポーザブルを呼び出すために、上記のコンポーザブルは**RowScope**のコンポーザブルとして定義し、スロットで、``BottomNavigationItem``を呼び出します。

``BottomNavigationItem``では、``icon, label, selected``などBottomNavigationを構成する要素を細かく設定することができます。ここでは、**BottomNavigation**に関連する処理だけを説明します。

``selected``‥**any{}**は、与えられたリストが条件を満たせばBooleanで結果を返してくれるものです。ここで何を確認しているのかというと、``BackStack``(リスト)から与えられた**route**と、画面を構成してる**route**がマッチしているかを確認しています。

上記のコードを実行すると、

![bottomScreen.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512202/95727262-db9e-1117-8bb7-e16b9312cd21.gif)

このように実装することができます。

##3. まとめ

今回は、Scaffoldを用いてBottonNavigationを実装してみました。
画面遷移時の、Argumentsの画面間受け渡し、Nested Navigationを今後改めて説明していきたいと思います。