How to inject viewModel in BaseFragment using Koin

I have the same scenario in my case. You can also do like below:

Add your ViewModel as an abstract and set value when you extend your BaseFragment.

My BaseFragment have:

abstract class BaseFragment<Binding : ViewDataBinding, ViewModel : BaseViewModel> : Fragment() {
    protected abstract val mViewModel: ViewModel
    protected lateinit var bindingObject: Binding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        bindingObject = DataBindingUtil.inflate(inflater, getLayoutResId(), container, false)
        return bindingObject.root
    }

     /**
       * Get layout resource id which inflate in onCreateView.
      */
     @LayoutRes
     abstract fun getLayoutResId(): Int

     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        doDataBinding()
    }

     /**
      * Do your other stuff in init after binding layout.
      */
      abstract fun init()

     private fun doDataBinding() {
       bindingObject.lifecycleOwner = viewLifecycleOwner // it is extra if you want to set life cycle owner in binding
       // Here your viewModel and binding variable imlementation 
       bindingObject.setVariable(BR.viewModel, mViewModel)  // In all layout the variable name should be "viewModel"
       bindingObject.executePendingBindings()
       init()
}

}

Here is my actual Fragment implementation:

class FragmentComments : BaseFragment<FragmentCommentsBinding, FragmentCommentsVM>() {
// Here is the your viewmodel imlementation 
override val mViewModel: FragmentCommentsVM by viewModel() 

override fun getLayoutResId(): Int = [fragment layout id like "R.layout.fragment_com"]

override fun init() {
...
}

I hope this helps you. Let me know if more help required!


I am currently solving the same issue, looking at source code of Koin, by viewModel() provides kotlin Lazy

/**
 * Lazy get a viewModel instance
 *
 * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type)
 * @param parameters - parameters to pass to the BeanDefinition
 * @param clazz
 */
fun <T : ViewModel> LifecycleOwner.viewModel(
        clazz: KClass<T>,
        qualifier: Qualifier? = null,
        parameters: ParametersDefinition? = null
): Lazy<T> = lazy { getViewModel(clazz, qualifier, parameters) }

which when initialized calls this other LifecycleOwner extension method that does the actual resolution of viewModel instance:

/**
 * Lazy getByClass a viewModel instance
 *
 * @param clazz - Class of the BeanDefinition to retrieve
 * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type)
 * @param parameters - parameters to pass to the BeanDefinition
 */
fun <T : ViewModel> LifecycleOwner.getViewModel(
        clazz: KClass<T>,
        qualifier: Qualifier? = null,
        parameters: ParametersDefinition? = null
): T {
    return getKoin().getViewModel(
            ViewModelParameters(
                    clazz,
                    this@getViewModel,
                    qualifier,
                    parameters = parameters
            )
    )
}

I havent tried it but it seems safe to say that if I call this method directly in my BaseFragment it should work the same way, my BaseFragment looks something like:

abstract class BaseFragment<VM : ViewModel> : Fragment() {

    lateinit var viewModel: VM
    abstract val viewModelClass: KClass<VM>

    override fun onCreate(savedInstanceState: Bundle?) {
        viewModel = getViewModel(clazz = viewModelClass)

        super.onCreate(savedInstanceState)
    }

}