Shared ViewModel in Android: Shared between Fragments

Shared ViewModel in Android: Shared between Fragments

Communication between Activities or Fragments in Android is a very common thing. Almost every application has some communication between various activities or fragments.

In this blog, we will learn how we can use the ViewModel in our application to communicate between various fragments in our application. We say it as SharedViewModel.

Data sharing between Fragments

Using SharedViewModel, we can communicate between fragments. If we consider two fragments, both the fragments can access the ViewModel through their activity.

Here, one fragment updates the data within the ViewModel which is shared between both the fragments and another fragment observes the changes on that data.

Let's take a small example and learn it.

Note: Do not forget to add the dependencies required for ViewModel, LiveData.

First, we will create a class SharedViewModel.

class SharedViewModel : ViewModel() {
    val message = MutableLiveData<String>()

    fun sendMessage(text: String) {
        message.value = text
    }
}

Now, we are going to create two Fragments:

  • MessageReceiverFragment: This Fragment is going to receive the message which will be sent by MessageSenderFragment. It will have a TextView which will show the received message.
  • MessageSenderFragment: This Fragment is going to send the message which will be received by MessageReceiverFragment. It will have a Button to send the message.

MessageReceiverFragment class:

class MessageReceiverFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_receiver, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
        model.message.observe(viewLifecycleOwner, Observer {
            textViewReceiver.text = it
        })
    }
}

fragment_receiver layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textViewReceiver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Your Message"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MessageSenderFragment class:

class MessageSenderFragment : Fragment() {
    lateinit var model: SharedViewModel

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_sender, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
        button.setOnClickListener { model.sendMessage("MindOrks") }
    }
}

fragment_sender layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Your Message"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Now, let's update the activity_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/receiverFragment"
        android:name="com.mindorks.sharedviewmodelsample.MessageReceiverFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toTopOf="@+id/senderFragment"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <fragment
        android:id="@+id/senderFragment"
        android:name="com.mindorks.sharedviewmodelsample.MessageSenderFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/receiverFragment" />


</androidx.constraintlayout.widget.ConstraintLayout>

Our MainActivity Class.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Now, you are ready to run the app, see if it is working as expected. It should work.

Let's discuss how it is working:

  • Here, we have created an activity that consists of two fragments. The same activity is the host for both the fragment.
  • In both the fragment, we have created the object of SharedViewModel which is the same object as we are using the same single activity as an owner. This is the reason it is shared. Notice that we have used the requireActivity().
ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
  • In the MessageSenderFragment, we are sending the message on button click, which sets the value of message - LiveData in the SharedViewModel.
  • Then, in the MessageReceiverFragment, we are observing the change in the message - LiveData, and whenever the message is coming, we are updating the data in textView.

This is how it works.

Important: Create SharedViewModel using the same owner.

Conclusion

In this blog, we learned about Shared ViewModel in Android to communicate with other fragments. In the last, we saw one example of fragment communication using Shared ViewModel. Hope you enjoyed this blog.

Keep Learning :)

Team MindOrks!