Search code examples
androidformskotlinandroid-livedataandroid-viewmodel

Combine multiple LiveData values into one using MediatorLiveData


I have an email, password, and phone number, that are all live data values updated in real time by a fragment as the user types.

How can I have a live data variable that observes all three of those variables, and returns something based on all of their combined results?

ViewModel

class AuthenticationViewModel: ViewModel() {
    var email: MutableLiveData<String> = MutableLiveData("")
    var password: MutableLiveData<String> = MutableLiveData("")
    var phoneNumber: MutableLiveData<Int> = MutableLiveData(0)

    val isFormValid: MediatorLiveData<Boolean> = {
        // if email is valid
        // and if password is valid
        // and if phoneNumber is valid
        // return true
        // How do I do this?
    }
}

Fragment

binding.emailInput.addTextChangedListener { email ->
    viewModel.email.value = email.toString()
}

viewModel.isFormValid.observe(this, {
    // do what I want in real time
})

Solution

  • Try adding each liveData as a source like this and check the other liveData's value when one of them trigger a change: ViewModel

    class AuthenticationViewModel: ViewModel() {
        val email: MutableLiveData<String> = MutableLiveData("")
        val password: MutableLiveData<String> = MutableLiveData("")
        val phoneNumber: MutableLiveData<Int> = MutableLiveData(0)
    
        val isFormValid: MediatorLiveData<Boolean> = MediatorLiveData().apply {
            addSource(email) { emailValue -> isValidEmail(emailValue) && isValidPassword(password.value) && isValidPhoneNumber(phoneNumber.value) }
            addSource(password) { passwordValue -> isValidEmail(email.value) && isValidPassword(passwordValue) && isValidPhoneNumber(phoneNumber.value) }
            addSource(phoneNumber) { phoneNumberValue -> isValidEmail(email.value) && isValidPassword(password.value) && isValidPhoneNumber(phoneNumberValue) }
        
        }
    }
    

    And then just observe the livedata as usual:

    Fragment

    binding.emailInput.addTextChangedListener { email ->
        viewModel.email.value = email.toString()
    }
    
    viewModel.isFormValid.observe(this, {
        // do what I want in real time
    })
    

    solution inspired by https://medium.com/codex/how-to-use-mediatorlivedata-with-multiple-livedata-types-a40e1a59e8cf , kinda the same but not using Triple and a new class for it, also you can add as many as you want.