iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…