In this Jetpack Compose tutorial we are createing a simple ToDo application using Room Database and ViewModel LiveData components. In this Compose Todo application we will create Notes, edit notes, update notes and delete notes. What we will cover Let's get started Step 1: Create Compose Application in Android studio Step 2: Add required dependencies in build.gradle file Step 3: Create Room Database Singleton Room instance We are creating the singleton room database with below code Here we need to create Todo Dao and Todo Entities respectively. Todo Entity Todo Dao Create ViewModel and ViewModelFactory Now let's create ViewModel and ViewModelFactory which will handle the Notes data from/to Room Database ViewModelFactory Now we need to design our UI to add Notes and Display data on list Step 5: Run Application Conclusion: In this Jetpack Compose Tutorial we covered Create ToDo Notes application with Room Database and ViewModel, LiveData components.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.rrtutors.todoapplication"
minSdk 21
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.10'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
def room_version = "2.3.0"
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-compose:1.0.0-alpha07'
implementation 'androidx.compose.runtime:runtime-livedata:1.0.4'
}
package com.rrtutors.todoapplication
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context
@Database(entities = [ToDo::class], version = 1, exportSchema = false)
abstract class RoomSingleton : RoomDatabase() {
abstract fun todoDao():TodoDAO
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.rrtutors.todoapplication
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "todoTBL")
class ToDo( @PrimaryKey
var id:Long?,
@ColumnInfo(name = "uuid")
var fullName: String,
@ColumnInfo(name = "notes")
var notes:String) {
}
package com.rrtutors.todoapplication
import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface TodoDAO {
@Query("SELECT * FROM todoTBL ORDER BY id DESC")
fun getTodos(): LiveData<MutableList<ToDo>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(todo:ToDo)
@Update
suspend fun update(todo:ToDo)
@Delete
suspend fun delete(todo:ToDo)
@Query("DELETE FROM todoTBL")
suspend fun clear()
}
package com.rrtutors.todoapplication
import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.room.RoomDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class TodoViewModel(application:Application):ViewModel() {
private val db=RoomSingleton.getInstance(application)
internal val todoList:LiveData<MutableList<ToDo>> = db.todoDao().getTodos()
fun insert(todo: ToDo){
viewModelScope.launch(Dispatchers.IO) {
db.todoDao().insert(todo)
}
}
fun update(todo: ToDo){
viewModelScope.launch(Dispatchers.IO) {
db.todoDao().update(todo)
}
}
fun delete(todo: ToDo){
viewModelScope.launch(Dispatchers.IO) {
db.todoDao().delete(todo)
}
}
fun clear(){
viewModelScope.launch(Dispatchers.IO) {
db.todoDao().clear()
}
}
}
package com.rrtutors.todoapplication
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class ToDoViewmodelFactory(private val application: Application):ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(TodoViewModel::class.java)) {
return TodoViewModel(application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
package com.rrtutors.todoapplication
import android.app.Application
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.shape.RoundedCornerShape
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.material.icons.filled.Refresh
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.launch
import java.util.*
import kotlin.random.Random
import com.rrtutors.todoapplication.ui.theme.ToDoApplicationTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ToDoApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
GetScaffold()
}
}
}
}
}
@Composable
fun GetScaffold(){
val scaffoldState: ScaffoldState = rememberScaffoldState(
snackbarHostState = SnackbarHostState()
)
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text(text = "Compose - ToDo Application",color= Color.White)},
backgroundColor = Color(0xFFFDA433),
)
},
content = {MainContent(scaffoldState)},
backgroundColor = Color(0xFFBEEFF5),
)
}
@Composable
fun MainContent(scaffoldState: ScaffoldState){
val scope = rememberCoroutineScope()
val context = LocalContext.current
val model : TodoViewModel = viewModel(
factory = ToDoViewmodelFactory(
context.applicationContext as Application
)
)
val list:List<ToDo> = model.todoList.observeAsState(listOf()).value
var textState = remember { mutableStateOf("") }
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
//contentAlignment = Alignment.Center
){
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
shape = RoundedCornerShape(8.dp,),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color(0xFFFFFFFF),
focusedIndicatorColor = Color.Transparent, //hide the indicator
),
value =textState.value, onValueChange ={textState.value = it}
,placeholder = {
Text(text = "Enter Your Notes")
},)
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp)
){
Button(
onClick = {
model.insert(
ToDo(
null,
UUID.randomUUID().toString(),
textState.value
)
)
scope.launch{
textState.value= ""
scaffoldState.snackbarHostState.showSnackbar(
message = "Notes added",
)
}
}) {
Text(text = "Add Notes")
}
Button(onClick = {
model.clear()
scope.launch{
scaffoldState.snackbarHostState.showSnackbar(
message = "All Notes deleted",
)
}
}) {
Text(text = "Clear")
}
}
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
items(list.size) { index ->
Card(
modifier = Modifier
.padding(2.dp)
.fillMaxWidth()
.wrapContentHeight(Alignment.CenterVertically)
) {
Row(
modifier = Modifier.padding(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "${list[index].id}",
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(start = 12.dp)
)
Text(
text = " : " + list[index].fullName.take(10),
)
Text(
text = " : " + list[index].notes,
style = TextStyle(
color = if (list[index].id!! >= 33)
Color(0xFF3B7A57)
else Color(0xFFAB274F)),
modifier = Modifier.weight(2F)
)
IconButton(onClick = {
list[index].notes = textState.value
model.update(list[index])
scope.launch{
scaffoldState.snackbarHostState
.showSnackbar(
"Notes updated id" +
" : ${list[index].id}",
)
textState.value= ""
}
}) {
Icon(Icons.Filled.Edit,"",tint = Color.Magenta)
}
IconButton(onClick = {
model.delete(list[index])
scope.launch{
scaffoldState.snackbarHostState
.showSnackbar(
"Notes deleted id" +
" : ${list[index].id}",
)
textState.value= ""
}
}) {
Icon(Icons.Filled.Delete,"",tint = Color.Red)
}
}
}
}
}
}
}
}
Article Contributed By :
|
|
|
|
1584 Views |