NavGraphのidで共有しているViewModelをAssistedInjectで生成したく、なおかつ今まで通り別フラグメントに共有したい要件がある際の解決方法を、どこにも記事がなかったため備忘録として残します。
今回は該当箇所のみを扱い、Dagger-Hiltの導入方法などは説明しません。
Hilt導入の公式ドキュメント ->
https://dagger.dev/hilt/gradle-setup
動作環境
- Dagger-Hilt 2.39
- Navigation 2.3.0
前提
AssistedInjectを楽にするためにFragmentの拡張関数としてtakahiromさんの記事を参考に作成しています。
ステップバイステップでDaggerを使った引数ありのViewModelの初期化を理解する
assistedViewModels
inline fun <reified T : ViewModel> Fragment.assistedViewModels(
owner: ViewModelStoreOwner = this,
crossinline body: () -> T
): Lazy<T> {
return viewModels({ owner }) {
object : ViewModelProvider.AndroidViewModelFactory(this.requireActivity().application) {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return body() as T
}
}
}
}
結論
findNavController().getViewModelStoreOwner(R.id.exampleGraphId)をViewModelStoreOwnerとし、親FragmentでAssistedInject、子Fragmentでは今まで通りViewModelProviderでStoreOwnerを上記のfindNav~ としてViewModelを取得する。
ViewModel
SampleViewModel.kt
class SampleViewModel @AssistedInject constructor(
@Assisted private val sampleId: String,
private val sampleRepository: SampleRepository
) : ViewModel() {
@AssistedFactory
interface Factory {
fun create(sampleId: String): SampleViewModel
}
}
親Fragment
ParentFragment.kt
class ParentFragment: Fragment() {
private val args: ParentFragmentArgs by navArgs()
@Inject
lateinit var viewModelFactory: SampleViewModel.Factory
// ここが肝(わかりやすいように変数で切ってます)
val owner: ViewModelStoreOwner = findNavController().getViewModelStoreOwner(R.id.exampleGraphId)
private val sampleViewModel: SampleViewModel by assistedViewModels(owner) {
viewModelFactory.create(args.sampleId)
}
子Fragment
ChildFragment.kt
class ChildFragment: Fragment() {
// ここが肝(ParentFragmentで指定したOwnerと一緒のものを指定する)
val owner: ViewModelStoreOwner = findNavController().getViewModelStoreOwner(R.id.exampleGraphId)
private val sharedViewModel: SampleViewModel by viewModels(
ownerProducer = { owner }
)