Search code examples
androidkotlinandroid-fragmentsandroid-activityandroid-viewmodel

receiver type mismatch in Kotlin with Android's `activityViewModels`


Haven't touched Android in years, and not really Kotlin at all. My Fragment has one of these:

package com.example.my_frag

import androidx.lifecycle.ViewModel
import androidx.fragment.app.activityViewModels
import com.example.MainViewModel


class MyFragViewModel : ViewModel() {
    private val viewModel: MainViewModel by activityViewModels<MainViewModel>()
}

For completion, here is the Fragment:

package com.example.my_frag

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.R


class MyFragment : Fragment() {
    private lateinit var myFragViewModel: MyFragViewModel

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        myFragViewModel =
                ViewModelProvider(this).get(MyFragViewModel::class.java)
        val root = inflater.inflate(R.layout.fragment_my, container, false)
        return root
    }
}

…and my Activity looks like:

package com.example

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer


class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.selectedItem.observe(this, Observer { item ->
            println("$item observed")
        })
    }
}

With the ViewModel being:

package com.example

import android.view.MenuItem
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel


class MainViewModel : ViewModel() {
    private val mutableSelectedItem = MutableLiveData<MenuItem>()
    val selectedItem: LiveData<MenuItem> get() = mutableSelectedItem

    fun selectItem(item: MenuItem) {
        mutableSelectedItem.value = item
    }
}

I was careful to add to my build.gradle these dependencies:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
    implementation "androidx.fragment:fragment-ktx:1.2.5"
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
    implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'com.google.android.material:material:1.2.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

However it doesn't work, activityViewModels is highlighted red and the error is:

Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: public inline fun <reified VM : ViewModel> Fragment.activityViewModels(noinline factoryProducer: (() -> ViewModelProvider.Factory)? = ...): Lazy<TypeVariable(VM)> defined in androidx.fragment.app

How do I use this pattern?


Solution

  • This line

    private val viewModel: MainViewModel by activityViewModels<MainViewModel>()
    

    should go in your Fragment class, not your other ViewModel class. activityViewModels() is a function of Fragment, not ViewModel. ViewModels should not reference each other. They are scoped to the lifecycle of an Activity or Fragment and don't have their own capital-L Lifecycle that another ViewModel can attach to.