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
730
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
29
Lazy APIを使ってGradleビルド速度を改善する
mikanichinose
1
81
「脳に収まるコードの書き方」を読んで学んだこと
mikanichinose
1
230
RepositoryのSSoT化
mikanichinose
0
99
Kotlin Multiplatform 始めました
mikanichinose
1
160
Web APIをなぜつくるのか
mikanichinose
0
4.1k
イベントをどう管理するか
mikanichinose
3
420
ライブラリでしかお目にかかれない珍しい実装
mikanichinose
2
520
Strong Skipping Mode によってrecompositionはどう変わったのか
mikanichinose
0
410
Other Decks in Technology
See All in Technology
LLM Internals: 언어 모델의 계보와 알고리즘 진화 (2023~2026)
inureyes
PRO
1
800
AIペネトレーションテスト・ セキュリティ検証「AgenticSec」紹介資料
laysakura
2
9.3k
【GCC2026】鉄拳8でのVFX開発事例
bandainamcostudios
PRO
0
120
【CEDEC2026】Creative Approaches to Localizing the Dialects and Unique Speech of Umamusume: Pretty Derby Characters in English
cygames
PRO
4
26k
現場回帰したデータエンジニアが考える AI 時代のキャリア開発 / Career Development in the Age of AI Perspectives from a Hands-on Data Engineer
medley
0
290
Android skills に学ぶ
kokiko
0
130
生成 AI の基礎 〜 サンプル実装で学ぶ基本原理
enakai00
7
4.5k
Oracle MCP Servers Explained
thatjeffsmith
0
290
35分でわかるEffective Platform Engineering
nwiizo
5
550
【GCC2026】大規模言語モデルを活用した内製検索サービスの社内展開や業務活用
bandainamcostudios
PRO
0
350
ハーレムエンジニアリング
kazuma777777
0
190
SO-101×VLAによる3色キューブのピック&プレース
abeja
0
220
Featured
See All Featured
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
500
WENDY [Excerpt]
tessaabrams
11
39k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
660
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.4k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
30 Presentation Tips
portentint
PRO
1
370
Un-Boring Meetings
codingconduct
0
390
Balancing Empowerment & Direction
lara
6
1.2k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
350
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
210
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 を今一度ちゃんと理解する必要があるのかもしれない