demsdems
demsdems

Reputation: 27

Cannot fill a MutableLiveData of type ArrayList, outcome is always null

Im working on a quizgame and i want to store some ids in a MutableLiveData-arraylist. Therfore i made a function to loop all my documents in de database and add each ID to the arraylist. BUT the outcome is always null. I don't see where i go wrong?

I'm working with a MVVM-structure

GameViewModel:

class GameViewModel : ViewModel() {

// database instance
val db = FirebaseFirestore.getInstance()

// the current category
private val _category = MutableLiveData<String>()
val category: LiveData<String>
    get() = _category

// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>()
val questionIdsArray: LiveData<ArrayList<Long>>
    get() = _questionIdsArray

// the current question
private val _question = MutableLiveData<String>()
val question: LiveData<String>
    get() = _question


/**
 * Set Current Category
 */
fun SetCategory (categoryName: String){
    _category.value = categoryName
}

/**
 * Get the list of QuestionIds
 */
fun GetListQuestionIds() {
    db.collection("questions")
        .whereEqualTo("category", "$_category")
        .get()
        .addOnSuccessListener { documents ->
            for (document in documents) {
                _questionIdsArray.value?.add(document.data["questionid"] as Long)
                Log.d("GetSize","${_questionIdsArray.value?.size}")
            }
            Log.d("GetSize2","${_questionIdsArray.value?.size}")
        }
        .addOnFailureListener { exception ->
            Log.w("errorforloop", "Error getting documents: ", exception)
        }
}
/**
 * Get a Question
 */
fun GetQuizQuestion() {
    Log.d("retro","${_questionIdsArray.value?.size}")
    db.collection("questions")
        .whereEqualTo("category", "$_category")
        .whereEqualTo("questionid", "${_questionIdsArray.value?.get(0)}")
        .get()
        .addOnSuccessListener { documents ->
            for (document in documents) {
                _question.value = document.data["question"].toString()
            }
        }
        .addOnFailureListener { exception ->
            Log.w("err", "Error getting documents: ", exception)
        }
}

GAMEFRAGMENT:

class GameFragment : Fragment() {

private lateinit var viewModel: GameViewModel

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val binding = FragmentGameBinding.inflate(inflater)

    // Get the viewModel
    viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
    binding.lifecycleOwner = this

    // Set the viewModel for DataBinding - this allows the bound layout access to all of the data in the VieWModel
    binding.gameviewModel = viewModel

    //arguments passed
    val selectedCategory = arguments?.getString("selectedCategory")!!

    //set current category so that the viewModel can use it
    viewModel.SetCategory(selectedCategory)

    viewModel.GetListQuestionIds()
    viewModel.GetQuizQuestion()

    return binding.root
}

If someone can enlighten me ...

Upvotes: 1

Views: 727

Answers (1)

dominicoder
dominicoder

Reputation: 10155

Your Problem

You're not initializing the array. This is your code:

// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>()
val questionIdsArray: LiveData<ArrayList<Long>>
    get() = _questionIdsArray

This declares a MutableLiveData of type ArrayList<Long>, but does not initialize it so its value defaults to null.

In your for loop you conditionally add items:

_questionIdsArray.value?.add(document.data["questionid"] as Long)

But of course value was never initialized so it's null so add is no-op (does nothing).

The Solution

Just ensure you initialize the live data object at some point.

You could do this inline in the declaration:

// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>(arrayListOf())
val questionIdsArray: LiveData<ArrayList<Long>>
    get() = _questionIdsArray

Or during your attempt to populate the list:

    .addOnSuccessListener { documents ->
        val idsArray = arrayListOf<Long>() // Non-null list to add to
        for (document in documents) {
            idsArray.add(document.data["questionid"] as Long)
            Log.d("GetSize","${idsArray.size}")
        }

        _questionIdsArray.value = idsArray // Now set live data with a valid list
        Log.d("GetSize2","${_questionIdsArray.value?.size}")
    }

Upvotes: 5

Related Questions