Jetpack Compose has revolutionized Android development by introducing a declarative approach to building UI. Coupled with Room, Google’s ORM (Object Relational Mapping) library, developers can seamlessly handle local database interactions. In this guide, we will explore how to integrate Room with Jetpack Compose, focusing on creating and managing a Room DAO (Data Access Object). By the end, you’ll have a clear understanding of how to implement a robust database layer in your Jetpack Compose applications.
What is Room DAO?
Room is a persistence library part of the Android Jetpack suite that simplifies database interactions in SQLite. The DAO (Data Access Object) is a key component of Room, allowing you to define methods for database queries and operations. DAOs abstract the complexity of raw SQL and provide a clean API for your app’s database operations.
Why use Room with Jetpack Compose?
Simplified local data storage.
Integration with LiveData or Kotlin Flows for reactive UI updates.
Type safety and compile-time validation of SQL queries.
Reduced boilerplate code compared to traditional SQLite.
Setting Up Your Project
Before diving into Room DAO, ensure your project is ready for Jetpack Compose and Room:
1. Add Dependencies
Update your build.gradle
file to include the necessary dependencies:
dependencies {
// Jetpack Compose
implementation "androidx.compose.ui:ui:1.x.x"
implementation "androidx.compose.material:material:1.x.x"
implementation "androidx.compose.ui:ui-tooling-preview:1.x.x"
// Room components
implementation "androidx.room:room-runtime:2.x.x"
kapt "androidx.room:room-compiler:2.x.x"
// Optional - Kotlin Flow support
implementation "androidx.room:room-ktx:2.x.x"
// Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.x.x"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.x.x"
}
Don’t forget to enable Kotlin annotation processing (kapt):
apply plugin: 'kotlin-kapt'
2. Configure Kotlin Compiler
Ensure your build.gradle
file has Jetpack Compose enabled:
android {
composeOptions {
kotlinCompilerExtensionVersion '1.x.x'
}
}
Creating the Room Database
1. Define Your Entity
An entity represents a table in the database. Use the @Entity
annotation to create one:
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "user_table")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
val email: String
)
2. Create the DAO
Define a DAO interface with methods to interact with the database. Use Room annotations like @Insert
, @Update
, @Delete
, and @Query
:
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
@Insert
suspend fun insertUser(user: User)
@Update
suspend fun updateUser(user: User)
@Delete
suspend fun deleteUser(user: User)
@Query("SELECT * FROM user_table ORDER BY name ASC")
fun getAllUsers(): Flow<List<User>>
}
3. Build the Database Class
The database class serves as the main access point to your persisted data. Annotate it with @Database
and include your entity and DAO:
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
4. Initialize Room in Application Class
Create a singleton instance of the database for global access:
import android.content.Context
import androidx.room.Room
object DatabaseProvider {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
INSTANCE = instance
instance
}
}
}
Integrating Room DAO with Jetpack Compose
1. Using ViewModel for Data Handling
The ViewModel manages UI-related data in a lifecycle-conscious way. Inject the DAO into your ViewModel:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
class UserViewModel(private val userDao: UserDao) : ViewModel() {
val userList: StateFlow<List<User>> = userDao.getAllUsers()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
fun addUser(user: User) {
viewModelScope.launch {
userDao.insertUser(user)
}
}
fun removeUser(user: User) {
viewModelScope.launch {
userDao.deleteUser(user)
}
}
}
2. Creating the UI
Use Jetpack Compose to create a UI that interacts with the ViewModel:
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun UserScreen(userViewModel: UserViewModel = viewModel()) {
val userList by userViewModel.userList.collectAsState()
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
LazyColumn(modifier = Modifier.weight(1f)) {
items(userList) { user ->
Text(text = "${user.name} - ${user.email}")
}
}
Button(onClick = {
userViewModel.addUser(User(name = "New User", email = "new@user.com"))
}) {
Text(text = "Add User")
}
}
}
Best Practices
Avoid Direct Database Calls in Compose Functions: Use ViewModel or repository layers to manage database interactions.
Use Kotlin Flows for Reactive Updates: Flows ensure your UI stays in sync with data changes without manual intervention.
Optimize Database Queries: Use indexed columns and efficient SQL queries for better performance.
Test Your DAO: Use Room’s built-in testing utilities to validate database operations.
Conclusion
Integrating Room DAO with Jetpack Compose provides a powerful way to manage local data while maintaining a reactive, declarative UI. By following this guide, you can create efficient, scalable, and maintainable applications. Start experimenting with Room and Jetpack Compose today to unlock their full potential!