Shared ViewModel in Android
In android, we can use ViewModel to share data between various fragments or activities by sharing the same ViewModel among all the fragments and they can access everything defined in the ViewModel. This is one way to have communication between fragments or activities. Almost every application has some communication between various activities or fragments.
What we are going to build in this article?
In the article, we are going to learn about Shared ViewModel in Android to communicate with other fragments. We will develop an application containing two fragments in which one fragment updates the data within the ViewModel which is shared between both the fragments and another fragment observes the changes on that data and displays the updated one on the screen. Application’s work is to send->receive->display a message. A sample GIF is given below to get an idea about what we are going to do in this article.
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.
Step 2: Create a class SharedViewModel
Go to the SharedViewModel.kt file and refer to the following code. Below is the code for the SharedViewModel.kt file. Comments are added inside the code to understand the code in more detail.
Kotlin
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class SharedViewModel : ViewModel() { // variable to contain message whenever // it gets changed/modified(mutable) val message = MutableLiveData<String>() // function to send message fun sendMessage(text: String) { message.value = text } } |
Step 3: Create two fragments and they are – MessageSenderFragment & MessageReceiverFragment
MessageSenderFragment – To send the message which will be received by MessageReceiverFragment. It will have a Button to send the message after clicking it. Go to the MessageSenderFragment.kt file and refer to the following code. Below is the code for the MessageSenderFragment.kt file. Comments are added inside the code to understand the code in more detail.
Kotlin
import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.EditText import androidx.lifecycle.ViewModelProvider class MessageSenderFragment : Fragment() { // to send message lateinit var btn: Button // to write message lateinit var writeMSg: EditText override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_message_sender, container, false ) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super .onViewCreated(view, savedInstanceState) // reference for button and EditText btn = view.findViewById(R.id.button) writeMSg = view.findViewById(R.id.writeMessage) // create object of SharedViewModel val model = ViewModelProvider(requireActivity()).get(SharedViewModel:: class .java) // call function "sendMessage" defined in SharedVieModel // to store the value in message. btn.setOnClickListener { model.sendMessage(writeMSg.text.toString()) } } } |
Navigate to the app > res > fragment_message_sender.xml and add the below code to that file. Below is the code for the fragment_message_sender.xml file.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" > < EditText android:id = "@+id/writeMessage" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:hint = "Write your message" android:textAlignment = "center" app:layout_constraintBottom_toTopOf = "@+id/button" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> < Button android:id = "@+id/button" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginBottom = "100dp" android:text = "Share Your Message" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
MessageReceiverFragment – To receive the message sent by MessageSenderFragment. It will have a TextView to show the updated message on the screen. Go to the MessageReceiverFragment.kt file and refer to the following code. Below is the code for the MessageReceiverFragment.kt file. Comments are added inside the code to understand the code in more detail.
Kotlin
class MessageReceiverFragment : Fragment() { // to contain and display shared message lateinit var displayMsg: TextView override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // inflate the fragment layout return inflater.inflate(R.layout.fragment_message_receiver, container, false ) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super .onViewCreated(view, savedInstanceState) // reference for the container declared above displayMsg = view.findViewById(R.id.textViewReceiver) // create object of SharedViewModel val model = ViewModelProvider(requireActivity()).get(SharedViewModel:: class .java) // observing the change in the message declared in SharedViewModel model.message.observe(viewLifecycleOwner, Observer { // updating data in displayMsg displayMsg.text = it }) } } |
Navigate to the app > res > fragment_message_receiver.xml and add the below code to that file. Below is the code for the fragment_message_receiver.xml file.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" > < TextView android:id = "@+id/textViewReceiver" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textSize = "20sp" android:textColor = "@color/black" android:textAlignment = "center" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
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() in both the fragments.
. . .
ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
. . .
Step 3: Update the activity_main.xml
The activity consists of two fragments and is the host for both the fragments.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > < fragment android:id = "@+id/receiverFragment" android:name = "com.gfg.article.sharedviewmodel.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.gfg.article.sharedviewmodel.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 > |
MainActivity.kt remains as it is.
Kotlin
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } |
Now, Run the app.
Output:
Source Code: Click Here
Please Login to comment...