0

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?

1 Answers1

0

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 fragment onCreateView() or earlier before inflating the child fragment, the view model won't be created in time for the child fragment to access in its onCreateView(). Using ViewModelProvider instantiates 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.