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
mikan
June 24, 2025
Technology
710
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
Navigation3でやりとりした画面引数をSavedStateHandleから引き出せない理由
mikanichinose
0
19
Lazy APIを使ってGradleビルド速度を改善する
mikanichinose
1
77
「脳に収まるコードの書き方」を読んで学んだこと
mikanichinose
1
230
RepositoryのSSoT化
mikanichinose
0
98
Kotlin Multiplatform 始めました
mikanichinose
1
160
Web APIをなぜつくるのか
mikanichinose
0
4k
イベントをどう管理するか
mikanichinose
3
410
ライブラリでしかお目にかかれない珍しい実装
mikanichinose
2
510
Strong Skipping Mode によってrecompositionはどう変わったのか
mikanichinose
0
410
Other Decks in Technology
See All in Technology
基調講演:人とAIをつなぐIoTの今と未来 ー 「フィジカル」と「デジタル」が出会うその先へ【SORACOM Discovery 2026】
soracom
PRO
0
360
AIで楽になるはずが、なぜ疲れる?
kinopeee
0
150
現場で使える AWS DevOps Agent 活用ノウハウ - Release Management 機能の検証結果を添えて / AWS DevOps Agent Release Management and Know-How
kinunori
5
810
人手不足への挑戦:車両保全を支えるIoTとクラウド内製化の道【SORACOM Discovery 2026】
soracom
PRO
0
170
データ活用研修 問いの発見と仮説構築【MIXI 26新卒技術研修】
mixi_engineers
PRO
1
690
それでも、技術なブログを書く理由 #kichijojipm / Why I Still Write Tech Blogs Even Now
shinkufencer
0
1.4k
データ組織の転換期 一足飛びしない段階的戦略
leveragestech
PRO
0
120
データ活用研修 データマネジメント【MIXI 26新卒技術研修】
mixi_engineers
PRO
4
820
テックカンファレンス三大ステークホルダーの文化人類学 ─ 違いを認め合う関係性作り
bash0c7
5
1.3k
AI研修(Day2)【MIXI 26新卒技術研修】
mixi_engineers
PRO
2
1.6k
システム監視を 「システムを監視するだけ」で 終わらせないために
seiud
0
160
Claude Mythos、Fable...フロンティアAIの最新動向と企業のセキュリティ対策
flatt_security
0
170
Featured
See All Featured
Building an army of robots
kneath
306
46k
Being A Developer After 40
akosma
91
590k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
390
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
360
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.7k
Embracing the Ebb and Flow
colly
88
5.1k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
340
Statistics for Hackers
jakevdp
799
230k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
240
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 を今一度ちゃんと理解する必要があるのかもしれない