/ Android

How to communicate between fragments and activity using ViewModel

Communication is one of the most important topic for the developers and can be done in many ways. But the challenge is to do it in easiest and optimized way. We can do it by using static filed but it will occur memory issue. To avoid memory issue many developers do something like

(activity as MainActivity).passDataToAnotherFragment()

What is the problem with this way? It binds the fragment with a specific activity , reduce re-usability. Then what is the recommended way?

Google recommend to use interface (before ViewModel) doc. For this we have to implement the interface in activity class. Child fragments will hold a reference of the interface implemented by activity . Data will be passed through interface methods.

new---doc

Let’s assume a simple scenario where we have two fragment under same activity, one to input a number and another is to show the double (2 x input). Activity will also show a message Your input is 123 like this.

1_78J2gE_YraJK65-l0KVZsQ

Can you imagine how much code we need to write and how many scenario we have to handle. I am not going in details of it as this article is about the easiest solution rather than demonstrate interface implementation. You can find the implementation here.

Easy way

Here comes ViewModel to rescue us from handling a lot of scenario and implementing interface . We only need to create ViewModel class and create instance in fragment but using the activity scope so that it will be available for all the fragment of the activity including activity itself.
1_XXNlG11KOI3LjlR1mmdQCg
Create a ViewModel class

class SharedViewModel:ViewModel(){
    val inputNumber = MutableLiveData<Int>()
}

To emit or pass data from our input fragment create ViewModel in activity scope. To do this we have to pass the activity reference as argument of the ViewModelProvides.of() method. Noe just pass the data to ViewModel object like this

activity?.let {
    sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}

et_input.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(p0: Editable?) {}

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

    override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
        txt?.let {
            var input = 0
            if (txt.toString().isNotEmpty()) {
                input = txt.toString().toInt()
            }

            sharedViewModel?.inputNumber?.postValue(input)
        }
    }

In the activity we just need to create instance of our ViewModel and observe the required data like this

val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

sharedViewModel.inputNumber.observe(this, Observer {
    it?.let {
        // do some thing with the number
    }
})

Now what about output fragment? We can do same for the output fragment to observe the data. But keep in mind that we need create the ViewModel instance in activity scope, otherwise android will create a separate instance rather than sharing the same instance and we will not get the data.

For output fragment do it like this

activity?.let {
    val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

    sharedViewModel.inputNumber.observe(this, Observer {
    it?.let {
            // do some thing with the number
        }
    })
}

That’s it. Source code can be found here.

StackOverflow discussion

https://stackoverflow.com/questions/51203608/android-pass-value-from-child-fragment-back-to-parent-fragment/51741902#51741902

https://stackoverflow.com/questions/45495536/android-communication-between-an-activity-and-a-fragment/51761868#51761868

Happy Coding 😃