Irbisa · Cheatsheet
Compose State
remember, mutableStateOf, derivedStateOf, state hoisting, ViewModel + StateFlow
This counter's click handler runs, but the number on screen never changes. What is wrong?
@Composablefun Counter() { var count = 0 Button(onClick = { count++ }) { Text("Clicked $count times") }}What is
mutableStateOf, and how does changing it trigger recomposition?@Composablefun LikeButton() { var liked by remember { mutableStateOf(false) } var taps by remember { mutableIntStateOf(0) } // no boxing on writes Row {…What is the difference between
rememberandrememberSaveable: what survives recomposition, rotation and process death?@Composablefun SearchBar() { var expanded by remember { mutableStateOf(false) } // lost on rotation var query by rememberSaveable { mutableStateOf("") } // restored after rotation and process death val normalized = remember(query) { query.trim().lowercase() } // recomputed when query changes…What is state hoisting, what is the difference between stateful and stateless composables, and where should state live?
// stateful: owns its state, easy to drop in, impossible to control from outside@Composablefun NameFieldStateful() { var name by rememberSaveable { mutableStateOf("") } NameField(name = name, onNameChange = { name = it })}…A dialog holds
remember { mutableStateOf(initialName) }, the parent passes a new name, and the field still shows the old one. Why?@Composablefun NameDialog(initialName: String, onSave: (String) -> Unit) { var name by remember { mutableStateOf(initialName) } AlertDialog( onDismissRequest = {}, confirmButton = { TextButton(onClick = { onSave(name) }) { Text("Save") } }, text = { TextField(value = name, onValueChange = { name = it }) }, )}How do you connect a ViewModel's
StateFlowto Compose:collectAsStateWithLifecyclevscollectAsStatevsproduceState?data class ProfileUiState(val name: String = "", val isLoading: Boolean = true) class ProfileViewModel(repo: ProfileRepository) : ViewModel() { val uiState: StateFlow<ProfileUiState> = repo.observeProfile() .map { ProfileUiState(name = it.name, isLoading = false) } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), ProfileUiState())…Why does adding an item to a
MutableListheld inmutableStateOfnot update the screen, and what domutableStateListOfor a new list change?class TodoHolder { // wrong: the reference never changes, so readers are never invalidated val broken = mutableStateOf(mutableListOf<String>()) fun addBroken(item: String) { broken.value.add(item) }…What is
derivedStateOfand what problem does it solve?@Composablefun ArticleList(articles: List<Article>) { val listState = rememberLazyListState() val scope = rememberCoroutineScope() // the index changes on every scroll frame; the Boolean flips rarely…Find the bug: the search box filters the list correctly, but after the parent updates its data the list stays the same.
@Composablefun ProductList(products: List<Product>, maxPrice: Int) { var query by rememberSaveable { mutableStateOf("") } val visible by remember { derivedStateOf { products.filter { it.price <= maxPrice && it.name.contains(query, ignoreCase = true) } } } Column { TextField(value = query, onValueChange = { query = it }) LazyColumn { items(visible, key = { it.id }) { ProductRow(it) } } }}What are side effects in Compose, and when do you use
LaunchedEffect,DisposableEffect,SideEffectandproduceState?@Composablefun UserScreen(userId: String, repo: UserRepository, analytics: Analytics) { // restarts when userId changes, cancelled when the screen leaves val user by produceState<User?>(initialValue = null, userId) { value = repo.load(userId) }…This button should show a snackbar when tapped, but the code does not compile. Why, and should you use
LaunchedEffectorrememberCoroutineScope?@Composablefun SaveButton(snackbarHostState: SnackbarHostState) { Button(onClick = { LaunchedEffect(Unit) { snackbarHostState.showSnackbar("Saved") } }) { Text("Save") }}The parent passes a new
onTimeoutwhile this splash screen is waiting, yet the old lambda is called. Why, and what doesrememberUpdatedStatefix?@Composablefun SplashScreen(onTimeout: () -> Unit) { LaunchedEffect(Unit) { delay(3_000) onTimeout() } Logo()}What is
snapshotFlow, and when do you convert Compose state into aFlow?@Composablefun FeedAnalytics(listState: LazyListState, analytics: Analytics) { LaunchedEffect(listState) { snapshotFlow { listState.firstVisibleItemIndex } .filter { it > 0 } .collect { index -> analytics.logScrolledTo(index) }…When is a
CompositionLocalthe right way to get a value down the tree, and when is it an abuse?data class Spacing(val small: Dp = 4.dp, val medium: Dp = 8.dp, val large: Dp = 16.dp) val LocalSpacing = staticCompositionLocalOf { Spacing() } // no sensible default: fail loudly instead of rendering with a fakeval LocalImageLoader = staticCompositionLocalOf<ImageLoader> {…When do you write a plain state holder class with a
rememberXState()factory instead of putting the state in a ViewModel?class CheckoutFormState(initialEmail: String) { var email by mutableStateOf(initialEmail) private set var showErrors by mutableStateOf(false) private set…Tapping the star fills it, it empties for a moment, then fills again. What is wrong with this code?
@Composablefun FavouriteRow(item: Item, onToggle: (Long) -> Unit) { var fav by remember(item) { mutableStateOf(item.isFavorite) } IconToggleButton( checked = fav, onCheckedChange = { fav = it onToggle(item.id) // ViewModel sends the request }, ) { Icon(if (fav) Icons.Filled.Favorite else Icons.Outlined.FavoriteBorder, "Favourite") }}Your ViewModel exposes
StateFlow<UiState>and a teammate wantsvar uiState by mutableStateOf(...)instead. What actually differs?Typing fast into a
TextFieldwhose value comes back from a ViewModel drops or reorders characters. Why, and how doesTextFieldStateavoid it?val DigitsOnly = InputTransformation { if (!asCharSequence().all { it.isDigit() }) revertAllChanges()} class PhoneViewModel : ViewModel() { // the field edits this directly; no value/onValueChange round trip…For a checkout form, what goes into
rememberSaveable, what intoSavedStateHandle, and what is reloaded? And how do you test the restore?class CheckoutViewModel(private val handle: SavedStateHandle) : ViewModel() { // small user input: survives process death val note: StateFlow<String> = handle.getStateFlow("note", "") fun onNoteChange(value: String) { handle["note"] = value }…How does the snapshot system under Compose state work, and what does it guarantee?