Introduction
This code demonstrates a Jetpack Compose application that utilizes Room database to manage employee data. The application allows adding new employees, deleting existing ones, and clearing all data. It leverages ViewModel to manage the data state and provide a single source of truth for the UI.
Breakdown
The code is organized into several classes:
- MainActivity.kt: This is the main activity that sets the content of the app using a Composable function called
MainContent
. - MainContent.kt: This composable function builds the UI for the app. It retrieves data from the ViewModel, displays the number of employees, and presents a list of employees. It also provides buttons for adding new employees and clearing all data.
- RoomSingleton.kt: This class defines a Room database named "roomdb" with a single entity -
Employee
. It provides a singleton instance using thegetInstance
method. - Employee.kt: This data class represents an employee with an ID (Long) and a full name (String).
- EmployeeDao.kt: This interface defines the data access methods for the
Employee
table in the Room database. It includes methods for fetching all employees, inserting a single or multiple employees, updating an employee, deleting an employee, and clearing all data. - EmployeeViewModel.kt: This ViewModel class manages the employee data. It retrieves data from the database on initialization and provides functions for adding, updating, deleting, and clearing employees. It also exposes a
StateFlow
of employees to be observed by the UI. - build.gradle: This file specifies the dependencies required for the project, including Room and ViewModel libraries.
Summary
This code showcases a well-structured Jetpack Compose application with Room database integration. The ViewModel effectively manages the data state and interacts with the database through the defined Dao methods. The UI is built using Compose functions and reacts to changes in the data flow. Overall, the code demonstrates good practices for data management and UI updates in a Jetpack Compose application.
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.Delete
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(0xFFC0E8D5)
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Box(
Modifier.size(48.dp).clip(CircleShape)
.background(Color(0xFF8DB600)),
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 = {
model.delete(item)
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "",
tint = Color(0xFFD3212D)
)
}
}
}
}
}
}
}
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
}
}
}
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
)
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)
@Delete
suspend fun delete(employee: Employee)
@Query("DELETE FROM employeeTbl")
suspend fun clear()
}
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()
}
private 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 delete(employee: Employee){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().delete(employee)
loadEmployees()
}
}
fun clear(){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().clear()
loadEmployees()
}
}
private fun effect(block: suspend () -> Unit) {
viewModelScope.launch(Dispatchers.IO) { block() }
}
}
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 - How to use AndroidView
- jetpack compose - Get screen width height in dp
- jetpack compose - Get screen layout direction
- jetpack compose - Get text from url
- jetpack compose ktor - How to pass parameters
- jetpack compose - Flow current time
- jetpack compose - How to flow a list
- jetpack compose - Search Room data using ViewModel
- jetpack compose - ViewModel Room add insert data
- jetpack compose - ViewModel Room edit update data
- compose glance - How to create app widget
- jetpack compose - Icon from vector resource
- jetpack compose - IconButton from vector resource