Jetpack compose flow: Room add remove update

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 the employees 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'
}
More android jetpack compose tutorials