androidkotlinmvvmandroid-livedataandroid-viewmodel

LiveData with data class doesn't update in ViewModel


I want to use my data class AuthData in my MainViewModel.

package de.hsfl.appamigos.capturetheflag

import androidx.lifecycle.MutableLiveData

data class AuthData(var game: MutableLiveData<String>)
package de.hsfl.appamigos.capturetheflag

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    private val authData: MutableLiveData<AuthData> = MutableLiveData()

    fun getGame(): LiveData<AuthData> = authData

    fun setGame(newAuthData: AuthData) {
        authData.value?.game = newAuthData.game
    }

    fun startNewGame(text: String) {
        val newGame = MutableLiveData("1234")
        val newAuthData = AuthData(newGame)
        setGame(newAuthData)
    }
}

When I try to observe changes in one of my Fragments it doesnt get notified.

package de.hsfl.appamigos.capturetheflag

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import de.hsfl.appamigos.capturetheflag.databinding.FragmentLobbyBinding

class LobbyFragment : Fragment() {

    ...

    val mainViewModel: MainViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View? {
        // Inflate the layout for this fragment
        //val root = inflater.inflate(R.layout.fragment_lobby, container, false)
        val binding = FragmentLobbyBinding.inflate(inflater)
        val naviGraph = findNavController()

        mainViewModel.getGame().observe(viewLifecycleOwner) {
            binding.textViewGameID.text = it.game.toString()
        }

        ...

        return binding.root;
        //return inflater.inflate(R.layout.fragment_lobby, container, false)
    }
}

I tried taking out the data class and only work with a Mutable and that worked so it's all connected kinda right. I guess it is because the authData doesn't change, but the attribute game does. How do I observe a change on the attribute itself? I cant work around the data class because that's demanded by my professor. I could use a normal class but I cant see how that would help.


Solution

  • You have a LiveData in a data class in a LiveData. That doesn't sound right.

    Your data class should only contain simple classes, that is, Numbers or Strings etc., or other data classes. Also your data classes should be immutable when exposed from the view model, that is, they only contain val properties. Your data class should look something like this:

    data class AuthData(val game: String)
    

    Adapt the view model to the changed data class like this:

    class MainViewModel : ViewModel() {
        private val authData: MutableLiveData<AuthData> = MutableLiveData()
    
        fun getGame(): LiveData<AuthData> = authData
    
        private fun setGame(newAuthData: AuthData) {
            authData.value = newAuthData
        }
    
        fun startNewGame(text: String) {
            val newGame = "1234"
            val newAuthData = AuthData(newGame)
            setGame(newAuthData)
        }
    }
    

    Now everything should work fine.