Jetpack compose: Room Add, Remove, Update

Jetpack Compose - Room Add, Remove, Update

This code demonstrates a basic Android application built with Jetpack Compose that utilizes Room for data persistence. The app manages a list of "Students" with functionalities to add, remove, and update them.

Key Components:

  • Room Database: The RoomSingleton class defines a Room database with a single entity - Student. It provides methods to access the DAO (Data Access Object) for interacting with student data.
  • Student Entity: The Student data class represents a student record with properties like ID (primary key), name (full name), and result (integer).
  • StudentDao: This interface defines methods for CRUD (Create, Read, Update, Delete) operations on student data within the database.
  • StudentViewModel: This ViewModel class acts as the single source of truth for student data. It holds a reference to the Room database instance and exposes functions to interact with students (insert, update, delete, clear all).
  • MainContent Composable: This function represents the UI of the app. It displays a list of students using LazyColumn and provides buttons for adding new students, clearing all students, editing individual students, and deleting them.

Summary

This example showcases how to integrate Jetpack Compose with Room to manage data in an Android application. It demonstrates live data updates, user interaction with the UI to manipulate data, and background operations using coroutines for database access. This provides a starting point for building more complex applications with persistent data storage and a reactive UI.


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.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
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.*
import kotlin.random.Random

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent { MainContent() }
    }
}


@Composable
fun MainContent(){
    val scope = rememberCoroutineScope()
    val model = viewModel<StudentViewModel>()
    val list:List<Student> = model.students.observeAsState(listOf()).value

    Column(
        modifier = Modifier.padding(12.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        Row(
            horizontalArrangement = Arrangement.spacedBy(12.dp)
        ){
            // add new student
            Button(onClick = {
                model.insert(
                    Student(
                        null,UUID.randomUUID().toString(),
                        Random.nextInt(10,90)
                    )
                )
            }) { 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) { index,item ->
                Card(
                    modifier = Modifier.fillMaxWidth(),
                    backgroundColor = Color(0xFFA1CAF1)
                ) {
                    Row(
                        modifier = Modifier.padding(12.dp),
                        verticalAlignment = Alignment.CenterVertically
                    ) {

                        Box(
                            Modifier.size(48.dp).clip(CircleShape)
                                .background(Color(0xFFF0FFFF)),
                            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.caption
                        )

                        Spacer(modifier = Modifier.width(12.dp))
                        Text(
                            text = " ${item.result}",
                            style = MaterialTheme.typography.h5
                        )
                        Spacer(modifier = Modifier.weight(1F))

                        // edit button
                        IconButton(onClick = {
                            val updatedItem = Student(
                                item.id,item.fullName,Random.nextInt(0,100)
                            )
                            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 = [Student::class], version = 1, exportSchema = false)
abstract class RoomSingleton : RoomDatabase() {
    abstract fun studentDao():StudentDao

    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 = "studentTbl")
data class Student(
    @PrimaryKey
    var id:Long?,

    @ColumnInfo(name = "uuid")
    var fullName: String,

    @ColumnInfo(name = "result")
    var result:Int
)
RoomDao.kt

package com.example.composeroomexample

import androidx.lifecycle.LiveData
import androidx.room.*

@Dao
interface StudentDao{
    @Query("SELECT * FROM studentTbl ORDER BY id DESC")
    fun getStudents():LiveData<MutableList<Student>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(student:Student)

    @Update
    suspend fun update(student:Student)

    @Delete
    suspend fun delete(student:Student)

    @Query("DELETE FROM studentTbl")
    suspend fun clear()
}
StudentViewModel.kt

package com.example.composeroomexample

import android.app.Application
import androidx.lifecycle.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


class StudentViewModel(application:Application)
    : AndroidViewModel(application){
    
	private val db:RoomSingleton = RoomSingleton
        .getInstance(application)

    internal val students : LiveData<MutableList<Student>> =
        db.studentDao().getStudents()

    fun insert(student: Student){
        viewModelScope.launch(Dispatchers.IO) {
            db.studentDao().insert(student)
        }
    }

    fun update(student: Student){
        viewModelScope.launch(Dispatchers.IO) {
            db.studentDao().update(student)
        }
    }

    fun delete(student: Student){
        viewModelScope.launch(Dispatchers.IO) {
            db.studentDao().delete(student)
        }
    }

    fun clear(){
        viewModelScope.launch(Dispatchers.IO) {
            db.studentDao().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'
    implementation 'androidx.compose.runtime:runtime-livedata:1.2.0-alpha07'
}
More android jetpack compose tutorials