How to favorite items using room in jetpack compose android

In this blog, we will explore how to favorite list items using room database in jetpack compose android with Example step by step in 2025.


How to favorite items using room in jetpack compose android

How to Favorite List Items using Room Database in Jetpack Compose Android with Example in 2025

Step 1 : Add below dependency -> build.gradle.kts -> In Project Level

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.7.3" apply false
id("com.android.library") version "8.7.3" apply false
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
//-----------------add below dependency-------------------------
id("com.google.dagger.hilt.android") version "2.49" apply false
id("com.google.devtools.ksp") version "1.9.22-1.0.17" apply false
}

Step 2 : Add below dependency -> build.gradle.kts -> In App Level

Add below dependency inside plugin tag ->
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
//------------------add below dependency----------------
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
id("com.google.devtools.ksp")
}

Add below dependency inside dependency tag ->
dependencies {
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
implementation("androidx.activity:activity-compose:1.9.3")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation(platform("androidx.compose:compose-bom:2024.12.01"))

//-------------------add below dependency------------------------
//Navigation
implementation("androidx.lifecycle:lifecycle-viewmodel-compose")
implementation("androidx.navigation:navigation-compose:2.8.5")
implementation("com.google.accompanist:accompanist-navigation-animation:0.30.1")

// Dependency Injection
implementation("com.google.dagger:hilt-android:2.49")
kapt("com.google.dagger:hilt-android-compiler:2.48")
implementation("androidx.hilt:hilt-work:1.2.0")
kapt("androidx.hilt:hilt-compiler:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.10.0")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Lifecycle
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")

// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")

// Coroutine Lifecycle Scopes
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")

// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.2")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")

// Coil
implementation("io.coil-kt:coil-compose:1.4.0")

// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")

//room
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
implementation("androidx.room:room-paging:2.6.1")

}

Step 3 : Add Internet Permission in Manifest file

<uses-permission android:name="android.permission.INTERNET"/>

Step 4 : Create Application Class for adding common things in through out the app level

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class ApplicationClass: Application()


Step 5 : Call Application Class in Manifest file -> because this class is first to be called when launch the application

<application
android:name=".ApplicationClass"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/Theme.MyJetpackComposeApp"
tools:targetApi="31">

Step 6 : Create data class -> MovieDetail.kt -> for holding the list data

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName

@Entity(tableName = "movieDetail")
data class MovieDetail(
@PrimaryKey(autoGenerate = false)
@SerializedName("id")
val id: String,
@SerializedName("title")
val title: String,
@SerializedName("thumbnail")
val thumbnail: String,
@SerializedName("publishDate")
val publishDate: String,
@SerializedName("isFavorite")
val isFavorite: Boolean,
)

Step 7 : Create MovieDao.kt interface class -> for handling the database operation

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface MovieDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(movieDetail: List<MovieDetail>)

@Query("Select * From movieDetail Where id = :id")
suspend fun getMovieDetailById(id: String): MovieDetail?

@Query("UPDATE movieDetail SET isFavorite=:isFavorite WHERE id = :id")
suspend fun updateFavorite(isFavorite: Boolean?, id: String)

@Query("DELETE FROM movieDetail WHERE id = :id")
suspend fun deleteMovieDetailById(id: String)

@Query("SELECT * FROM movieDetail")
suspend fun getAllMovieDetails(): List<MovieDetail?>
}

Step 8 : Create MovieDatabase.kt class -> for creating the database

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(version = 1, entities = [MovieDetail::class], exportSchema = false)
abstract class MovieDatabase : RoomDatabase() {
abstract fun getMovieDetailDao(): MovieDao
}

Step 9 : Create DatabaseModule.kt class -> for initializing the database and handle top of the module object class in daggerHilt.

import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DataBaseModule {
@Provides
@Singleton
fun provideMovieDatabase(@ApplicationContext context: Context): MovieDatabase {
return Room.databaseBuilder(
context,
MovieDatabase::class.java,
"movieWorld.db"
).build()
}

@Singleton
@Provides
fun provideMovieDetailDao(moviesDatabase: MovieDatabase): MovieDao =
moviesDatabase.getMovieDetailDao()
}

Step 10 : Create MovieViewModel.kt class -> for handling the business logic.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class MovieViewModel @Inject constructor(
private val movieDetailDao: MovieDao,
) : ViewModel() {
private val _movies = MutableStateFlow<List<MovieDetail?>>(arrayListOf())
val movies get() = _movies.asStateFlow()

init {
addMovieListToDB(moviesList)
}

private fun addMovieListToDB(movies: List<MovieDetail>) {
viewModelScope.launch {
if(movieDetailDao.getAllMovieDetails().isEmpty()){
movieDetailDao.insert(movies)
}
}
}

fun getMoviesFromDB() {
viewModelScope.launch {
_movies.value = movieDetailDao.getAllMovieDetails()
}
}

fun updateFavoriteMovies(isFavorite: Boolean, id: String) {
viewModelScope.launch {
movieDetailDao.updateFavorite(isFavorite, id)
_movies.value = _movies.value.map { item ->
if (id == item?.id) {
item.copy(isFavorite = isFavorite)
} else item
}
}
}

fun removeMovieFromDB(id: String) {
viewModelScope.launch {
movieDetailDao.deleteMovieDetailById(id)
}
}
}

Step 11 : Create MoviesRoute.kt class -> for creating design of movie screen 

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import coil.compose.rememberImagePainter

val moviesList = listOf(
MovieDetail(
"1",
"Avenger End Game",
"https://www.hindustantimes.com/ht-img/img/2023/11/01/1600x900/Screenshot_2023-06-02_105309_1685683406455_1698859576740.png",
"24-12-2024",
false
),
MovieDetail(
"2",
"Dune The Prophecy",
"https://i0.wp.com/www.thewrap.com/wp-content/uploads/2024/11/dune-prophecy-emily-watson-olivia-williams-hbo.jpg?fit=1200%2C675&ssl=1",
"24-12-2024",
false
),
MovieDetail(
"3",
"Guardian Galaxy",
"https://platform.vox.com/wp-content/uploads/sites/2/chorus/uploads/chorus_asset/file/15618476/tfa_poster_wide_header-1536x864-324397389357.0.0.1537961254.jpg?quality=90&strip=all&crop=0%2C3.4613147178592%2C100%2C93.077370564282&w=1200",
"24-12-2024",
false
),
MovieDetail(
"4",
"The Star Wars",
"https://static1.moviewebimages.com/wordpress/wp-content/uploads/2023/05/guardians-3.jpg",
"24-12-2024",
false
),
MovieDetail(
"5",
"Transformers Final Death",
"https://platform.polygon.com/wp-content/uploads/sites/2/2024/09/transformers_one_orion.jpg?quality=90&strip=all&crop=9.9872685185185,0,80.025462962963,100",
"24-12-2024",
false
),
MovieDetail(
"6",
"Avatar",
"https://www.vice.com/wp-content/uploads/sites/2/2019/11/1573610428860-Avatar-2.jpeg?w=1024",
"24-12-2024",
false
),
)

@Composable
fun MoviesRoute() {
val viewModel = hiltViewModel<MovieViewModel>()
val movies by viewModel.movies.collectAsState()

LifecycleEventEffect(Lifecycle.Event.ON_START) {
viewModel.getMoviesFromDB()
}

MovieScreen(movies, viewModel)
}

@Composable
fun MovieScreen(movies: List<MovieDetail?>, viewModel: MovieViewModel) {
LazyColumn(modifier = Modifier.padding(start = 5.dp, top = 10.dp, end = 5.dp)) {
itemsIndexed(movies) { _, item ->
item?.let {
MovieItemUi(item, viewModel)
}
}
}
}

@Composable
private fun MovieItemUi(
movieItem: MovieDetail,
viewModel: MovieViewModel
) {
Box {
Card(
modifier = Modifier
.fillMaxWidth()
.height(250.dp)
.padding(5.dp),
elevation = CardDefaults.cardElevation(
defaultElevation = 2.dp
),
colors = CardDefaults.cardColors(
containerColor = Color.White
),
shape = RoundedCornerShape(8.dp),
) {
Column {
Image(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentScale = ContentScale.FillBounds,
painter = rememberImagePainter(movieItem.thumbnail),
contentDescription = "The design logo",
)
Text(
modifier = Modifier.padding(10.dp),
text = movieItem.title,
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
)
}
}

IconToggleButton(
modifier = Modifier
.padding(15.dp)
.align(Alignment.TopEnd)
.background(Color.White.copy(alpha = 0.9f), shape = CircleShape),
checked = movieItem.isFavorite,
onCheckedChange = {
viewModel.updateFavoriteMovies(it, movieItem.id)
}
) {
Icon(
modifier = Modifier.size(28.dp),
imageVector =
if (movieItem.isFavorite) Icons.Default.Favorite
else Icons.Filled.FavoriteBorder,
contentDescription = "Favorite",
tint = Color.Black
)
}
}
}

Step 12 : Call MoviesRoute.kt class in MainActivity.kt class -> must add @AndroidEntryPoint for initializing daggerHilt

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.example.myjetpackcomposeapp.room.MoviesRoute
import com.example.myjetpackcomposeapp.ui.theme.MyJetpackComposeAppTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyJetpackComposeAppTheme {
MoviesRoute()
}
}
}
}

Reference

Here are the reference blog of complete tutorial of How to favorite items using room in jetpack compose android step by step in 2025 you can easily learn and use in your code.


Conclusion

In this blog i have explain you How to favorite list items using room database in jetpack compose android step by step in 2025. you can easily use this code and modify according to your need.

Final output of this code

How to favorite items using room in jetpack compose android
Oversimplified Coding

I am Shubhangam Upadhyay founder of Oversimplified Coding. My motive of this blog to help those developer are just start there carrier in coding line. We cover here Android, Kotlin, Core Java, Jetpack Compose and Flutter etc. related topics. I have 6+ Years of experience in Android Development and working on MNC company as a Senior Android Developer Position. I have worked on 5 different companies and experience on many different type of real time application project.

*

Post a Comment (0)
Previous Post Next Post