How to Build a Simple Contact List Android App using MVVM and Room Database?
Last Updated :
28 Apr, 2025
A contact list app is a great tool to store and manage our contacts in one place. With the rise of smartphones and mobile devices, contact list apps have become increasingly popular. In this article, we will explore the development of a contact list app using Kotlin, Room Database, and MVVM architecture.
Model — View — ViewModel (MVVM) is the industry-recognized software architecture pattern that overcomes all drawbacks of MVP and MVC design patterns. MVVM suggests separating the data presentation logic(Views or UI) from the core business logic part of the application.
The separate code layers of MVVM are:
- Model: This layer is responsible for the abstraction of the data sources. Model and ViewModel work together to get and save the data.
- View: The purpose of this layer is to inform the ViewModel about the user’s action. This layer observes the ViewModel and does not contain any kind of application logic.
- ViewModel: It exposes those data streams which are relevant to the View. Moreover, it serves as a link between the Model and the View.
A sample video 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 in Android Studio
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: Set Up Project dependencies
Add room, view model, and live data dependencies
// Room Database
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
implementation "androidx.room:room-ktx:2.3.0"
androidTestImplementation "androidx.room:room-testing:2.3.0"
// Lifecycle components
// MVVM
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.5.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation 'androidx.activity:activity-ktx:1.6.1'
Add kotlin-kapt plugin inside plugin{}
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
Enable View Binding
To enable view binding add this code inside the android {} block in build.gradle(app) file
buildFeatures {
viewBinding = true
}
Step 3: Create an entity class (Contacts) for Room Database
Kotlin
@Entity(tableName = "Contacts")
class Contacts (
@PrimaryKey(autoGenerate = true)
var id : Int?=null,
var name : String,
var number : String
)
Step 4: Create ContactDao interface to perform queries
Kotlin
@Dao
interface ContactDao {
@Query("Select * from Contacts")
fun getAllContacts() : LiveData<List<Contacts>>
@Insert(onConflict = OnConflictStrategy.REPLACE )
fun insertContact(contact : Contacts)
@Delete
fun delete(contact: Contacts)
}
Step 5: Create Contact Database
The database is defined as an abstract class "ContactDatabase" which extends Room's "RoomDatabase" class. The class is annotated with "@Database" which tells Room that this is a database class and it should be used to create a database.
Kotlin
@Database(entities = [Contacts::class], version = 1, exportSchema = false)
abstract class ContactDatabase : RoomDatabase() {
// Dao interface for the database
abstract fun contactsDao() : ContactDao
companion object {
@Volatile
var INSTANCE : ContactDatabase?=null
// Singleton instance of the database
fun getDatabaseInstance(context : Context) : ContactDatabase{
val tempInstance = INSTANCE
if(tempInstance!=null){
return tempInstance
}
// Synchronized block to make sure that
// only one instance of the database is created
synchronized(this){
val roomDatabaseInstance = Room.databaseBuilder(context,ContactDatabase::class.java,"Contacts").allowMainThreadQueries().build()
INSTANCE = roomDatabaseInstance
return roomDatabaseInstance
}
}
}
}
Step 6: Design the layout
activity_main.xml
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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@android:drawable/ic_input_add" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_create_contact
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"
tools:context=".CreateContact">
<EditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Name">
</EditText>
<EditText
android:id="@+id/etNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="Enter Number">
</EditText>
<Button
android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save"
android:layout_gravity="center">
</Button>
</LinearLayout>
contacts_layout for recycler view
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="2dp"
android:layout_margin="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3"
android:background="@color/white">
<ImageView
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@drawable/ic_baseline_person_24">
</ImageView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:weightSum="2"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/contactName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sangyan Bhagi"
android:textSize="22dp"
android:layout_weight="1"
android:layout_marginStart="5dp"
android:textColor="@color/black"
android:textStyle="bold">
</TextView>
<TextView
android:id="@+id/contactNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="9131902797"
android:textSize="22dp"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp"
android:textColor="@color/black"
android:textStyle="bold">
</TextView>
</LinearLayout>
<ImageView
android:id="@+id/deleteButton"
android:layout_width="30dp"
android:layout_height="50dp"
android:layout_weight="1"
android:src="@drawable/ic_baseline_delete_24">
</ImageView>
</LinearLayout>
</androidx.cardview.widget.CardView>
Step 7: Create ContactRepository class
The repository class is named "ContactRepository" and it takes in a parameter of type "ContactDao" in its constructor. The ContactRepository class is an abstraction layer between the Room database and the ViewModel, it will handle the data operations.
Kotlin
class ContactRepository(val dao : ContactDao)
{
// function to get all contacts from the database
fun getAllContacts() : LiveData<List<Contacts>>{
return dao.getAllContacts()
}
// function to insert a contact in the database
fun insertContact(contact : Contacts) {dao.insertContact(contact)}
// function to delete a contact from the database
fun deleteContact(contact: Contacts) {
dao.delete(contact)
}
}
Step 8: Create a Contact adapter class
The adapter class is named "ContactsAdapter" and it takes in two parameters in its constructor: "context" and "list". The "context" parameter is used to access the application's context and the "list" parameter is a list of contacts that will be displayed in the RecyclerView. In the onBindViewHolder method, the contact's name and number are set to the TextViews of the layout. The delete button onClickListener is set to delete the contact from the database and notify the adapter of the change. Also, the itemView onClickListener is set to make a phone call to the number of the contact.
Kotlin
class ContactsAdapter(val context : Context, val list : List<Contacts>) : RecyclerView.Adapter<ContactsAdapter.ViewHolder>() {
// Inner ViewHolder class
class ViewHolder(val binding : ContactsLayoutBinding) : RecyclerView.ViewHolder(binding.root){}
// DAO instance to interact with the database
private val dao = ContactDatabase.getDatabaseInstance(context).contactsDao()
// function to inflate the layout for each contact and create a new ViewHolder instance
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ContactsLayoutBinding.inflate(LayoutInflater.from(parent.context),parent,false)
)
}
// function to bind the data to the view elements of the ViewHolder
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.binding.contactName.text = list[position].name
holder.binding.contactNumber.text = list[position].number
// delete button onClickListener to delete the
// contact from the database and notify the
// adapter of the change
holder.binding.deleteButton.setOnClickListener{
dao.delete(list[position])
notifyItemRemoved(position)
}
// itemView onClickListener to make a phone call
// to the number of the contact
holder.itemView.setOnClickListener{
val intent = Intent(Intent.ACTION_CALL, Uri.parse("" + list[position].number))
context.startActivity(intent)
}
}
// function returns the number of items in the list
override fun getItemCount(): Int {
return list.size
}
}
Step 9: Create Contact View Model
The ViewModel class is named "ContactViewModel" and it takes in a single parameter "application" in its constructor. The ViewModel class extends the "AndroidViewModel" class which is a subclass of the "ViewModel" class that is aware of the application context.
Kotlin
class ContactViewModel(application: Application) : AndroidViewModel(application) {
val repository : ContactRepository
init {
val dao = ContactDatabase.getDatabaseInstance(application).contactsDao()
repository = ContactRepository(dao)
}
fun addContacts(contact : Contacts){
repository.insertContact(contact)
}
fun getAllContacts() : LiveData<List<Contacts>> = repository.getAllContacts()
}
Step 10: Write Code for CreateContact Activity
Kotlin
class CreateContact : AppCompatActivity() {
// private variable to inflate the layout for the activity
private lateinit var binding : ActivityCreateContactBinding
// variable to access the ViewModel class
val viewModel : ContactViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// inflate the layout
binding = ActivityCreateContactBinding.inflate(layoutInflater)
setContentView(binding.root)
// set onClickListener for save button
binding.save.setOnClickListener{
createContact(it)
}
}
// function to create new contact and call
// the addContacts function from the ViewModel class
private fun createContact(it: View?) {
// read name and number from EditTexts
val name = binding.etName.text.toString()
val number = binding.etNumber.text.toString()
// create new contact object
val data = Contacts(null,name = name , number = number)
// call addContacts function from the ViewModel class
viewModel.addContacts(data)
// display a Toast message to confirm the save
Toast.makeText(this@CreateContact, "Saved", Toast.LENGTH_SHORT).show()
// start MainActivity
startActivity(Intent(this@CreateContact,MainActivity::class.java))
}
}
Step 11: Write Code for MainActivity
Kotlin
class MainActivity : AppCompatActivity() {
// private variable to inflate the layout for the activity
private lateinit var binding: ActivityMainBinding
// variable to access the ViewModel class
val viewModel : ContactViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// inflate the layout
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// set onClickListener for the floating action button
binding.floatingActionButton.setOnClickListener{
val intent = Intent(this , CreateContact::class.java)
startActivity(intent)
}
// Observe the LiveData returned by the getAllContacts method
viewModel.getAllContacts().observe(this , Observer { list->
// set the layout manager and the adapter for the recycler view
binding.recyclerView.layoutManager = LinearLayoutManager(applicationContext)
binding.recyclerView.adapter = ContactsAdapter(this,list)
})
}
}
Output:
Similar Reads
How to Build a Simple Note Android App using MVVM and Room Database? Android provides us a feature with which we can store users' data inside their mobile itself with different storage options such as Shared Preferences, SQLite database, and the Room Database. All the data storing techniques are having different use cases. In this article, we will specifically take a
15+ min read
How to Build a Grocery Android App using MVVM and Room Database? In this article, we are going to build a grocery application in android using android studio. Many times we forget to purchase things that we want to buy, after all, we canât remember all the items, so with the help of this app, you can note down your grocery items that you are going to purchase, by
12 min read
How to Build a ChatGPT Like App in Android using OpenAI API? Chat GPT is nowadays one of the famous AI tools which are like a chatbot. This chatbot answers all the queries which are sent to it. In this article, we will be building a simple ChatGPT-like android application by integrating the OpenAI API(ChatGPT) where we can ask any question and get an appropri
5 min read
How to Build a Food and Their Price List Android App using MVVM Architecture with Kotlin? In this article, we will food app that displays a list of foods and their price using the retrofit library and MVVM architecture. Model â View â ViewModel (MVVM) is the industry-recognized software architecture pattern that overcomes all drawbacks of MVP and MVC design patterns. MVVM suggests separa
5 min read
How to Build a Simple Notes App in Android? Notes app is used for making short text notes, updating when you need them, and trashing when you are done. It can be used for various functions as you can add your to-do list in this app, some important notes for future reference, etc. The app is very useful in some cases like when you want quick a
9 min read