Mobile Development6 min read

Android with Jetpack Compose

Build Android apps with Jetpack Compose: composable functions, state management with ViewModel, navigation, Hilt DI, and Kotlin coroutines.

androidjetpack-composekotlinmobile

Jetpack Compose is the modern UI toolkit for Android - declarative, composable, and Kotlin-first. Combined with Kotlin coroutines and the MVVM architecture pattern (ViewModel + StateFlow), it provides a reactive, testable approach to Android development.

Compose is the current standard for new Android UIs - avoid XML layouts for new screens. Use ViewModel + StateFlow for state management (not Compose state hoisting for complex state). Use Hilt for dependency injection. Kotlin coroutines for all async operations.

Workflow

  1. Write composable functionsComposables are the building block of Compose UI - functions that emit UI based on state.
  2. Manage state with ViewModel and StateFlowViewModel survives configuration changes (rotation); StateFlow provides reactive state emission.
  3. Navigate with Compose NavigationNavigation Component manages the backstack and handles deep linking declaratively.
  4. Inject dependencies with HiltHilt (built on Dagger) manages dependency graphs at the right Android lifecycle scopes.
  5. Use Kotlin coroutines for asyncCoroutines provide structured concurrency - suspending functions for async without callback hell.

Output Format

Composable functions with Modifier parameter, sealed class UiState, HiltViewModel with StateFlow, NavHost with typed routes, Hilt modules for dependency injection, viewModelScope.launch for coroutines.

Examples

UiState pattern: `sealed class ProductUiState { object Loading : ProductUiState() data class Success(val products: List<Product>) : ProductUiState() data class Error(val message: String) : ProductUiState() }; // in Composable: when(state) { is Loading -> CircularProgressIndicator() is Success -> ProductList(state.products) is Error -> ErrorScreen(state.message) }`
Lazy list: `LazyColumn(modifier = Modifier.fillMaxSize()) { items(products, key = { it.id }) { product -> ProductCard(product = product, onAddToCart = { viewModel.addToCart(product.id) }) } }`
Material 3 theme: `MaterialTheme(colorScheme = if (darkTheme) darkColorScheme() else lightColorScheme(), typography = Typography, content = content)`

Edge Cases & Gotchas

  • Recomposition performance: composables that recompose frequently with expensive work (complex layout, many children) should use `derivedStateOf { }` to avoid unnecessary recompositions, or `remember { }` for expensive computations
  • Backstack and deep links: deep links that bypass the normal navigation flow require careful backstack management. Use `NavController.navigate(route, navOptions { popUpTo("home") { inclusive = false } })` to create the right backstack
  • Previews: `@Preview @Composable fun ProductCardPreview() { ProductCard(product = previewProduct, onAddToCart = {}) }` - enable design-time previews in Android Studio. Keep composables preview-compatible by injecting data, not fetching it inside

When to Break These Rules

  • For apps with complex custom drawing (games, specialized visualizations), Android Canvas (via `Canvas` composable or SurfaceView) or OpenGL ES is required. Compose handles standard UI well but isn't designed for game rendering.

Reference Files

  • Compose docs: https://developer.android.com/jetpack/compose
  • Compose state: https://developer.android.com/jetpack/compose/state
  • Hilt: https://developer.android.com/training/dependency-injection/hilt-android
  • Kotlin coroutines: https://kotlinlang.org/docs/coroutines-overview.html

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.