Home > Software engineering >  ViewModel Data Loses State When Accessed From Second Fragment
ViewModel Data Loses State When Accessed From Second Fragment

Time:11-20

I'm facing an issue with my ViewModel that I use to hold user login data.

I update this ViewModel with user data from fragment A after a user logs in, but when I try to access the data from fragment B the data fields I just set are always null.

When fragment B is initialized the user LiveData field is never initially observed, however, when I trigger a change to the user object from fragment B the change is correctly observed within fragment B. It appears that the previous values of the fields in my ViewModel never reach fragment B, but new values do.

For a sanity check I made a simple string variable (not even a LiveData object) that I set to a value from fragment A, then, after navigating to fragment B I printed the value: it is uninitialized every time. It's as if the ViewModel I inject into fragment B is totally separate from the ViewModel I inject into fragment A.

What am I missing that causes the ViewModel observation in fragment B not to initially trigger with the last known value of user set from fragment A?

Fragment A

class FragmentA : Fragment() {
    private val viewModel: LoginViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel.user.observe(this, {
            it?.let {
                //Called successfully every time
                navigateToFragmentB()
            }
        })
        
        val mockUserData = User()
        viewModel.loginSuccess(mockUserData)
    }
}

Fragment B

class FragmentB : Fragment() {
    private val viewModel: LoginViewModel by viewModel()
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        ...
        
        viewModel.user.observe(viewLifecycleOwner, { user ->
            user?.let {
                binding.initialsBubble.text = user.getInitials()
            } ?: navigateAway()
        })
    }
}

ViewModel

class LoginViewModel(
    private val loginRepo: LoginRepo
) : ViewModel() {
    private val _user = MutableLiveData<User?>()
    val user: LiveData<User?> = _user
    

    fun loginSuccess(result: AuthenticationResult) {
        val user = loginRepo.login(result)
        _user.postValue(user)
    }
}

CodePudding user response:

You should use sharedViewModel for both fragment.

Use these lines of code in both fragments

private val viewModel: LoginViewModel by activityViewModels()

instead of

private val viewModel: LoginViewModel by viewModel()
  • Related