Reputation: 21
Working on a project in Kotlin in AndroidStudio, fairly basic stuff, but MutableLiveData is not working like I expect it to. Am I doing something wrong or maybe I'm confused on how it works?
I'm using a viewModel to handle the LiveData and for testing purposes rn am just running some code in a fragment's onCreate method:
ViewModel
class UserViewModel : ViewModel() {
var currentUser: User = DataSource.user
private var _selectedSkillPoints = MutableLiveData<Int>(currentUser.selectedSkill.points)
val selectedSkillPoints: LiveData<Int> = _selectedSkillPoints
...
}
Fragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
private var user: User = DataSource.user
user.selectedSkill.points++
MaterialAlertDialogBuilder(requireContext())
.setMessage("user.selectedSkill.points = ${user.selectedSkill.points} \n sharedViewModel.selectedSkillPoints.value = ${sharedViewModel.selectedSkillPoints.value} ")
.show()
}
I would expect that updating currentUser.points also updates the LiveData pointing to it. but the Alert shows that only currentUser.points is updated while userPoints stays the same at the initial value so an observer will never run. So what am I missing? How can I get this to work like I was expecting? Thanks
Upvotes: 2
Views: 2291
Reputation: 776
The only relationship between _selectedSkillPoints MutableLiveData object in the ViewModel and currentUser (referencing DataSource.user) is that the _selectedSkillPoints MutableLiveData object was only initialised with the value of currentUser.selectedSkill.points. This is just an initial value for the MutableLiveData object, but MutableLiveData is not observing currentUser nor Datasource.user -MutableLiveData does not observe whatever object you pass as initial value to its public constructor
The value observed is MutableLiveData.value (in your case sharedViewModel.selectedSkillPoints.value). So if you want to .observe() the MutableLiveData object and trigger a callback, you have to set the value of sharedViewModel.selectedSkillPoints.value with one of the following:
- sharedViewModel.selectedSkillPoints.value = newValue //equivalent to setValue()
- sharedViewModel.selectedSkillPoints.setValue(newValue) //from main thread
- sharedViewModel.selectedSkillPoints.postValue(newValue) //from background thread
The problem arises because you are incrementing the points value in the Fragment; it would make more sense to do the increment in the ViewModel as part of an event called from the Fragment, and observing the value in the Fragment for a callback to update the displayed state. Something like:
ViewModel
class UserViewModel : ViewModel() {
var currentUser: User = DataSource.user
private var _selectedSkillPoints = MutableLiveData<Int>(currentUser.selectedSkill.points)
val selectedSkillPoints: LiveData<Int> = _selectedSkillPoints
fun incrementSelectedSkillPoints {
_selectedSkillPoints.value = _selectedSkillPoints.value + 1
}
...
}
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel.selectedSkillPoints.observe(viewLifecycleOwner) { points ->
MaterialAlertDialogBuilder(requireContext())
.setMessage("sharedViewModel.selectedSkillPoints.value = $points")
.show()
}
Upvotes: 1