Introduction
This code demonstrates a Jetpack Compose application that implements search functionality using Room database. The application manages a list of employees and allows users to:
- Search for employees based on a name prefix.
- Add 100 employees at once.
- Clear all employees from the list.
The code utilizes several key components:
- Jetpack Compose for building the user interface.
- Room for persisting employee data in a local database.
- ViewModel to manage the application state and data access logic.
- Flow for handling asynchronous data updates.
Breakdown
The code is divided into several Kotlin files:
- MainActivity.kt: This is the main activity class that sets up the content of the app using Jetpack Compose. It defines the
MainContent
composable which displays the UI elements. - RoomSingleton.kt: This class defines a singleton instance of the Room database named
RoomSingleton
. It provides access to theEmployeeDao
for interacting with the employee data. - RoomEntity.kt: This class defines the data structure for an
Employee
object with fields for ID and full name. - RoomDao.kt: This interface defines the Data Access Object (DAO) methods for interacting with the employee table in the database. It includes methods for searching, inserting, and clearing employee data.
- EmployeeViewModel.kt: This ViewModel class holds the application logic for managing employee data. It provides functions to search for employees, insert new employees, and clear the entire list.
- build.gradle: This file specifies the Gradle dependencies needed for the project, including Room and lifecycle libraries.
MainContent Composable
The MainContent
composable is the heart of the UI. It utilizes several functionalities:
- It retrieves a reference to the
EmployeeViewModel
usingviewModel
. - It maintains a local state variable
textState
to store the search query entered by the user. - It collects the flow of searched employees from the ViewModel using
searchEmp
function and stores the result inlist
state variable. - The UI displays the number of employees, a search bar for filtering, and a list of employees retrieved from the database.
Summary
This code showcases the integration of Jetpack Compose, Room, and ViewModel to build a search functionality in an Android application. It demonstrates how to manage UI state, handle asynchronous data updates with Flow, and interact with a local database for data persistence.
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.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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 textState = remember{ mutableStateOf("")}
val list = model.searchEmp(textState.value)
.collectAsState(initial = emptyList())
Column(
modifier = Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp)
){
// add new student
Button(onClick = {
for (i in 1..100){
model.insert(
Employee(
null,UUID.randomUUID().toString()
)
)
}
}) { Text(text = "Add 100 Employees") }
// delete all students
Button(onClick = { model.clear() }) {
Text(text = "Clear")
}
}
Text(
text = if (textState.value.trim().isEmpty()){"Employees"}
else {"Employees start with (${textState.value.trim()})"} +
" ${list.value.size}",
fontWeight = FontWeight.Bold
)
Row() {
TextField(
value = textState.value,
onValueChange = {textState.value = it},
modifier = Modifier.fillMaxWidth(),
placeholder = { Text(text = "Search employee")}
)
}
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(vertical = 12.dp)
) {
itemsIndexed(list.value) { index,item ->
Card(
modifier = Modifier.fillMaxWidth(),
backgroundColor = Color(0xFFDCE2C9)
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
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
)
}
}
}
}
}
}
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 WHERE uuid" +
" LIKE:startsWith || '%' ORDER BY id DESC")
fun searchEmployee(startsWith:String):Flow<List<Employee>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(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)
fun searchEmp(startsWith:String):Flow<List<Employee>>{
return db.employeeDao().searchEmployee(startsWith)
}
fun insert(employee: Employee){
viewModelScope.launch(Dispatchers.IO) {
db.employeeDao().insert(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 build json array
- jetpack compose - Kotlinx serialization build json object
- 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 - Count up flow by ViewModel
- jetpack compose flow - Room add remove update
- 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