Pertemuan 13
Latihan Registrasi Siswa dengan CRUD Room Database
Pemrograman Perangkat Bergerak B · 2026
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.
com.example.studentapp, bahasa Kotlin, dan Minimum SDK API 24.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].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.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.@Dao berisi empat fungsi: getAllSiswa() mengembalikan Flow<List<Siswa>> menggunakan @Query, serta fungsi suspend untuk insert(), update(), dan delete() menggunakan anotasi Room masing-masing.@Database yang mengextend RoomDatabase, mengimplementasikan pola Singleton menggunakan companion object, @Volatile, dan blok synchronized untuk memastikan hanya satu instance database yang dibuat.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.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.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.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.ORM resmi Android untuk penyimpanan data lokal — menyediakan abstraksi di atas SQLite dengan type-safety dan integrasi Kotlin Coroutines melalui Flow.
Entity mendefinisikan struktur tabel database, sedangkan DAO menyediakan antarmuka fungsi untuk operasi insert, query, update, dan delete secara type-safe.
AndroidViewModel memisahkan logika bisnis dari UI. StateFlow memungkinkan UI bereaksi otomatis terhadap perubahan data database secara reaktif.
Toolkit UI deklaratif modern Android — UI dideskripsikan sebagai fungsi Kotlin dan di-rekomposisi secara otomatis ketika state berubah, tanpa notifyDataSetChanged().
Equivalen RecyclerView di Compose — hanya merender item yang tampil di layar, efisien untuk daftar panjang. Menggunakan key untuk rekomposisi yang optimal.
Pola desain yang memastikan hanya satu instance AppDatabase yang ada selama siklus hidup aplikasi, diimplementasikan dengan @Volatile dan synchronized.
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 )
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) }
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 } } } } }
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) } }
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) } ) } } } }
Komentar
Posting Komentar