はじめに
Navigation 2.8.0-alpha08で、Jetpack Composeでも型安全なナビゲーションが可能になりました。
Navigation Compose
ナビゲーションは以下の3つのコンポーネントで成り立っています。
- ホスト(NavHost)
現在のデスティネーションを表示するUI要素 - グラフ(NavGraph)
アプリ内のすべてのデスティネーションを定義するデータ構造 - コントローラ(NavController)
デスティネーション間の移動を中央で管理するもの
型安全になることによって大きく変わるのは、デスティネーションを定義するグラフの部分です。
これは、従来のグラフの書き方です。
composable(
route = "detail/{id}",
arguments = listOf(
navArgument("id") {
type = NavType.IntType
nullable = false
}
)
) { backStackEntry ->
DetailScreen(
id = backStackEntry.arguments!!.getInt("id")
)
}
例として、詳細画面(DetailScreen)はInt型のIDを必要としています。詳細画面に遷移するにはこのようにします。
navController.navigate("detail/1")
遷移先として指定できるのは文字列であるため、数値を文字列に変換する必要があります。もしここで、間違えて、数値に変換できない文字列を渡してしまうと、クラッシュします。
navController.navigate("detail/hoge") // 実行時にクラッシュ!!!
そこで、このような拡張関数を定義すると、間違った型を渡してしまうことを防ぐことができます。
fun NavController.navToDetail(id: Int) {
navigate("detail/$id")
}
navController.navToDetail(1) //OK
navController.navToDetail("hoge") // NG -- ビルドできない
しかし、その拡張関数を間違って定義してしまうと、ビルドは通り、実行時にクラッシュしてしまいます。
fun NavController.navToDetail(id: String) {
navigate("detail/$id")
}
navController.navToDetail("hoge") // NG -- ビルドはできるが、クラッシュ!!!
このように、これまでのナビゲーションの型安全は不完全で、実行時にクラッシュを起こさないためには使い方を間違えないように気をつけるしかありませんでした。できれば、間違ったコードはビルド時に失敗してほしいです。
型安全なナビゲーション
Navigation 2.8.0-alpha08から、Kotlin Serializationを使った、型安全なグラフの定義ができるようになりました。
plugins {
// Kotlin Serialization Plugin
kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
// Navigation Compose
implementation("androidx.navigation:navigation-compose:2.8.0-alpha08")
// Kotlin Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3")
}
// 引数を受け取らないデスティネーションの定義
@Serializable
object Home
// IDを引数として受け取るデスティネーションの定義
@Serializable
data class Detail(
val id: Int
)
NavHost(
navController = navController,
startDestination = Home
) {
composable<Home> {
HomeScreen(
onClickDetail = {
navController.navigate(Detail(1))
}
)
}
composable<Detail> { backStackEntry ->
val detail: Detail = backStackEntry.toRoute()
DetailScreen(
id = detail.id
)
}
}
このように、navigate
に文字列ではなくdata classを渡せるようになっているため、型を間違えることがありません。また、detail/{id}
のようなRoute文字列を定義する必要もなくなっています。
内部的には、com.example.myapplication.Detail/{id}
のように、指定したタイプとパラメータ名からRouteが設定されているようです。
まとめ
Navigation 2.8.0-alpha08で安定版になったComposeのSafe Argsについて試してみました。型安全になるだけでなく、グラフの定義がシンプルになって使いやすいです。