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
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
mikan
June 24, 2025
Technology
700
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
75
「脳に収まるコードの書き方」を読んで学んだこと
mikanichinose
1
220
RepositoryのSSoT化
mikanichinose
0
93
Kotlin Multiplatform 始めました
mikanichinose
1
150
Web APIをなぜつくるのか
mikanichinose
0
3.9k
イベントをどう管理するか
mikanichinose
3
410
ライブラリでしかお目にかかれない珍しい実装
mikanichinose
2
510
Strong Skipping Mode によってrecompositionはどう変わったのか
mikanichinose
0
410
Modeling UiEvent
mikanichinose
0
140
Other Decks in Technology
See All in Technology
本当の”仕事”を手放せる未来が見えた
mu7889yoon
0
220
AI時代のエンジニアキャリアについて今一度考える
sakamoto_582
1
1.2k
型は壁、Rustでもバグを直すな、表現できなくせよ
nwiizo
9
1k
Claude Codeとハーネスについて考えてみる
oikon48
17
8.2k
End-to-Endで考える信頼性 —LINEアプリにおけるクライアント開発×SRE連携の実践
maruloop
2
350
プライバシー保護の理論と実践
lycorptech_jp
PRO
1
240
フルAIで個人開発して学んだあれこれ / yuruai vol.1
isaoshimizu
0
180
しぶいSRE: サーバから見えない障害にどう向き合うか。ラストワンマイルのデバッグ実践 / Shibui SRE
kanny
6
1.9k
はてなのサービス基盤を支える Kubernetes《足腰》
masayoshimaezawa
0
440
打造你的 AI 工作流:Agent Skill + MCP 實戰工作坊
appleboy
0
540
最近評価が難しくなった
maroon8021
0
220
なぜ私たちのSREプラクティスはなかなか機能しないのか 〜システムより先に組織を見る〜 / Why our SRE practices aren't really working
vtryo
0
680
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
230
How to build a perfect <img>
jonoalderson
1
5.8k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
250
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
760
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
960
Exploring anti-patterns in Rails
aemeredith
3
430
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.3k
KATA
mclloyd
PRO
35
15k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
330
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
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 を今一度ちゃんと理解する必要があるのかもしれない