Jetpack Compose Todo Application

Published October 28, 2021


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

  • Create Singleton Room Database
  • Create ViewModel and ViewModelFactory
  • UI to display Notes

 

Let's get started

 

Step 1: Create Compose Application in Android studio

Step 2: Add required dependencies in build.gradle file

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'
}

 

Step 3: Create Room Database

Singleton Room instance

We are creating the singleton room database with below code

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
        }
    }
}

 

Here we need to create Todo Dao and Todo Entities respectively.

 

Todo Entity

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) {

}

 

Todo Dao

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()
}

 

 

Create ViewModel and ViewModelFactory

Now let's create ViewModel and ViewModelFactory which will handle the Notes data from/to Room Database

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()
        }
    }

}

 

ViewModelFactory

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")

    }
}

 

 

Now we need to design our UI to add Notes and Display data on list

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)
                            }
                        }
                    }
                }
            }
        }
    }
}

 

Step 5: Run Application

Jetpack Compose Todo application with Room Database using ViewModel Livedata

 

 

Conclusion: In this Jetpack Compose Tutorial we covered Create ToDo Notes application with Room Database and ViewModel, LiveData components.


Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

59 Views

Subscribe For Daily Updates

Flutter Questions
Android Questions