I have rigged up a simple login fragment with a view model. Here is the fragment :
class LoginFragment : Fragment() {
companion object {
fun newInstance() = LoginFragment()
}
private lateinit var viewModel: LoginViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
viewModel.loginState.observe(this, Observer{
handleState(it)
})
login_button.setOnClickListener {
viewModel.isUserValid(username.text.toString(), pass.toString())
}
}
private fun handleState(status: RegisterState) {
if (status.statusMessage.equals("Good"))
view?.findNavController()?.navigate(R.id.action_registerFragment_to_homeFragment)
else
Snackbar.make(login_container, "Welcome to SwA", Snackbar.LENGTH_LONG).show();
}
}
and here is my view model :
class LoginViewModel : ViewModel() {
lateinit var auth: FirebaseAuth
private var _loginState = MutableLiveData<LoginState>()
val loginState : MutableLiveData<LoginState> get() = _loginState
init {
loginState.value = LoginState()
}
fun isUserValid(email: String, password: String): Boolean {
//Add call to authenticate through firebase
auth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener {
if (it.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
val user = auth.currentUser
//updateUI(user)
} else {
// If sign in fails, display a message to the user.
_loginState.value?.statusMessage = "Authentication Failed"
}
}
return true
}
}
This works and registers a change to the string status when a failed log in is attempted, however it also submits an onChange() when loading the fragment causing the snackbar to appear in the UI before they have actually entered anything when the fragment is created. How can I initialize the view state without triggering an onChange() ?
LiveData class has a method
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
which checks if owner of the LifeCycle
, Activity
or Fragment
, is on a state after STARTED which means for an Activity if it has called onStart()
.
So whenever an Activity
or Fragment
observes a LiveData it gets LiveData's value by setValue
or PostValue
after onStart is called.
One of the ways to prevent same value to update UI more than once is to use SingleLiveEvent
class, example here. Another one is using Event class, example is here.