Reputation: 464
I have a DocumentsFragment with a TabLayout with 3 tabs:
TabRulesFragment, TabProceduresFragment, TabGuidanceFragment
In DocumentsFragment I initialize a shared viewModel, DocumentsSharedViewModel with a factory:
class DocumentsFragment : Fragment() {
private lateinit var sharedViewModel: DocumentsSharedViewModel
private lateinit var viewPager2: ViewPager2
private lateinit var documentsCollectionAdapter: DocumentsCollectionAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val program = DocumentsFragmentArgs.fromBundle(requireArguments()).program
val name = DocumentsFragmentArgs.fromBundle(requireArguments()).name
val viewModelFactory = DocumentsSharedViewModelFactory(program, name)
sharedViewModel = ViewModelProvider(this, viewModelFactory)[DocumentsSharedViewModel::class.java]
to share data between the documents fragment and the 3 tab fragments. When I try to connect to the shared viewModel in one of the tab fragments (TabRulesFragment for example):
class TabRulesFragment : Fragment() {
private lateinit var tabRulesRecyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val sharedViewModel : DocumentsSharedViewModel by viewModels()
val binding = TabRulesFragmentBinding.inflate(layoutInflater)
binding.viewModel = sharedViewModel
I get an error that I can't create an instance of the DocumentsSharedViewModel:
java.lang.RuntimeException: Cannot create an instance of class com.smellydogcoding.westvirginiaelectronicfieldguide.ui.documents.DocumentsSharedViewModel
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:188)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:238)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:169)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:44)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:31)
at com.smellydogcoding.westvirginiaelectronicfieldguide.ui.documents.rulesTab.TabRulesFragment.onCreateView$lambda-0(TabRulesFragment.kt:30)
at com.smellydogcoding.westvirginiaelectronicfieldguide.ui.documents.rulesTab.TabRulesFragment.onCreateView(TabRulesFragment.kt:32)
I'm assuming that viewModelProvider is looking for the factory (which doen't exist in TabRulesFragment because it's in DocumentsFragment) and throwing an error when it doesn't find it. Is there any way to use data from a shared data model without creating another instance of it?
Upvotes: 2
Views: 3739
Reputation: 1
If you want to share viewmodel with the factory you can override getDefaultViewModelProviderFactory()
method in the owing activity. then in fragments use val viewmodel : MainViewModel by activityViewModels()
. This way you don't need to provide a factory in each fragment.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override val defaultViewModelProviderFactory: ViewModelProvider.Factory
get() = MainViewModel.factory}
Upvotes: 0
Reputation: 21
If you have a ViewModel Factory you have to add this code to both Fragments.
val sharedModel: DocumentsSharedViewModel by activityViewModels{DocumentsSharedViewModel.Factory}
Upvotes: 0
Reputation: 10910
If you want to make a ViewModel scoped to the owning Activity that you can share between fragments, you can use the following to get it in both fragments.
val sharedModel: DocumentsSharedViewModel by activityViewModels()
according to the docs, which has this simple example, where both fragments can access the same ViewModel
class ListFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//...
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//...
}
}
As pointed out in the comments, if you wanted the ViewModel to remain scoped to the parent fragment instead of using the activity scope you could use this instead to access it in the child fragment
val sharedModel: DocumentsSharedViewModel by viewModels({ requireParentFragment() })
Upvotes: 6