This code demonstrates a Jetpack Compose application that manages a list of employees using a Room database. It allows adding, removing, and updating employees.
The application leverages Kotlin Flows for asynchronous data updates and recomposition in the UI.
Code Breakdown
The code is divided into several parts:
- MainActivity: This is the main activity that sets up the UI using Jetpack Compose. It retrieves the
EmployeeViewModel
and subscribes to theemployees
flow to display the list of employees. - MainContent: This composable function builds the UI for the application. It includes buttons for adding new employees and clearing the list, a LazyColumn to display the list of employees, and individual cards for each employee with edit and delete buttons.
- RoomSingleton: This is a singleton class that provides access to the Room database instance.
- RoomEntity: This class defines the data structure of an employee with properties for id and fullName.
- RoomDao: This interface defines the methods for interacting with the employee table in the database, including getting all employees, inserting, updating, deleting, and clearing the table.
- EmployeeViewModel: This ViewModel class manages the data access logic. It retrieves the Room database instance, exposes a flow of employees retrieved from the database, and provides functions for adding, updating, deleting, and clearing employees.
Summary
This code showcases a practical implementation of Jetpack Compose with Room database for managing data and updating the UI using Kotlin Flows. It demonstrates CRUD (Create, Read, Update, Delete) operations on the employee data and provides a user-friendly interface for interacting with it.
MainActivity.kt
package com.example.composeroomexample
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import java.util.*
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { MainContent() }
}
}
@Composable
fun MainContent(){
val model = viewModel<EmployeeViewModel>()
val list = model.employees.collectAsState(initial = emptyList())
Column(
modifier = Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp)
){
// add new student
Button(onClick = {
model.insert(
Employee(
null,UUID.randomUUID().toString()
)
)
}) { Text(text = "Add Student") }
// delete all students
Button(onClick = { model.clear() }) {
Text(text = "Clear")
}
}
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(vertical = 12.dp)
) {
itemsIndexed(list.value) { index,item ->
Card(
modifier = Modifier.fillMaxWidth(),
backgroundColor = Color(0xFFDCDCDC)
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
Modifier.size(48.dp).clip(CircleShape)
.background(Color(0xFFAA98A9)),
contentAlignment = Alignment.Center
) {
Text(
text = "${item.id}",
style = MaterialTheme.typography.h6
)
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = item.fullName.take(10),
style = MaterialTheme.typography.h6
)
Spacer(modifier = Modifier.weight(1F))
// edit button
IconButton(onClick = {
val updatedItem = Employee(
item.id,UUID.randomUUID().toString()
)
model.update(updatedItem)
}) { Icon(Icons.Filled.Edit,"")}
// delete button
IconButton(onClick = { model.delete(item)}) {
Icon(Icons.Filled.Delete,"")
}
}
}
}
}
}
}
RoomSingleton.kt
package com.example.composeroomexample
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context
@Database(entities = [Employee::class], version = 1, exportSchema = false)
abstract class RoomSingleton : RoomDatabase() {
abstract fun employeeDao():EmployeeDao
companion object {
private var INSTANCE: RoomSingleton? = null
fun getInstance(context: Context): RoomSingleton {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context,
RoomSingleton::class.java,
"roomdb")
.build()
}
return INSTANCE as RoomSingleton
}
}
}
RoomEntity.kt
package com.example.composeroomexample
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "employeeTbl")
data class Employee(
@PrimaryKey
var id:Long?,
@ColumnInfo(name = "uuid")
var fullName: String
)
RoomDao.kt
package com.example.composeroomexample
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface EmployeeDao{
@Query("SELECT * FROm employeeTbl ORDER BY id DESC")
fun getEmployees():Flow<List<Employee>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(employee:Employee)
@Update
suspend fun update(employee:Employee)
@Delete
suspend fun delete(employee:Employee)
@Query("DELETE FROM employeeTbl")
suspend fun clear()
}
EmployeeViewModel.kt
package com.example.composeroomexample
import android.app.Application
import androidx.lifecycle.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class EmployeeViewModel(application:Application)
: AndroidViewModel(application){
private val db:RoomSingleton = RoomSingleton
.getInstance(application)
internal val employees : Flow<List<Employee>> =
db.employeeDao().getEmployees()
fun insert(employee: Employee){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().insert(employee)
}
}
fun update(employee: Employee){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().update(employee)
}
}
fun delete(employee: Employee){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().delete(employee)
}
}
fun clear(){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().clear()
}
}
}
build.gradle [dependencies]
apply plugin: 'kotlin-kapt'
dependencies {
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-alpha06"
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha06'
}
- jetpack compose - Kotlinx serialization ignore unknown keys
- jetpack compose - Kotlinx serialization alternative json names
- jetpack compose - Kotlinx serialization decode from string
- jetpack compose - Kotlinx serialization encode defaults
- jetpack compose - How to use ViewModel state
- jetpack compose - Flow using ViewModel
- jetpack compose - Count up flow by ViewModel
- jetpack compose flow - Room implement search
- jetpack compose - Search Room data using ViewModel
- jetpack compose - ViewModel Room add insert data
- jetpack compose - ViewModel Room edit update data
- jetpack compose - ViewModel Room delete clear data
- compose glance - How to create app widget
- jetpack compose - Icon from vector resource
- jetpack compose - IconButton from vector resource