Irbisa · Шпаргалка
Состояние в Compose
remember, mutableStateOf, derivedStateOf, поднятие состояния, ViewModel + StateFlow
Обработчик клика у этого счётчика срабатывает, но число на экране не меняется. Что не так?
@Composablefun Counter() { var count = 0 Button(onClick = { count++ }) { Text("Clicked $count times") }}Что такое
mutableStateOfи как его изменение запускает рекомпозицию?@Composablefun LikeButton() { var liked by remember { mutableStateOf(false) } var taps by remember { mutableIntStateOf(0) } // no boxing on writes Row {…Чем
rememberотличается отrememberSaveable: что переживает рекомпозицию, поворот экрана и смерть процесса?@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…Что такое state hoisting, чем stateful-composable отличается от stateless и где должно жить состояние?
// 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 })}…Диалог держит
remember { mutableStateOf(initialName) }, родитель передаёт новое имя, а поле показывает старое. Почему?@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 }) }, )}Как связать
StateFlowиз ViewModel с Compose:collectAsStateWithLifecycle,collectAsStateилиproduceState?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())…Почему добавление элемента в
MutableListвнутриmutableStateOfне обновляет экран и что меняютmutableStateListOfили новый список?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) }…Что такое
derivedStateOfи какую проблему он решает?@Composablefun ArticleList(articles: List<Article>) { val listState = rememberLazyListState() val scope = rememberCoroutineScope() // the index changes on every scroll frame; the Boolean flips rarely…Найдите баг: поле поиска фильтрует список правильно, но после обновления данных родителем список остаётся прежним.
@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) } } }}Что такое side effects в Compose и когда использовать
LaunchedEffect,DisposableEffect,SideEffectиproduceState?@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) }…Эта кнопка должна показывать снекбар по нажатию, но код не компилируется. Почему и что выбрать:
LaunchedEffectилиrememberCoroutineScope?@Composablefun SaveButton(snackbarHostState: SnackbarHostState) { Button(onClick = { LaunchedEffect(Unit) { snackbarHostState.showSnackbar("Saved") } }) { Text("Save") }}Родитель передаёт новый
onTimeout, пока сплэш-экран ждёт, но вызывается старая лямбда. Почему и что исправляетrememberUpdatedState?@Composablefun SplashScreen(onTimeout: () -> Unit) { LaunchedEffect(Unit) { delay(3_000) onTimeout() } Logo()}Что такое
snapshotFlowи когда нужно превращать стейт Compose воFlow?@Composablefun FeedAnalytics(listState: LazyListState, analytics: Analytics) { LaunchedEffect(listState) { snapshotFlow { listState.firstVisibleItemIndex } .filter { it > 0 } .collect { index -> analytics.logScrolledTo(index) }…Когда
CompositionLocalслужит правильным способом передать значение вниз по дереву, а когда это злоупотребление?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> {…Когда вы пишете обычный класс-state holder с фабрикой
rememberXState()вместо того, чтобы держать стейт во ViewModel?class CheckoutFormState(initialEmail: String) { var email by mutableStateOf(initialEmail) private set var showErrors by mutableStateOf(false) private set…Нажатие на звезду заполняет её, потом она на мгновение пустеет и снова заполняется. Что не так с этим кодом?
@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") }}ViewModel отдаёт
StateFlow<UiState>, а коллега хочет вместо этогоvar uiState by mutableStateOf(...). В чём реальная разница?При быстром наборе в
TextField, значение которого возвращается из ViewModel, символы теряются или меняются местами. Почему и как этого избегаетTextFieldState?val DigitsOnly = InputTransformation { if (!asCharSequence().all { it.isDigit() }) revertAllChanges()} class PhoneViewModel : ViewModel() { // the field edits this directly; no value/onValueChange round trip…Для формы оформления заказа: что кладут в
rememberSaveable, что вSavedStateHandle, что перезагружают и как протестировать восстановление?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 }…Как устроена система снапшотов под стейтом Compose и что она гарантирует?