Search code examples
androidandroid-fragmentsandroid-livedatamutablelivedata

Mutable Live Data Value is updated on creation of fragment


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


Solution

  • 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.