Search code examples
androidkotlinandroid-viewmodel

Unresolved reference (vars) in Fragment class, MVVM, Viewmodel


I'm following UDACITY free course on developing Android apps with Kotlin and I'm actually at the Viewmodel/MVVM part of the lesson, ie implementing Viewmodel classes for a better separation of concerns. So anyway, Im blocked right now. The exercise I'm on is about creating the Viewmodel class and transferring variables and functions from the Fragment class to this newly created class. I follow the tutorial step by step, check the correct answer on the provided git diff and I still find myself blocked by Unresolved reference errors.

Before changing the code, i had to update my Gradle module file to use ViewModel

//ViewModel
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

Then I had to declare my Viewmodel object in my Fragment class

gameViewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)

ViewModelProviders being deprecated, old course, I had to change, after search, to

gameViewModel = ViewModelProvider(this).get(GameViewModel::class.java)

It seems to be the right way to do, but I'm still left with some Unresolved references on the variables word (gameViewModel.word) and score (gameViewModel.score) in the Fragment class, unable to compile. I dont know if I declared the Viewmodel object correctly or if I'm missing something else...

I dont have this problem, Unresolved reference, with my ViewModel class functions, ie gameViewModel.onCorrect() and gameViewModel.onSkip(). They seem to be properly declared and called in the Fragment class, which begs me the question on my variables, word and score...

My Fragment class

package com.example.android.guesstheword.screens.game

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment.findNavController
import com.example.android.guesstheword.R
import com.example.android.guesstheword.databinding.GameFragmentBinding
import timber.log.Timber

class GameFragment : Fragment() {

private lateinit var binding: GameFragmentBinding

private lateinit var gameViewModel: GameViewModel



override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {

    // Inflate view and obtain an instance of the binding class
    binding = DataBindingUtil.inflate(
            inflater,
            R.layout.game_fragment,
            container,
            false
    )

    Timber.i("onCreateView  GameFragment called")
    gameViewModel = ViewModelProvider(this).get(GameViewModel::class.java)
    Timber.i("ViewModelProvider is called")

    binding.correctButton.setOnClickListener {
        gameViewModel.onCorrect()
        updateScoreText()
        updateWordText()
    }
    binding.skipButton.setOnClickListener {
        gameViewModel.onSkip()
        updateScoreText()
        updateWordText()
    }
    updateScoreText()
    updateWordText()
    return binding.root

}

/**
 * Called when the game is finished
 */
fun gameFinished() {
    val action = GameFragmentDirections.actionGameToScore(gameViewModel.score)
    findNavController(this).navigate(action)
}

/** Methods for updating the UI **/

private fun updateWordText() {
    binding.wordText.text = gameViewModel.word
}

private fun updateScoreText() {
    binding.scoreText.text = gameViewModel.score.toString()
}

override fun onDestroyView() {
    super.onDestroyView()
    Timber.i("onDestroyView GameFragment called")
}
}

My ViewModel class

package com.example.android.guesstheword.screens.game

import androidx.lifecycle.ViewModel
import timber.log.Timber

var word = ""
var score = 0

private lateinit var wordList: MutableList<String>

class GameViewModel: ViewModel() {
init {
    Timber.i("GameViewModel is created")
    resetList()
    nextWord()
}

override fun onCleared() {
    super.onCleared()
    Timber.i("GameViewModel is cleared")
}
/**
 * Resets the list of words and randomizes the order
 */


fun resetList() {
    wordList = mutableListOf(
            "queen",
            "hospital",
            "basketball",
            "cat",
            "change",
            "snail",
            "soup",
            "calendar",
            "sad",
            "desk",
            "guitar",
            "home",
            "railway",
            "zebra",
            "jelly",
            "car",
            "crow",
            "trade",
            "bag",
            "roll",
            "bubble"
    )
    wordList.shuffle()
}
/**
 * Moves to the next word in the list
 */
private fun nextWord() {
    //Select and remove a word from the list
    if (wordList.isEmpty()) {
        //gameFinished()
    } else {
        word = wordList.removeAt(0)
    }
}
/** Methods for buttons presses **/

fun onSkip() {
    score--
    nextWord()
}

fun onCorrect() {
    score++
    nextWord()
}
}

Where did I screw up ?


Solution

  • The variables you're trying to access aren't part of the same scope:

    var word = ""
    var score = 0
    
    private lateinit var wordList: MutableList<String>
    
    class GameViewModel: ViewModel() {
    ...
    

    your variables are declared outside of the ViewModel, add them inside the class GameViewModel to make them instance variables:

    class GameViewModel: ViewModel() {
    var word = ""
    var score = 0
    ...
    }