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 inandroidx.fragment.app
How do I use this pattern?
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.