Belajar Jetpack Compose - Data Persistence & Offline
Episode 11 of 23

Belajar Jetpack Compose - Data Persistence & Offline

Episode ini membuat aplikasi bekerja offline: integrasi Room dengan Flow, Preferences DataStore untuk preferensi, state restoration dengan rememberSaveable dan SavedStateHandle, serta pola offline-first dengan repository layer.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

Aplikasi yang baik tetap berguna tanpa koneksi. Episode 11 membekali kalian dengan penyimpanan data lokal: Room untuk database relasional, DataStore untuk preferensi, dan pola restoration agar state tidak hilang saat aplikasi di-recreate.

Di Compose, penyimpanan dan UI bertemu lewat Flow: database mengekspos aliran data, UI meng-collect-nya, dan perubahan langsung tercermin. Inilah pola data-driven UI yang juga membentuk arsitektur offline-first.

Episode 11 ini meliputi Room, DataStore, state restoration, dan repository layer.

Integrasi Room dengan Compose

Entity, DAO, dan Database

Room menyimpan data relasional di SQLite dengan type safety. Definisikan Entity, DAO, dan Database:

KotlinRoom entity dan DAO
@Entity(tableName = "tugas")
data class Tugas(
    @PrimaryKey val id: Long,
    val judul: String,
    val selesai: Boolean = false
)
 
@Dao
interface TugasDao {
    @Insert
    suspend fun tambah(tugas: Tugas)
 
    @Query("SELECT * FROM tugas ORDER BY id DESC")
    fun semuaTugas(): Flow<List<Tugas>>
}

@Dao dengan return type Flow<List<Tugas>> membuat UI selalu sinkron: setiap perubahan tabel memancarkan data baru. Anotasi Room seperti @PrimaryKey diproses oleh KSP di build Gradle.

Room dan KSP

Room memakai KSP untuk memproses anotasi. Tambahkan plugin dan dependency di build Gradle:

KotlinRoom dengan KSP
plugins {
    id("com.google.devtools.ksp")
}
 
dependencies {
    implementation("androidx.room:room-runtime:2.7.0")
    implementation("androidx.room:room-ktx:2.7.0")
    ksp("androidx.room:room-compiler:2.7.0")
}

androidx.room:room-ktx menyediakan dukungan coroutines dan Flow, dan ksp menjalankan code generation. Setelah sinkronisasi, kalian bisa mengecek kelancaran konfigurasi dengan ./gradlew :app:build.

Preferences DataStore

Menyimpan Preferensi Sederhana

Untuk key-value kecil seperti preferensi theme, pakai Preferences DataStore:

KotlinPreferences DataStore
val Context.dataStore by preferencesDataStore(name = "preferensi")
 
val darkModePref: Flow<Boolean> = context.dataStore.data
    .map { it[prefsKeyDarkMode] ?: false }

preferencesDataStore(name = "preferensi") membuat instance DataStore, dan dataStore.data adalah Flow yang bisa di-collect di Compose. Nilai yang dibaca bisa langsung menggerakkan theme dari episode 6.

State Restoration

rememberSaveable di Compose

Di episode 5 kalian memakai rememberSaveable untuk satu nilai. Untuk objek kompleks, daftarkan Saver:

KotlinSaver custom
val DaftarSaver = listSaver<DaftarTugas, String>(
    save = { it.items },
    restore = { DaftarTugas(it) }
)
 
var daftar by rememberSaveable(saver = DaftarSaver) {
    mutableStateOf(DaftarTugas(emptyList()))
}

Saver mengubah objek menjadi nilai yang bisa disimpan di Bundle dan mengembalikannya lagi. listSaver menyimpan isi list dan restore membangun ulang objek saat Activity di-recreate.

SavedStateHandle di ViewModel

Untuk state ViewModel, SavedStateHandle menyimpan nilai melewati process death:

KotlinSavedStateHandle
class FormViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    var nama: String
        get() = savedStateHandle["nama"] ?: ""
        set(value) { savedStateHandle["nama"] = value }
}

savedStateHandle["nama"] otomatis dipulihkan setelah recreation. Kombinasi rememberSaveable di UI dan SavedStateHandle di ViewModel membangun restoration yang lengkap.

Offline-first dengan Repository Layer

Repository Sebagai Sumber Tunggal

Pola offline-first: UI membaca dari repository, repository memutuskan data lokal atau jaringan:

KotlinRepository pattern
class ArtikelRepository(
    private val lokal: ArtikelDao,
    private val remote: ArtikelApi
) {
    fun semuaArtikel(): Flow<List<Artikel>> =
        lokal.semuaArtikel().flatMapLatest { lokalList ->
            if (lokalList.isEmpty()) {
                refreshDariJaringan()
            }
            lokal.semuaArtikel()
        }
 
    private suspend fun refreshDariJaringan() {
        val remoteList = remote.fetch()
        lokal.upsert(remoteList)
    }
}

Repository menyembunyikan sumber data dari UI. flatMapLatest memicu refresh saat cache kosong, dan hasil jaringan disimpan ke Room — sekali tersimpan, daftar tampil offline.

UI yang Digerakkan Data

ViewModel mengekspos Flow repository, dan UI meng-collect dengan pola dari episode 10:

KotlinData-driven UI
val artikel by viewModel.artikelFlow.collectAsStateWithLifecycle()
LazyColumn {
    items(artikel, key = { it.id }) { item ->
        KartuArtikel(item)
    }
}

collectAsStateWithLifecycle menghubungkan Room, repository, dan UI. Karena seluruh lapisan data mengalir sebagai Flow, offline dan online sama-sama tampil mulus.

Penutup

Episode 11 membangun persistensi: Room dengan Flow dan KSP untuk data relasional, Preferences DataStore untuk preferensi, state restoration dengan Saver dan SavedStateHandle, serta pola offline-first dengan repository yang memutuskan sumber data dan membuat UI digerakkan data.

Inti yang harus dibawa pulang:

  • DAO dengan return Flow membuat UI selalu sinkron dengan database.
  • Room memakai KSP untuk memproses anotasi.
  • DataStore menangani preferensi key-value kecil.
  • rememberSaveable dengan Saver menyimpan objek kompleks.
  • SavedStateHandle memulihkan state ViewModel melewati process death.
  • Repository memisahkan sumber data dan mendukung offline-first.

Di episode 12 selanjutnya kita akan membahas animations dan motion — animate*AsState untuk transisi halus, AnimatedVisibility dan updateTransition, animasi berbasis fisika, serta polish UI dengan gesture.

Belajar Jetpack Compose - Data Persistence & Offline | Belajar Jetpack Compose