Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Navigation3でViewModelにデータを渡す方法
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
mikan
June 24, 2025
Technology
690
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Navigation3でViewModelにデータを渡す方法
https://yumemi.connpass.com/event/357336/
mikan
June 24, 2025
More Decks by mikan
See All by mikan
Lazy APIを使ってGradleビルド速度を改善する
mikanichinose
1
73
「脳に収まるコードの書き方」を読んで学んだこと
mikanichinose
1
220
RepositoryのSSoT化
mikanichinose
0
91
Kotlin Multiplatform 始めました
mikanichinose
1
150
Web APIをなぜつくるのか
mikanichinose
0
3.9k
イベントをどう管理するか
mikanichinose
3
400
ライブラリでしかお目にかかれない珍しい実装
mikanichinose
2
500
Strong Skipping Mode によってrecompositionはどう変わったのか
mikanichinose
0
400
Modeling UiEvent
mikanichinose
0
140
Other Decks in Technology
See All in Technology
FinOps × AIエージェントで実現する コストインシデントの自動調査
oasis1994liveforever
0
130
2026 TECHFRESH 畢業分享會 - 開發日常大解密!從領域驅動到企業級上線
line_developers_tw
PRO
0
880
タクシーアプリ『GO』の実践的データ活用
mot_techtalk
3
190
小さく始める AI 活用推進 ― 日経電子版 Web チームの事例/nikkei-tech-talk47
nikkei_engineer_recruiting
0
240
2026TECHFRESH畢業分享會 - Lightning Talk - E起 See See : 電商推薦讀心術? 數據說了算
line_developers_tw
PRO
0
880
自律型AIエージェントは何を破壊するのか
kojira
0
150
How Timee Delivers Day 1 Production Ready LLM Features
tomoyks
0
160
Oracle AI Database@Google Cloud:サービス概要のご紹介
oracle4engineer
PRO
6
1.5k
中期計画、2回作ってみた ~業務委託と正社員、両方の視点から~
demaecan
1
720
Socrates × Looker 〜セマンティックレイヤーで進化するデータ分析エージェント〜
hanon52_
3
2.2k
自宅LLMの話
jacopen
1
480
AIの性能が向上しても未解決な組織の重大問題は何か?/An Unsolved Organizational Problem in the Age of AI
moriyuya
4
630
Featured
See All Featured
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
170
sira's awesome portfolio website redesign presentation
elsirapls
0
280
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Un-Boring Meetings
codingconduct
0
310
Building an army of robots
kneath
306
46k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
Product Roadmaps are Hard
iamctodd
PRO
55
12k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
160
AI: The stuff that nobody shows you
jnunemaker
PRO
8
710
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.3k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
140
Transcript
Navigation3 で ViewModel に データを 渡す方 法 モバチキ#8 mikan( 一瀬喜弘)
自己紹介 object Mikan { val name = "一瀬喜弘" val company
= "karabiner.tech" val occupation = Engineer.Android var work = "OisixのAndroidアプリ開発" val favoriteCharacter.GQuuuuuuX = "コモリ・コーハート" }
🙇♂️🙇♂️ 注意 🙇♂️🙇♂️ まだ Alpha 版でしかない Navigation3 に ついて 基本的な
ことを 大して 語らずに いきなりAdvanced な 内容を 話します
Navigation3 でましたね
Navigation3 でましたね
ちなみに Oisix に Navigation なんて 贅沢な ものは ありません FragmentManager でがんばってます💪
Navigation3 で 使われる コンポーネントと Navigation Component との 対比
Navigation3 で使われるコンポーネント BackStack: 画面遷移の履歴 実体は SnapshotStateList<Any> NavDisplay: BackStack を監視して、現在のキーに相当するUI を表示する
EntryProvider: キーをNavEntry に変換する関数 (key) -> NavEntry NavEntry: キーとコンテンツを結び付ける metadata: アニメーションの制御とか NavKey: BackStack をrememberSaveable したいときに必要になる (SceneStrategy: adaptive layout 対応)
Navigation Component との対比 Navigation Component Navigation3 NavController SnapshotStateList BackStackEntry NavEntry
NavHost NavDisplay NagGraph EntryProvider
Navigation とViewModel
Navigation とViewModel Navigation とViewModel を組み合わせるときに必要になる考慮 ライフサイクル管理 遷移先へのデータ授受
Navigation とViewModel Navigation とViewModel を組み合わせるときに必要になる考慮 ライフサイクル管理 ViewModel のライフサイクルをBackStack に合わせる BackStack
からキーが取り除かれたら、ViewModel のリソースも開放する
Navigation とViewModel Navigation とViewModel を組み合わせるときに必要になる考慮 遷移先へのデータ授受 ex. 商品一覧画面→商品詳細画面への遷移 商品ID を渡す
素のままだと 機能に 乏しいので NavEntryDecorator を 使って 拡張します
NavEntryDecorator NavEntry の機能を拡張するための仕組み // navEntryDecorator ファクトリ関数を使って作る @Composable fun rememberLogNavEntryDecorator() =
remember { navEntryDecorator<Any>( onPop = { key -> // NavEntryがBackStackから離れるときに実行する処理 }, decorator = { entry -> // NavEntryがBackStackに追加されたときに実行する処理 entry.Content() } ) } // より複雑なことをやりたい場合は `NavEntryDecorator<Any>` を継承してから作る public fun ComplexNavEntryDecorator(): NavEntryDecorator<Any> { // ... }
NavEntryDecorator NavEntry の機能を拡張するための仕組み // ex. BackStackのライフサイクルをログ出力するためのNavEntryDecorator // star-zeroさんの記事を参考にしてます // https://star-zero.medium.com/jetpack-navigation3%E3%81%AE%E6%84%9F%E6%83%B3-440c600dc143
@Composable fun rememberLogNavEntryDecorator() = remember { navEntryDecorator<Any>( onPop = { key -> Log.d("$key", "POP: $key") }, decorator = { entry -> LaunchedEffect(Unit) { Log.d("${entry.contentKey}", "ENTER: ${entry.contentKey}") } entry.Content() } ) }
定義済みのNavEntryDecorator SceneSetupNavEntryDecorator 重複レンダリング防止 NavEntry の状態を保持したまま移動可能にしている(moveableContentOf を利用) アニメーション対応 SavedStateNavEntryDecorator rememberSaveable で保存した状態のライフサイクルをNavEntry
に合わせる NavEntry をスコープとしたSavedState 対応 ViewModelStoreNavEntryDecorator ViewModel のライフサイクルをNavEntry に合わせる NavEntry をViewModelStore として扱えるようにする SavedStateHandle を利用可能にする
使い方 val backStack = rememberNavBackStack(HomeRoute) NavDisplay( entryDecorators = listOf( rememberSceneSetupNavEntryDecorator(),
rememberSavedStateNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator(), ), backStack = backStack, entryProvider = entryProvider { entry<HomeRoute> { val viewModel = hiltViewModel<HomeViewModel>() // ... } entry<ItemsRoute> { val viewModel = hiltViewModel<ItemsViewModel>() // ... } } ) create BackStack
使い方 entryDecorators = listOf( rememberSceneSetupNavEntryDecorator(), rememberSavedStateNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator(), ), val backStack
= rememberNavBackStack(HomeRoute) NavDisplay( backStack = backStack, entryProvider = entryProvider { entry<HomeRoute> { val viewModel = hiltViewModel<HomeViewModel>() // ... } entry<ItemsRoute> { val viewModel = hiltViewModel<ItemsViewModel>() // ... } } ) setup entryDecorators List として渡す Modifier と同様に登録順が挙動に影響を 与える
使い方 val viewModel = hiltViewModel<HomeViewModel>() val viewModel = hiltViewModel<ItemsViewModel>() val
backStack = rememberNavBackStack(HomeRoute) NavDisplay( entryDecorators = listOf( rememberSceneSetupNavEntryDecorator(), rememberSavedStateNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator(), ), backStack = backStack, entryProvider = entryProvider { entry<HomeRoute> { // ... } entry<ItemsRoute> { // ... } } ) create viewmodel by hiltViewModel() hilt-navigation-compose は navigation-compose 専用というわけでは ないのでNavigation3 でも引き続き利用可 能
使い方 val backStack = rememberNavBackStack(HomeRoute) NavDisplay( entryDecorators = listOf( rememberSceneSetupNavEntryDecorator(),
rememberSavedStateNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator(), ), backStack = backStack, entryProvider = entryProvider { entry<HomeRoute> { val viewModel = hiltViewModel<HomeViewModel>() // ... } entry<ItemsRoute> { val viewModel = hiltViewModel<ItemsViewModel>() // ... } } ) ここまででNavEntry-aware なViewModel のセットアップが完了 → つぎはViewModel へのデータの授受
SavedStateHandle 経由でデータを受け取る @HiltViewModel class ItemViewModel @Inject constructor( private val hogeUseCase:
HogeUseCase, private val fugaRepository: FugaRepository, savedStateHandle: SavedStateHandle, ): ViewModel() { private val itemId = savedStateHandle["itemId"] // こんな感じでデータを受け取りたい }
SavedStateHandle 経由でデータを受け取る @HiltViewModel class ItemViewModel @Inject constructor( private val hogeUseCase:
HogeUseCase, private val fugaRepository: FugaRepository, savedStateHandle: SavedStateHandle, ): ViewModel() { private val itemId = savedStateHandle["itemId"] // こんな感じでデータを受け取りたい } // ver. navigation-compose navController.navigate(ItemRoute(itemId)) // → これでいける!
SavedStateHandle 経由でデータを受け取る @HiltViewModel class ItemViewModel @Inject constructor( private val hogeUseCase:
HogeUseCase, private val fugaRepository: FugaRepository, savedStateHandle: SavedStateHandle, ): ViewModel() { private val itemId = savedStateHandle["itemId"] // こんな感じでデータを受け取りたい } // ver. navigation-compose navController.navigate(ItemRoute(itemId)) // → これでいける! // ver. navigation3 backStack.add(ItemRoute(itemId)) // → これでいける?
SavedStateHandle 経由でデータを受け取る null 。 。 だと。 。 init { Log.d("ItemViewModel",
"itemId: ${savedStateHandle.get<String>("itemId")}") }
天下のGoogle 様がそんなことするはずないよね
天下のGoogle 様がそんなことするはずないよね サンプル実装(nav3-recipe) ViewModelProvider.Factory を自前実装してデータを直 接受け取っている
天下のGoogle 様がそんなことするはずないよね サンプル実装(nav3-recipe) Factory を実装して AssistedInject でデータを直接 受け取っている
SavedStateHandle に データを 詰める ところまで 対応できてない or しない 疑惑
現状の対応方法: AssistedInject を使って部分DI する @HiltViewModel(assistedFactory = ItemViewModel.Factory::class) class ItemViewModel @AssistedInject
constructor( private val hogeUseCase: HogeUseCase, private val fugaRepository: FugaRepository, @Assisted private val route: ItemRoute, ): ViewModel() { // ... @AssistedFactory interface Factory { fun create(route: ItemRoute): ItemViewModel } } entery<ItemRoute> { route -> val viewModel = hiltViewModel { factory: ItemViewModel.Factory -> factory.create(route) } }
なぜSavedStateHandle にデータが格納されないの か。その理由を探るため我々はViewModel の奥地へと 歩みを進めた。 。 インスタンスはどこからやってくるのか viewModels() ViewModelLazy ViewModelProvider
ViewModelProvider.Factory CreationExtras viewModelFactory {}
なぜSavedStateHandle にデータが格納されないの か。その理由を探るため我々はViewModel の奥地へと 歩みを進めた。 。 どうやって構成変更を生き延びているのか onRetainNonConfigurationInstance ViewModelStore ViewModelStoreOwner
なぜSavedStateHandle にデータが格納されないの か。その理由を探るため我々はViewModel の奥地へと 歩みを進めた。 。 どうやってOS によるプロセスデスを生き延びているのか onSaveInstanceState SavedStateRegistry
SavedStateRegistryController SavedStateRegistryOwner SavedStateProvider SavedStateHandle SavedStateHandlesProvider SavedStateHandlesProvider.SavedStateHandlesVM SavedStateViewModelFactory enableSavedStateHandles
なぜSavedStateHandle にデータが格納されないの か。その理由を探るため我々はViewModel の奥地へと 歩みを進めた。 。 Navigation3 ではViewModelStore, SavedStateHandle はどうやって統合するのか
SavedStateNavEntryDecorator ViewModelStoreNavEntryDecorator
TO BE CONTINUE...
まとめ Navigation3 には機能を拡張する仕組みとしてNavEntryDecorator がある Navigation3 でViewModel を使うためにはSavedStateNavEntryDecorator と ViewModelStoreNavEntryDecorator を組み合わせて使う
( いまのところ) SavedStateHandle にはデータが入ってこない AAC ViewModel を今一度ちゃんと理解する必要があるのかもしれない