Android Intro
Kotlin is the official language for Android. The Jetpack libraries (Compose, ViewModel, Room, WorkManager) embrace Kotlin idioms — coroutines, flows, sealed classes — so writing Android in Kotlin feels native rather than translated. This lesson sketches the modern shape: Compose UI + ViewModel + Flow.
Compose screen backed by a ViewModel and Flow
EXAMPLE
// build.gradle.kts — modern Android stack
// implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0")
// implementation("androidx.compose.material3:material3:1.2.1")
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
// 1) UI state as a sealed class — exhaustive when()
sealed interface UserUi {
data object Loading : UserUi
data class Loaded(val name: String, val orders: Int) : UserUi
data class Error(val message: String) : UserUi
}
// 2) ViewModel holds a StateFlow the UI subscribes to
class UserViewModel : ViewModel() {
private val _ui = MutableStateFlow<UserUi>(UserUi.Loading)
val ui: StateFlow<UserUi> = _ui
fun load(id: String) {
viewModelScope.launch {
_ui.value = UserUi.Loading
try {
delay(400) // pretend network
_ui.value = UserUi.Loaded(name = "Alice ($id)", orders = 12)
} catch (e: Throwable) {
_ui.value = UserUi.Error(e.message ?: "unknown")
}
}
}
}
// 3) Composable screen — declarative + state-driven
@Composable
fun UserScreen(id: String, vm: UserViewModel = viewModel()) {
val ui by vm.ui.collectAsState()
LaunchedEffect(id) { vm.load(id) } // re-fetch when id changes
Surface(Modifier.padding(16.dp)) {
when (val state = ui) {
is UserUi.Loading -> CircularProgressIndicator()
is UserUi.Error -> Text("Error: ${state.message}")
is UserUi.Loaded -> Column {
Text(state.name, style = MaterialTheme.typography.titleLarge)
Text("Orders: ${state.orders}")
Spacer(Modifier.height(8.dp))
Button(onClick = { vm.load(id) }) { Text("Refresh") }
}
}
}
}
// 4) Activity hosts the Compose root
// class MainActivity : ComponentActivity() {
// override fun onCreate(s: Bundle?) {
// super.onCreate(s)
// setContent { MaterialTheme { UserScreen(id = "42") } }
// }
// }
Why it matters
Sealed UI state + StateFlow + collectAsState is the modern Android trio. The compiler proves you handle every case, the screen survives rotation through ViewModel, and Compose only recomposes the parts that actually changed when the flow emits.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Android Studio → New Project → Empty Activity. // MainActivity uses Jetpack Compose by default in modern templates.Try it Yourself »
Discussion
Loading…