Pertemuan 13

Latihan Registrasi Siswa dengan CRUD Room Database

Nama  : Triana Velia Hutabalian
NRP   : 5025231190
Kelas  : PPB (B)

Pada latihan ini, saya membuat aplikasi Registrasi Siswa menggunakan Kotlin dan Jetpack Compose di Android Studio. Aplikasi ini merupakan sistem manajemen data siswa dengan fitur CRUD (Create, Read, Update, Delete) yang menyimpan data secara persisten menggunakan Room Database. Konsep utama yang diterapkan meliputi pembuatan Entity dan DAO untuk operasi database, pengelolaan state reaktif dengan StateFlow dan collectAsState(), pemisahan logika bisnis menggunakan ViewModel, serta rendering daftar siswa secara efisien dengan LazyColumn.

1
Membuat project baru di Android Studio dengan template Empty Activity (Compose), nama project StudentApp, package com.example.studentapp, bahasa Kotlin, dan Minimum SDK API 24.
2
Menambahkan versi dependency di gradle/libs.versions.toml yaitu room = "2.7.1" dan ksp = "2.1.0-1.0.29", lalu mendaftarkan library room-runtime, room-ktx, room-compiler, lifecycle-viewmodel-compose, dan plugin ksp pada bagian [libraries] dan [plugins].
3
Mengaktifkan plugin ksp di app/build.gradle.kts pada blok plugins {}, lalu menambahkan dependency Room dan lifecycle-viewmodel-compose pada blok dependencies {}. Menambahkan android.disallowKotlinSourceSets=false di gradle.properties agar KSP kompatibel dengan AGP 9.x.
4
Membuat package data/ dan membuat data class Siswa dengan anotasi @Entity(tableName = "siswa"), properti id sebagai @PrimaryKey(autoGenerate = true) dengan default value 0, serta properti nama dan email bertipe String.
5
Membuat SiswaDao dengan anotasi @Dao berisi empat fungsi: getAllSiswa() mengembalikan Flow<List<Siswa>> menggunakan @Query, serta fungsi suspend untuk insert(), update(), dan delete() menggunakan anotasi Room masing-masing.
6
Membuat AppDatabase dengan anotasi @Database yang mengextend RoomDatabase, mengimplementasikan pola Singleton menggunakan companion object, @Volatile, dan blok synchronized untuk memastikan hanya satu instance database yang dibuat.
7
Membuat package viewmodel/ dan membuat StudentViewModel yang mengextend AndroidViewModel. ViewModel mengakses DAO dari database, mengkonversi Flow menjadi StateFlow menggunakan stateIn(), dan menyediakan fungsi insert(), update(), delete() yang memanggil DAO dalam viewModelScope.launch.
8
Membuat package ui/ dan membuat composable FormInput berisi dua OutlinedTextField untuk input nama dan email, serta satu Button yang menampilkan teks "+ Tambah Siswa" saat mode tambah atau "Simpan Perubahan" saat mode edit, dikontrol oleh parameter isEditing: Boolean.
9
Membuat composable StudentItem berupa Card dengan layout Row yang menampilkan avatar bulat berisi dua inisial nama, kolom nama dan email, serta dua IconButton untuk aksi edit (ikon pensil) dan hapus (ikon tempat sampah) dengan warna yang sesuai dari MaterialTheme.colorScheme.
10
Membuat MainScreen yang mengambil StudentViewModel via viewModel(), mengelola state form (nama, email, editingSiswa) dengan remember, dan merender LazyColumn yang mengonsumsi allSiswa via collectAsState(). Memperbarui MainActivity untuk memanggil MainScreen di dalam Scaffold.
Room Database

ORM resmi Android untuk penyimpanan data lokal — menyediakan abstraksi di atas SQLite dengan type-safety dan integrasi Kotlin Coroutines melalui Flow.

@Entity & @Dao

Entity mendefinisikan struktur tabel database, sedangkan DAO menyediakan antarmuka fungsi untuk operasi insert, query, update, dan delete secara type-safe.

ViewModel & StateFlow

AndroidViewModel memisahkan logika bisnis dari UI. StateFlow memungkinkan UI bereaksi otomatis terhadap perubahan data database secara reaktif.

Jetpack Compose

Toolkit UI deklaratif modern Android — UI dideskripsikan sebagai fungsi Kotlin dan di-rekomposisi secara otomatis ketika state berubah, tanpa notifyDataSetChanged().

LazyColumn

Equivalen RecyclerView di Compose — hanya merender item yang tampil di layar, efisien untuk daftar panjang. Menggunakan key untuk rekomposisi yang optimal.

Singleton Pattern

Pola desain yang memastikan hanya satu instance AppDatabase yang ada selama siklus hidup aplikasi, diimplementasikan dengan @Volatile dan synchronized.

data/Siswa.kt
package com.example.studentapp.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "siswa")
data class Siswa(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val nama: String,
    val email: String
)
data/SiswaDao.kt
package com.example.studentapp.data

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface SiswaDao {

    @Query("SELECT * FROM siswa ORDER BY id ASC")
    fun getAllSiswa(): Flow<List<Siswa>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(siswa: Siswa)

    @Update
    suspend fun update(siswa: Siswa)

    @Delete
    suspend fun delete(siswa: Siswa)
}
data/AppDatabase.kt
package com.example.studentapp.data

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [Siswa::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {

    abstract fun siswaDao(): SiswaDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "siswa_database"
                ).build().also { INSTANCE = it }
            }
        }
    }
}
viewmodel/StudentViewModel.kt
package com.example.studentapp.viewmodel

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.example.studentapp.data.AppDatabase
import com.example.studentapp.data.Siswa
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

class StudentViewModel(application: Application) : AndroidViewModel(application) {

    private val dao = AppDatabase.getDatabase(application).siswaDao()

    val allSiswa = dao.getAllSiswa()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

    fun insert(siswa: Siswa) = viewModelScope.launch { dao.insert(siswa) }
    fun update(siswa: Siswa) = viewModelScope.launch { dao.update(siswa) }
    fun delete(siswa: Siswa) = viewModelScope.launch { dao.delete(siswa) }
}
ui/MainScreen.kt
package com.example.studentapp.ui

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.studentapp.data.Siswa
import com.example.studentapp.viewmodel.StudentViewModel

@Composable
fun MainScreen(
    modifier: Modifier = Modifier,
    viewModel: StudentViewModel = viewModel()
) {
    val siswaList by viewModel.allSiswa.collectAsState()
    var nama by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    var editingSiswa by remember { mutableStateOf<Siswa?>(null) }

    Column(modifier = modifier.fillMaxSize().padding(16.dp)) {
        Text("Registrasi Siswa", fontSize = 22.sp, fontWeight = FontWeight.Bold)
        Text("Room Database", fontSize = 13.sp, color = MaterialTheme.colorScheme.primary)
        Spacer(Modifier.height(16.dp))

        FormInput(
            nama = nama, email = email,
            isEditing = editingSiswa != null,
            onNamaChange = { nama = it },
            onEmailChange = { email = it },
            onSubmit = {
                if (nama.isNotBlank() && email.isNotBlank()) {
                    if (editingSiswa != null)
                        viewModel.update(editingSiswa!!.copy(nama = nama, email = email))
                    else
                        viewModel.insert(Siswa(nama = nama, email = email))
                    nama = ""; email = ""; editingSiswa = null
                }
            }
        )
        Spacer(Modifier.height(20.dp))
        Text("Daftar Siswa", fontSize = 16.sp, fontWeight = FontWeight.SemiBold)
        Spacer(Modifier.height(8.dp))

        LazyColumn {
            items(siswaList, key = { it.id }) { siswa ->
                StudentItem(
                    siswa = siswa,
                    onEdit = { editingSiswa = it; nama = it.nama; email = it.email },
                    onDelete = { viewModel.delete(it) }
                )
            }
        }
    }
}
 Hasil Output
StudentApp — Android Studio · Pixel 6 API 34 · CRUD Registrasi Siswa
Screenshot StudentApp - Registrasi Siswa dengan Room Database

Komentar

Postingan populer dari blog ini

Pertemuan 4&5 PPB (B) - 25/03/2026

Pertemuan 2 PPB (B) - 04/03/2026

Pertemuan 7 - 08/04/2026