Rayge
Rayge

Reputation: 21

MutableLiveData value not updating confusion?

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

Answers (1)

Dissident Dev
Dissident Dev

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

Related Questions