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()