🦁

JetpackCompose TypeSafeNavigationについて(2.8.1)

2024/10/09に公開

https://developer.android.com/develop/ui/compose/navigation#kts

ついに出ましたTypeSafeNavigation!!!🎉🎉🎉

ということで早速本題に入ります。


before
composable(
    route = "hoge/{hogeId}",
    arguments = listOf(navArgument("hogeId") { type = NavType.StringType })
) { backStackEntry ->
    HogeScreen(id = backStackEntry.arguments?.getString("hogeId"))
}
after
@Serializable
data class HogeRoute(val id: String)

composable<HogeRoute> { backStackEntry ->
    HogeScreen(id = backStackEntry.toRoute<HogeRoute>().id)
}


とてもいいですね......

data objectを渡すことでbackStackEntry.toRoute<T>によって

ルート型を取得できるのがとてもスマートでいい......


現状のTypeSafeNavigationの問題

とてもいいように見えているTypeSafeNavigatonですが

現状色々と悲しいところがあります......


@Serializable
data class HogeRoute(val id: String)

@Serializable
data class PogeRoute(val id: String)

composable<HogeRoute> { backStackEntry ->
    val args = backStackEntry.toRoute<PogeRoute>().id
    HogeScreen(id = args)
}


はい

こちらですがなんとコンパイルエラーになりません

ランタイムエラーでHogeRouteに遷移したタイミングでクラッシュします😇

更にいうと、


navController.navigate(route = T)

でしか遷移する事ができないので


@Serializable
data class HogeRoute(val id: String)

navController.navigate(route = HogeRoute("hoge"))
navController.navigate(route = "HogeRoute")
navController.navigate(route = HogeRoute)


全てコンパイルが通ってしまう上に当然の如く

"Hoge"がナビゲーションに存在しなければクラッシュしますし、

一番下のHogeRoute宣言はobjectではなくdata classなので

当然クラッシュします......


どう゛じでま゛え゛に゛でぎでだごどがでぎな゛ぐな゛っ゛でる゛ん゛だよ゛!!!💢💢💢


はい

JetpackNavigationで出来てた事が見事に出来なくなっております

悲しいね


暫定対応

  • テンプレート作成
  • navigateメソッドの外部使用禁止
  • toRouteメソッドの外部使用禁止

心苦しいですが現状これしかないかなと思ってます😇


というのもnowinandroid自体が近い手法をとっていて、このような実装を行っています。

@Serializable data class TopicRoute(val id: String)

fun NavController.navigateToTopic(topicId: String, navOptions: NavOptionsBuilder.() -> Unit = {}) {
    navigate(route = TopicRoute(topicId)) {
        navOptions()
    }
}

fun NavGraphBuilder.topicScreen(
    showBackButton: Boolean,
    onBackClick: () -> Unit,
    onTopicClick: (String) -> Unit,
) {
    composable<TopicRoute> {
        TopicScreen(
            showBackButton = showBackButton,
            onBackClick = onBackClick,
            onTopicClick = onTopicClick,
        )
    }
}

https://github.com/android/nowinandroid/blob/main/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt


私はこの実装を参考に以下のテンプレートを作成しました。

@Serializable
object ${UpperRouteNAME}Route

fun NavController.navigateTo${UpperRouteNAME}(
    routeData: ${UpperRouteNAME}Route,
    navOptions: NavOptionsBuilder.() -> Unit = {},
) {
    navigate(route = routeData) {
        navOptions()
    }
}

fun NavGraphBuilder.${LowerRouteNAME}Navigation(
    nextRouteAction: () -> Unit = {},
) {
    composable<${UpperRouteNAME}Route> {

    }
}

private fun NavBackStackEntry.getSafeArgs(): ${UpperRouteNAME}Route {
    return this.toRoute<${UpperRouteNAME}Route>()
}


これによりナビゲーションの作成はテンプレートを使って行うということを徹底することにより

JetpackNavigationで実現できていた

  • navigateToメソッドの自動生成
  • safeArgs取得時の方安全の保証

これが達成できるという算段です。


色々と難のあるTypeSafeNavigationですが

以前のバージョンよりは遥かに描きやすい且つ直感的にもわかりやすい実装になっているので

願わくばNavBackStackEntry.toRouteが

NavigationComposable関数で宣言した<T>をそのまま返してくれるようになることを

願うしかないですね......😭

TRUSTDOCK テックブログ

Discussion