I have the following classes:
The ViewModel
class MealPlanViewModel @AssistedInject constructor(private val mealPlanRepository: MealPlanRepository, @Assisted private val canteenId: String) : ViewModel() {
// AssistedInject code for using ViewModel injection with runtime parameters
@AssistedInject.Factory
interface AssistedFactory {
fun create (canteenId: String): MealPlanViewModel
}
companion object {
fun provideFactory(assistedFactory: AssistedFactory, canteenId: String): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return assistedFactory.create(canteenId) as T
}
}
}
}
The parent fragment:
class MealPlanFragment : Fragment() {
val canteenId = "mensa"
@Inject
lateinit var viewModelAssistedFactory: MealPlanViewModel.AssistedFactory
private val viewModel: MealPlanViewModel by viewModels {
MealPlanViewModel.provideFactory(viewModelAssistedFactory, canteenId)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = FragmentMealPlanBinding.inflate(inflater, container, false)
val tabLayout = binding.tabLayout
val viewPager = binding.viewPager
viewPager.adapter = MealPlanPagerAdapter(childFragmentManager, lifecycle)
return binding.root
}
}
and the child fragment attached to the parent fragment by viewpager
@AndroidEntryPoint
class DayFragment : Fragment() {
private val viewModel: MealPlanViewModel by viewModels(
ownerProducer = {requireParentFragment()}
)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = FragmentDayBinding.inflate(inflater, container, false)
viewModel.getMealPlanByDay(...).observe(viewLifecycleOwner, Observer {
...
})
return binding.root
}
companion object {
@JvmStatic
fun newInstance(position: Int) =
DayFragment().apply {
arguments = Bundle().apply {
putInt(POSITION, position)
}
}
}
}
The call of
viewModel.getMealPlanByDay(...).observe(viewLifecycleOwner, Observer {
})
leads to the following error: Cannot create an instance of class MealPlanViewModel has no zero argument constructor
But if I don't use AssistedInject to create the ViewModel in the parent fragment and instantiate it like in the following instead I don't get the "has no zero argument constructor" in the child fragment.
val bundle: Bundle = ...
val mealPlanRepository = ...
val mealPlanViewModelFactory = MealPlanViewModelFactory(mealPlanRepository, canteenId, this, bundle)
val viewModel = ViewModelProvider(this, mealPlanViewModelFactory).get(MealPlanViewModel::class.java)
Any ideas on why that is and how to fix this while still using AssistedInject?
By this question, the Kotlin property delegate AssistedInject
by viewModels()lazily instantiates the view model. So if you don't reference the view model in the parent fragmentonCreateView()or earlier before inflating the child fragment, the view model won't be created in time for the child fragment to access in itsonCreateView(). UsingViewModelProviderinstantiates the view model as soon as the parent fragment is created.Thanks for the question, I ran into the same problem and resolved it the same way you did.