contentintech

Android with Kotlin Cheatsheet

Quick reference for Kotlin syntax, Jetpack Compose composables, state and remember, Modifiers, Navigation Compose, coroutines, and Flow.

Jetpack ComposeKotlinCoroutinesState
NotesCheatsheet

Kotlin Syntax

Variables & Null Safety

val name = "Ada"            // immutable
var count = 0               // mutable
val price: Double = 9.99    // explicit type

var nick: String? = null    // nullable
val len = nick?.length ?: 0 // safe call + Elvis
nick?.let { use(it) }       // run if non-null
val x = nick!!              // throws if null (avoid)

Functions & Lambdas

fun add(a: Int, b: Int) = a + b         // expression body
fun hi(name: String, p: String = "!") = "Hi $name$p"
hi(name = "Sam")                        // named arg

val square = { n: Int -> n * n }        // lambda
list.filter { it % 2 == 0 }             // implicit `it`
list.map { it * 2 }
list.forEach { println(it) }

Data & Sealed Classes

data class User(val id: Int, val name: String)
val u2 = u1.copy(name = "New")          // copy

sealed interface UiState {
    data object Loading : UiState
    data class Success(val data: List<User>) : UiState
    data class Error(val msg: String) : UiState
}

when (state) {                          // exhaustive
    UiState.Loading    -> …
    is UiState.Success -> state.data
    is UiState.Error   -> state.msg
}

Compose Basics

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

setContent {                    // in Activity.onCreate
    AppTheme { Greeting("Android") }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() = Greeting("Preview")

Layout Composables

Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
    Text("Top"); Text("Bottom")
}
Row(verticalAlignment = Alignment.CenterVertically) {
    Icon(Icons.Default.Star, null); Text("Label")
}
Box(contentAlignment = Alignment.Center) {
    Image(...); Text("Overlay")
}
Spacer(Modifier.height(16.dp))

LazyColumn {
    items(users, key = { it.id }) { u -> UserRow(u) }
    item { Footer() }
}
ComposableUse
ColumnVertical stack
RowHorizontal stack
BoxOverlay / z-stack
LazyColumnScrollable list
LazyRowHorizontal list
LazyVerticalGridGrid of cells

State & remember

var count by remember { mutableStateOf(0) }
count++                                  // triggers recomposition

// survives rotation / process recreation
var text by rememberSaveable { mutableStateOf("") }

// derived value, recomputed only when inputs change
val enabled by remember { derivedStateOf { text.isNotBlank() } }

// State hoisting: stateless child
@Composable
fun Field(value: String, onChange: (String) -> Unit) {
    TextField(value = value, onValueChange = onChange)
}

// ViewModel + StateFlow
val state by vm.state.collectAsStateWithLifecycle()
APISurvives
rememberRecomposition
rememberSaveable+ rotation / process death
ViewModel+ full config changes

Modifiers

Modifier
    .fillMaxWidth()          // .fillMaxSize() / .size(48.dp)
    .padding(16.dp)          // order matters!
    .background(color)
    .clip(RoundedCornerShape(12.dp))
    .border(1.dp, color)
    .clickable { onClick() }
    .weight(1f)              // inside Row/Column
    .align(Alignment.Center) // inside Box/Column/Row
    .aspectRatio(16f / 9f)

Navigation

@Serializable object Home
@Serializable data class Details(val id: Int)

val nav = rememberNavController()
NavHost(nav, startDestination = Home) {
    composable<Home> {
        HomeScreen(onOpen = { nav.navigate(Details(it)) })
    }
    composable<Details> { entry ->
        val args = entry.toRoute<Details>()
        DetailsScreen(args.id)
    }
}

nav.navigate(Details(7))     // go to
nav.popBackStack()           // back
nav.navigate(Home) { popUpTo(Home) { inclusive = true } }

Coroutines & Flow

suspend fun load() = withContext(Dispatchers.IO) { api.get() }

viewModelScope.launch { _state.value = load() }   // scoped, auto-cancels

// concurrent
coroutineScope {
    val a = async { api.getA() }
    val b = async { api.getB() }
    combine(a.await(), b.await())
}

// Flow pipeline
queries
    .debounce(300)
    .filter { it.length >= 2 }
    .distinctUntilChanged()
    .mapLatest { repo.search(it) }   // cancels previous
    .collect { render(it) }

// StateFlow in a ViewModel
private val _s = MutableStateFlow<UiState>(UiState.Loading)
val s: StateFlow<UiState> = _s.asStateFlow()

Common Patterns

NeedAPI
Run suspend work on key changeLaunchedEffect(key)
Setup / teardownDisposableEffect
Callback from event handlerrememberCoroutineScope
Collect Flow lifecycle-awarecollectAsStateWithLifecycle()
Screen scaffoldingScaffold { TopAppBar, FAB }
Theme colors / typeMaterialTheme.colorScheme
Type-safe HTTPRetrofit suspend @GET
Get ViewModel in composableviewModel() / hiltViewModel()

Section navigation