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 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
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.