We can pass parameters to our ViewModel, using it as a data holder, also to share data between Fragments, and to maintain its state across recreation processes.
This is part of a multi-part series on Advanced ViewModels in Android. This post focuses on how our ViewModel can be shared between Fragments.
Sharing View Models can be useful in the following scenarios:
- Master/Detail flow.
- Main View with a Modal (Dialog, BottomSheet, etc).
In our case, we have a Master/Detail flow: ShowNewsFragment allows the user to search for news and NewsDetailFragment will display the details of the selected news. We can archive this by creating a ViewModel along with an Activity Context:
private val viewModel: SharedNewsViewModel by activityViewModels { SharedNewsViewModel.Factory(newsService) }
As per the documentation, activityViewModels is a property delegate to get a reference to a ViewModel scoped to its Activity. So, our shared ViewModel will persist across Fragment recreations.
Given that our ViewModel is a data holder, let's create the LiveData we want to share between our Fragments:
private val _selectedNews = MutableLiveData<News>()
And the appropriate getter and setter for the selected news:
fun setSelectedNews(news: News) {
_selectedNews.value = news
}
fun getSelectedNews() = _selectedNews.value
Then, in NewsDetailFragment we need to access the same shared ViewModel:
private val viewModel: SharedNewsViewModel by activityViewModels()
Note that there is no need to pass the Factory because here we get a reference of the ViewModel previously created in ShowNewsFragment.
Finally, and also in NewsDetailFragment, we need to get and display the selected news:
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
viewModel.getSelectedNews()?.let { news ->
with(binding) {
ivNews.loadUrl(news.urlToImage)
tvTitle.text = news.title
tvAuthor.text = news.author}
...
}
}
}
Have you noticed that we are not using Bundles to pass data to Fragments? Due to the fact that our ViewModel is the data holder, we can forget about TransactionTooLargeException and its 1MB size limit when we are dealing with Bundles and passing data between Activity or Fragment arguments because we are accessing LiveData values instead.
When using shared ViewModel, we do not observe the shared LiveData in Detail or Modal view, we only get the values from the shared LiveData. Basically, if we observe the shared LiveData in Detail or Modal view, we will get new events as soon as we start observing it because the shared LiveData has existing values posted in the Master or Primary View and this can lead to undesirable results if we observe the status (Loading, Success, Error) and perform operations based on that status.