Search code examples
androidviewmodelkoinandroid-koin

Koin: getting instance of viewmodel from parent fragment using an interface/abstract class


I've declared an abstract viewModel (let's say AnimalsViewModel), two viewModels extend it (DogViewModel, CatViewModel)

the corresponding fragments (DogFragment, CatFragment) host a common fragment (AnimalFragment) that needs the instance of whatever AnimalViewModel the parent fragment is using. But how can I get it in AnimalFragment? I tried this:

class AnimalFragment : Fragment() {

    private val viewModel by lazy {
            requireParentFragment().getViewModel<AnimalViewModel>()
    }
}

but it doesn't work as koin doesn't find the instance owned by the parent and throws an error stating that it doesn't find a provider for AnimalViewModel (obviously, since is abstract). I tried to make it an interface to no avail.

Is there any way to achieve this?

EDIT: to clarify, let's say DogFragment has an instance of DogViewModel with id 1 and CatFragment has CatViewModel with id 2, AnimalFragment inside inside DogFragment should receive viewModel #1 and the one inside CatFragment should receive #2. The problem is not getting the viewModel in the correct scope but getting the viewModel using an abstract class as identifier.

this works perfectly but I don't want to add a parameter to AnimalFragment to know which viewModel it should request

private val viewModel by lazy {
        
            requireParentFragment().getViewModel<DogViewModel>()
     

    }

Solution

  • I ended up defining an interface which provides a viewmodel of the required abstract type. It's not ideal but it's better than passing arguments around or mentioning the parent fragments in the child.

    interface AnimalContainer {
        abstract val viewModel: AnimalViewModel
    }
    
    class AnimalFragment : Fragment() {
        private val viewModel by lazy {
            (requireParentFragment() as? AnimalContainer)?.viewModel
                ?: error("AnimalFragment not allowed in container ${requireParentFragment()}")
        }
    }