Introduction
This code demonstrates an Android application built with Jetpack Compose and Room Persistence Library. The application manages employee data, allowing users to add, update, and clear employee records. The code utilizes several key components:
- Jetpack Compose: A modern UI framework for building declarative and reactive user interfaces.
- Room: A persistence library that provides an abstraction layer for SQLite databases.
- ViewModel: A lifecycle-aware class that manages data for a UI component.
Breakdown of the Code
The code is divided into several files:
- MainActivity.kt: The main activity of the application. It sets up the content of the screen using
setContent
and utilizesEmployeeViewModel
to manage employee data. - RoomSingleton.kt: Defines a singleton class for accessing the Room database instance.
- RoomEntity.kt: Defines the data structure for an employee (
Employee
) with fields for ID and full name. - RoomDao.kt: Defines the Data Access Object (DAO) interface for interacting with the employee table in the database.
- EmployeeViewModel.kt: The ViewModel class responsible for managing employee data. It interacts with the DAO to perform database operations and exposes functions to add, update, clear, and load employees.
- build.gradle: Defines the necessary dependencies for the project, including Room and ViewModel libraries.
Summary
The code showcases a practical example of using Jetpack Compose with Room for data persistence. The ViewModel acts as a central point for data management, ensuring data consistency and providing a reactive way to update the UI based on changes in the database. The application demonstrates basic CRUD (Create, Read, Update, Delete) operations for employee data.
MainActivity.kt
package com.example.composeroomexample
import android.os.Bundle
import android.util.Log
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.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.text.font.FontWeight
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()
Log.d("xapp", "Recomposition")
Column(
modifier = Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp)
){
Button(onClick = {
val employees = mutableListOf<Employee>()
for (i in 1..10){
employees.add(
Employee(
null,UUID.randomUUID().toString()
)
)
}
model.insertAll(employees)
}) { Text(text = "Add 10 Employees") }
Button(onClick = {
model.clear()
}) { Text(text = "Clear") }
}
Text(
text ="${list.value.size} Employees",
fontWeight = FontWeight.Bold
)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
itemsIndexed(list.value) { index,item ->
Card(
modifier = Modifier.fillMaxWidth(),
backgroundColor = Color(0xFFF1E9D2)
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Box(
Modifier.size(48.dp).clip(CircleShape)
.background(Color(0xFFF6ADC6)),
contentAlignment = Alignment.Center
) {
Text(
text = "${item.id}",
style = MaterialTheme.typography.h6
)
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = item.fullName.take(12),
style = MaterialTheme.typography.h6
)
Spacer(modifier = Modifier.weight(1F))
IconButton(onClick = {
val updatedItem = Employee(
item.id,UUID.randomUUID().toString()
)
model.update(updatedItem)
}) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = ""
)
}
}
}
}
}
}
}
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.*
@Dao
interface EmployeeDao{
@Query("SELECT * FROM employeeTbl ORDER BY id DESC")
fun allEmployees():List<Employee>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(employee:Employee)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(list:List<Employee>)
@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(employee:Employee)
@Query("DELETE FROM employeeTbl")
suspend fun clear()
}
EmployeeViewModel.kt
package com.example.composeroomexample
import android.app.Application
import android.util.Log
import androidx.lifecycle.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class EmployeeViewModel(application:Application)
: AndroidViewModel(application){
private val db:RoomSingleton = RoomSingleton
.getInstance(application)
private val _Employees = MutableStateFlow(emptyList<Employee>())
var employees: StateFlow<List<Employee>> = _Employees
init {
loadEmployees()
}
fun loadEmployees() = effect {
_Employees.value = db.employeeDao().allEmployees()
Log.d("xapp", "Loading employees")
}
fun insert(employee: Employee){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().insert(employee)
loadEmployees()
}
}
fun insertAll(list:List<Employee>){
viewModelScope.launch (Dispatchers.IO){
db.employeeDao().insertAll(list)
loadEmployees()
}
}
fun update(employee: Employee){
viewModelScope.launch(Dispatchers.IO){
db.employeeDao().update(employee)
loadEmployees()
}
}
fun clear(){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().clear()
loadEmployees()
}
}
private fun effect(block: suspend () -> Unit) {
viewModelScope.launch(Dispatchers.IO) { block() }
}
}
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 build json array
- jetpack compose - Flow current time
- jetpack compose - How to flow a list
- jetpack compose - How to use ViewModel state
- jetpack compose - Flow using ViewModel
- jetpack compose - Search Room data using ViewModel
- jetpack compose - ViewModel Room add insert 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