mcpField
N
navigation3

navigation3

github.com/new-silvermoon開発1.0k106 回インストール15 週間前に更新
リポジトリ

navigation3 について

Jetpack ComposeでNavigation 3を使い、状態駆動のナビゲーションを実装するスキルです。バックスタック、ViewModelのスコープ、アダプティブレイアウト、ディープリンク、状態復元、アニメーション、テスト、Navigation Composeからの移行を扱います。

主な機能

  1. 01シリアライズ可能なナビゲーションキー
  2. 02可変状態のバックスタック
  3. 03ViewModel用エントリーデコレーター
  4. 04アダプティブレイアウトとディープリンク
  5. 05状態復元とテスト

活用シーン

  1. 01遷移先をアプリケーション状態としてモデル化
  2. 02ナビゲーションエントリーごとにViewModelをスコープ
  3. 03スマートフォン、タブレット、折りたたみ端末のレイアウトに対応
  4. 04バックスタックの変更と画面描画をテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/new-silvermoon/awesome-android-agent-skills --skill navigation3

インストール後、Claude Code で/navigation3を入力して読み込まれたか確認します。

詳細

Navigation 3

概要

Navigation 3 を使用して、Jetpack Compose で状態駆動型のナビゲーションを実装します。Navigation Compose とは異なり、Navigation 3 は NavController を介さず、アプリケーションの状態としてナビゲーションをモデル化します。このスキルでは、ナビゲーションキー、バックスタック管理、ViewModel のスコープ設定、エントリデコレーター、アダプティブレイアウト、ディープリンク、アニメーション、状態復元、テストについて説明します。

セットアップ

Navigation 3 の依存関係を追加します。

// build.gradle.kts
dependencies {

    implementation("androidx.navigation3:navigation3-runtime:1.0.0-alpha08")
    implementation("androidx.navigation3:navigation3-ui:1.0.0-alpha08")

    // Lifecycle integration
    implementation("androidx.lifecycle:lifecycle-viewmodel-navigation3:2.9.2")

    // Serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
}

// Enable serialization plugin
plugins {
    kotlin("plugin.serialization") version "2.2.0"
}

コアコンセプト

1. ナビゲーションキーを定義する

ナビゲーション先は、不変でシリアライズ可能なオブジェクトで表します。

import kotlinx.serialization.Serializable

@Serializable
data object Home

@Serializable
data class Profile(
    val userId: String
)

@Serializable
data class Product(
    val productId: String,
    val showReviews: Boolean = false
)

@Serializable
data object Settings

キーがナビゲーションの状態になります。


2. バックスタックを作成する

Navigation 3 では、NavController の代わりに変更可能な状態バックスタックを使用します。

@Composable
fun MyApp() {

    val backStack = remember {
        mutableStateListOf<Any>(Home)
    }

    AppNavDisplay(backStack)
}

3. NavDisplay を作成する
@Composable
fun AppNavDisplay(
    backStack: SnapshotStateList<Any>
) {

    NavDisplay(
        backStack = backStack,
        onBack = {
            if (backStack.size > 1) {
                backStack.removeLast()
            }
        }
    ) { key ->

        when (key) {

            Home ->
                HomeScreen(
                    onProfileClick = {
                        backStack += Profile(it)
                    }
                )

            is Profile ->
                ProfileScreen(
                    userId = key.userId
                )

            is Product ->
                ProductScreen(
                    productId = key.productId,
                    showReviews = key.showReviews
                )

            Settings ->
                SettingsScreen()
        }
    }
}

ナビゲーションパターン

次の画面へ移動する
backStack += Profile("user123")
戻る
backStack.removeLast()
現在の画面を置き換える
backStack[backStack.lastIndex] = Home
バックスタックをクリアする
backStack.clear()
backStack += Home
ルート画面まで戻る
while (backStack.size > 1) {
    backStack.removeLast()
}

引数の処理

引数を取得する

引数はすでにナビゲーションキーに含まれています。

when (val key = currentKey) {

    is Profile -> {
        ProfileScreen(
            userId = key.userId
        )
    }

    is Product -> {
        ProductScreen(
            productId = key.productId
        )
    }
}
オブジェクトではなく ID を渡す
// CORRECT
backStack += Profile(user.id)

// Fetch object inside ViewModel
class ProfileViewModel(
    savedStateHandle: SavedStateHandle,
    repository: UserRepository
) : ViewModel() {

    val profile = savedStateHandle.toRoute<Profile>()

    val user =
        repository.getUser(profile.userId)
}
// INCORRECT

backStack += User(...)
backStack += ProductRepository(...)
backStack += ProductViewModel(...)

ViewModel の統合

Navigation 3 では、エントリデコレーターを使用して ViewModel のスコープを設定します。

NavDisplay(

    backStack = backStack,

    entryDecorators = listOf(

        rememberSceneSetupNavEntryDecorator(),

        rememberSavedStateNavEntryDecorator(),

        rememberViewModelStoreNavEntryDecorator()
    )

) { key ->

    // destinations
}
ViewModel の例
class ProfileViewModel(
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    val profile =
        savedStateHandle.toRoute<Profile>()
}

エントリデコレーター

Navigation 3 では、ライフサイクル機能を付与するためにデコレーターを使用します。

シーンの設定
rememberSceneSetupNavEntryDecorator()

各エントリのナビゲーションシーンを作成します。

保存状態
rememberSavedStateNavEntryDecorator()

プロセス再作成後に、遷移先の状態を自動的に復元します。

ViewModel ストア
rememberViewModelStoreNavEntryDecorator()

各ナビゲーションエントリに ViewModel のスコープを設定します。


アダプティブナビゲーション

Navigation 3 は Material Adaptive レイアウトと統合されています。

NavDisplay(

    backStack = backStack,

    sceneStrategy = rememberListDetailSceneStrategy()

)

アダプティブシーン戦略を使用すると、次のレイアウトに自動的に切り替えられます。

  • シングルペイン(スマートフォン)
  • 2 ペイン(タブレット)
  • 折りたたみ式デバイス

ディープリンク

ディープリンクはナビゲーションキーに解決されるようにします。

fun handleDeepLink(uri: Uri) {

    val userId =
        uri.lastPathSegment ?: return

    backStack += Profile(userId)
}

ルート文字列を手動で構築することは避けてください。


アニメーション

ナビゲーションの遷移は、シーン遷移を使用して定義します。

NavDisplay(

    backStack = backStack,

    transitionSpec = {

        fadeIn() togetherWith fadeOut()

    }
)

Navigation 3 のアニメーション API は、アルファ版の間に変更される可能性があります。


状態の復元

ナビゲーションキーはシリアライズ可能で、自動的に復元されます。

val backStack = rememberSaveable(
    saver = navBackStackSaver()
) {
    mutableStateListOf(Home)
}

キーがシリアライズ可能であることを必ず確認してください。


テスト

状態駆動型であるため、ナビゲーションのテストは簡単です。

例
@Test
fun navigateToProfile() {

    val backStack =
        mutableStateListOf<Any>(Home)

    backStack += Profile("123")

    assertEquals(
        Profile("123"),
        backStack.last()
    )
}

Compose UI テストでは、現在のバックスタックを調べて画面のレンダリングを検証できます。


Navigation Compose からの移行

Navigation ComposeNavigation 3
NavController変更可能なバックスタック
NavHostNavDisplay
navigate()backStack += Key
popBackStack()removeLast()
文字列ルートシリアライズ可能なキー
composable()when(key)
ナビゲーショングラフ状態駆動型の遷移先

推奨されるプロジェクト構成

navigation/

    AppNavigation.kt

    NavigationKeys.kt

    NavigationDisplay.kt

feature/

    home/

    profile/

    settings/

重要なルール

実施すること
  • 不変でシリアライズ可能なキーを使用する
  • ナビゲーションの状態を Compose 内に保持する
  • 複雑なオブジェクトではなく ID を渡す
  • エントリデコレーターを使用して ViewModel のスコープを設定する
  • 状態の復元には rememberSaveable を使用する
  • ナビゲーションを監視可能なアプリケーション状態としてモデル化する
実施しないこと
  • 文字列ルートを使用する
  • ナビゲーション経由でリポジトリや ViewModel を渡す
  • ナビゲーションキーを変更する
  • バックスタックにビジネスオブジェクトを保存する
  • 再コンポジション時にバックスタックを再作成する
  • Navigation Compose API と Navigation 3 API を混在させる

参考資料

  • Android Navigation 3 のドキュメント
  • Navigation 3 のサンプル
  • Lifecycle ViewModel Navigation 3 のドキュメント
  • Material 3 Adaptive Navigation のドキュメント

コメント