Jetpack Compose向けナビゲーションライブラリ「Jetpack Navigation 3」のalpha版が公式から先日リリースされました
はじめに
この記事では「Jetpack Navigation 3」の基本的な使い方、従来の公式のNavigationとの違いについて検証します
dependencies {
// 執筆バージョン
implementation "androidx.navigation3.navigation3-runtime:1.0.0-alpha03"
implementation "androidx.navigation3.navigation3-ui:1.0.0-alpha03"
}
とりあえず基本形は?
// Navigation 3
@Composable
fun Main() {
val startKey: String = remember { "HOME" }
val backStack: SnapshotStateList<String> = remember {
mutableStateListOf(startKey)
}
NavDisplay(
backStack = backStack
) { key: String ->
when (key) {
"HOME" -> NavEntry("HOME") {
Column {
Text("ここはホーム画面です")
Button(
onClick = { backStack.add("LIBRARY") }
) {
Text("クリックしてライブラリ画面に遷移する")
}
}
}
"LIBRARY" -> NavEntry("LIBRARY") {
Text("ここはライブラリ画面です")
}
else -> error("不明な画面です")
}
}
}
こんな感じです
SnapshotStateList
というList
向けMutableState
でバックスタックの状態をComposableのサイクルで監視し、バックスタックをリスト操作すると画面遷移します
従来の公式のNavigationとの違いは?
// 従来のNavigation
@Composable
fun Main() {
val startKey: String = remember { "HOME" }
val controller = rememberNavController()
NavHost(
navController = controller,
startDestination = startKey
) {
composable("HOME") {
Column {
Text("ここはホーム画面です")
Button(
onClick = { controller.navigate("LIBRARY") }
) {
Text("クリックしてライブラリ画面に遷移する")
}
}
}
composable("LIBRARY") {
Text("ここはライブラリ画面です")
}
}
}
大きな違いはバックスタックのリストが隠蔽されているかです
Navigation3ではバックスタックのリスト操作をアプリ開発者に委ねることとなりました
他の違いとして、この記事では説明を省きますが、アダプティブレイアウトを構築するための便利な機能「シーン」がNavigation3に備わっています
Navigation 3 は自分でバックスタックをリスト操作する
とはいっても基本は
// ホーム画面に移動
backStack.add("HOME")
// ひとつ前の画面に戻る
backStack.removeAt(backStack.lastIndex)
のふたつだけです
このふたつを駆使しながら、Navigation3で色々と挑戦していきましょう
メインの話はここまでです
(補足)挑戦の前に、上記の基本形を実用形に
まず、上記の基本形はバックスタックをrememberSaveable
で状態保持しておらず、Androidライフサイクルのアクティビティの破棄によりバックスタックが初期化されてしまいます
これを修正します
修正案1 Navigation 3にて用意されているrememberNavBackStack
を使う
@Serializable
data object Home: NavKey
@Serializable
data object Library: NavKey
@Composable
fun Main() {
val startKey = remember { Home }
val backStack: SnapshotStateList<NavKey>
= rememberNavBackStack(startKey)
NavDisplay(
backStack = backStack,
entryProvider = entryProvider {
entry<Home> { key: Home -> /* ホーム画面 */ }
entry<Library> { key: Library -> /* ライブラリ画面 */ }
}
)
rememberNavBackStack
は内部でrememberSaveable
によりバックスタックを状態保持しているので、これで完了です
ついでにentryProvider
という、従来のNavigationのように使える関数も使ってみました、お手軽です
修正案2 バックスタックをrememberSaveable
にて保存可能なオブジェクトに変換する
@Serializable
sealed interface Key
@Serializable
data object Home: Key
@Serializable
data object Library: Key
@Composable
fun Main() {
val startKey = remember { Home }
val backStack: SnapshotStateList<Key> = rememberSaveable(
saver = Saver<SnapshotStateList<Key>, SavedState>(
saver = { mutableStateList: SnapshotStateList<Key> ->
encodeToSavedState(mutableStateList.toList())
},
restore = { savedState: SavedState ->
decodeFromSavedState<List<Key>>(savedState)
.toMutableStateList()
}
)
) { mutableStateListOf(startKey) }
NavDisplay(
backStack = backStack
) { key: Key ->
when (key) {
Home -> NavEntry(Home) {
/* ホーム画面 */
}
Library -> NavEntry(Library) {
/* ライブラリ画面 */
}
}
}
}
sealed interface
は基底クラスのサブクラスの型を保持したままシリアライズとデシリアライズをするので上記のように書けます
(詳しくはkotlinx serialization の公式ドキュメントを読んでください)
バックスタックをrememberSaveable
にて保持可能なオブジェクトのSavedState
に変換しました
またこの修正案では、sealed interface
の最大の特徴のwhen
文にて、サブクラスの一覧を静的に取得しIDEの機能で自動的に列挙できる便利な機能を活用できます
画面を作ったのにNavDisplay
に登録し忘れたなんてミスはコンパイラが注意してくれるのでなくなります
個人的にはこちらの修正案のほうが好きです
総括
この記事では「Jetpack Navigation 3」の基本的な使い方、従来のNavigationとの大きな違いについて検証し執筆しました
また数日中にNavigation3に関する記事を書こうと考えてますので、よろしくお願いします
参考
・ Android Developers Blog : Announcing Jetpack Navigation 3
・ AndroidX Navigation3 : Overview
・ AndroidX Navigation3 : リリースページ
・ AndroidX Navigation3 : 公式サンプル
License
Copyright 2025 oikvpqya Yuya
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.