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.
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.
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.
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
Happy Coding 😃