This episode dissects the heart of Compose: state and recomposition. You'll master remember, mutableStateOf, state hoisting, ViewModel integration with StateFlow, and snapshotFlow and derivedStateOf for managing data flow in the UI.

Everything you've learned so far boils down to one concept: state. UI in Compose is a function of state — so the way you store, change, and flow state determines the quality of your application. Episode 5 dissects this concept down to its roots.
In episode 2 you saw remember and mutableStateOf in passing. Now it's time to understand them thoroughly, plus two APIs that often cause confusion: snapshotFlow and derivedStateOf.
Episode 5 covers the basics of state, state hoisting, ViewModel integration, and snapshot and derived state.
mutableStateOf wraps a value into an observable, and remember keeps the value surviving across recompositions. Without remember, the value would be reset on every composition:
@Composable
fun FormKomentar() {
var nama by remember { mutableStateOf("") }
OutlinedTextField(
value = nama,
onValueChange = { nama = it },
label = { Text("Nama") }
)
}var nama by remember { mutableStateOf("") } uses the by delegate so reads and writes are automatically connected to the snapshot. Every time nama changes, the composables that read this variable are recomposed.
remember is lost when the Activity is recreated — for example on screen rotation. For state that must survive, use rememberSaveable, which saves the value through a Bundle:
@Composable
fun Pencarian() {
var query by rememberSaveable { mutableStateOf("") }
OutlinedTextField(
value = query,
onValueChange = { query = it },
placeholder = { Text("Cari...") }
)
}rememberSaveable { mutableStateOf("") } keeps the value across configuration changes. Episode 11 will cover state restoration in more depth.
State hoisting is the practice of moving state to a higher composable so it becomes stateless and reusable. The common pattern: state goes up, events come down.
@Composable
fun LoginScreen() {
var email by remember { mutableStateOf("") }
LoginForm(email = email, onEmailChange = { email = it })
}
@Composable
fun LoginForm(email: String, onEmailChange: (String) -> Unit) {
OutlinedTextField(
value = email,
onValueChange = onEmailChange,
label = { Text("Email") }
)
}LoginForm receives a value and a callback and doesn't store state itself. As a result, LoginForm can be tested, previewed, and reused in different contexts. A good rule of thumb: stateless composables are easier to maintain.
A ViewModel stores state that survives configuration changes and separates the UI from business logic. The modern pattern: the ViewModel exposes a StateFlow, and the UI collects it:
class ProfilViewModel : ViewModel() {
private val _nama = MutableStateFlow("Anonim")
val nama: StateFlow<String> = _nama.asStateFlow()
fun perbaruiNama(namaBaru: String) {
_nama.value = namaBaru
}
}
@Composable
fun ProfilScreen(viewModel: ProfilViewModel) {
val nama by viewModel.nama.collectAsState()
Text(nama)
}collectAsState() turns a StateFlow into Compose state: every new value triggers recomposition. Combining ViewModel, StateFlow, and Compose is the foundation of the MVVM architecture that will be discussed in episode 20. Its state logic can be tested without an emulator — run ./gradlew :app:testDebugUnitTest to run unit tests on the ViewModel.
derivedStateOf computes a value derived from other state and only triggers recomposition when the derived result actually changes:
val daftarTugas by remember { mutableStateOf<List<Tugas>>(emptyList()) }
val selesaiCount by remember {
derivedStateOf { daftarTugas.count { it.selesai } }
}derivedStateOf is useful when the transformation is expensive and the result changes rarely — a performance pattern that will be deepened in episode 15. Another example: showing a scroll-to-top status only after a certain position.
snapshotFlow is the reverse direction: it turns Compose snapshot state into a Kotlin data stream that can be collected and transformed with Flow operators:
snapshotFlow { sliderValue }.distinctUntilChanged()
.debounce(300)
.collect { nilai ->
onNilaiDebounce(nilai)
}snapshotFlow { sliderValue } captures state changes, distinctUntilChanged drops duplicates, and debounce(300) delays processing. Using it with side effects (LaunchedEffect) will be discussed in episode 10.
Episode 5 unlocked the heart of Compose: state and recomposition. You mastered remember and mutableStateOf, rememberSaveable for configuration changes, state hoisting with the state-up/events-down pattern, ViewModel integration through StateFlow and collectAsState, and derivedStateOf and snapshotFlow for managing derived values and data flows.
Key takeaways:
remember keeps state across recompositions; rememberSaveable survives recreation.collectAsState is the standard MVVM pattern.derivedStateOf computes derivations that change rarely.snapshotFlow turns Compose state into a Kotlin Flow.In episode 6 we will discuss theming and Material Design — MaterialTheme, typography, colors, and shapes, light and dark theme support, dynamic color on Android 12, and how to build your own design system.