This video (MVVM & Nested Fragments/Views: ViewModel Contracts - By Marcos Paulo Damesceno, Bret Erickson
droidcon San Francisco 2019
) shows a way to deal with communication between activities/fragments using ViewModel.
I am implementing it for learning purpose but I got stuck.
// 18:35 of the video
private const val VM_KEY = "view_model_contract_key"
fun <T> Fragment.viewModelContracts() = lazy {
val clazz: Class<ViewModel> = arguments?.getSerializable(VM_KEY) as Class<ViewModel>
val viewModelProvider = ViewModelProvider(requireActivity())
return@lazy viewModelProvider.get(clazz) as T
}
The ViewModelStoreOwner passed as parameter is an Activity, but if I have a Fragment inside another Fragment where both of them share the same ViewModel, the ViewModel returned by viewModelContracts() will be a different object as the one created by the Parent Fragment.
interface ChildViewModelContract {
// ...
}
class SomeViewModel : ViewModel(), ChildViewModelContract {
// ...
}
class ParentFragment: Fragment {
private val viewModel: SomeViewModel by viewModels()
// ...
}
class ChildFragment: Fragment {
private val viewModelContract: ChildViewModelContract by viewModelContracts()
// ...
}
The ideal solution would be to check in fun <T> Fragment.viewModelContracts() if the ViewModelProvider of the parent fragment has the ViewModel stored in it, and if not, use the ViewModelProvider of the Activity. But I'm not knowing how to do this.
fun <T> Fragment.viewModelContracts() = lazy {
val clazz: Class<ViewModel> = arguments?.getSerializable(VM_KEY) as Class<ViewModel>
val parentFragment = parentFragment
if (parentFragment != null) {
val viewModelProvider = ViewModelProvider(parentFragment)
// is there any way to do something like this?
if (viewModelProvider.isViewModelStored(clazz)) {
return@lazy viewModelProvider.get(clazz) as T
}
}
val viewModelProvider = ViewModelProvider(requireActivity())
return@lazy viewModelProvider.get(clazz) as T
}