Compose
25.01.2023 | https://youtu.be/qb0Ezy-WO_k |
https://composelibraries.com/ |
Compose
Cовременная, декларативная библиотека для создания пользовательских интерфейсов в Android. Compose значительно упрощает разработку UI, заменяя традиционную императивную модель (основанную на Views и XML) на декларативный подход.
• Декларативный UI: В Compose UI описывается как функция. Вы определяете, как интерфейс должен выглядеть в зависимости от текущего состояния данных, а библиотека автоматически обновляет UI при изменении этих данных.
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
• Состояние и управление: Compose использует состояние для обновления интерфейса. Если изменяется состояние, вызов функции обновляет только измененные части интерфейса — это называется рекомпозицией. Состояние в Compose управляется с помощью таких функций, как remember
, mutableStateOf
, rememberSaveable
.
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}
• Без XML: Compose полностью устраняет необходимость в XML-файлах для описания интерфейсов. Всё создается и управляется на уровне кода.
• Модульность и многократное использование: Compose предлагает возможность создавать небольшие, независимые и переиспользуемые компоненты. Это упрощает создание и поддержку сложных интерфейсов.
• Совместимость с View: Compose и традиционные Views могут взаимодействовать друг с другом. Вы можете встраивать Views в Compose или использовать Compose в существующих проектах с Views.
@Composable
fun CustomView() {
AndroidView(factory = { context ->
TextView(context).apply {
text = "This is a traditional Android View!"
}
})
}
• Упрощенная анимация: В Compose встроена поддержка анимаций, которая позволяет легко добавлять динамичные эффекты в интерфейс.
@Composable
fun AnimatedBox() {
var expanded by remember { mutableStateOf(false) }
val size by animateDpAsState(if (expanded) 200.dp else 100.dp)
Box(
Modifier
.size(size)
.clickable { expanded = !expanded }
.background(Color.Blue)
)
}
Lifecycle
Жизненный цикл Compose отличается от традиционных View-систем в Android, так как он основан на декларативном подходе к построению интерфейсов. В Compose нет привычных методов жизненного цикла, таких как onCreate
, onStart
и onDestroy
. Вместо этого, основной механизм работы с UI — это рекомпозиция и управление состоянием.
Этапы жизненного цикла в Composable-функции
Composition
Функция вызывается, чтобы построить UI. Здесь создаются иерархия и состояние интерфейса.
Recomposition
Вызывается повторно, если одно из наблюдаемых состояний изменилось. Только изменённые части интерфейса пересоздаются.
Disposal
Происходит, когда элемент удаляется из UI-иерархии. Используется для очистки ресурсов.
Этапы отрисовки Composable-функции
Composition
Composable-функции вызываются для создания или обновления UI-иерархии. Создаются объекты, представляющие состояние и элементы интерфейса.
Layout
Компоненты определяют свои размеры и позиции. Каждый Composable сообщает системе свои ограничения (constraints) и рассчитывает свои размеры в соответствии с ними.
Drawing
После вычисления Layout
Compose вызывает методы рисования (например, draw
), чтобы отобразить элементы на экране. В этом этапе происходит рендеринг пикселей на Canvas
.
Annotations
@Composable
Используется для обозначения функции, которая описывает часть пользовательского интерфейса.
@Composable
fun Greeting(name: String) {
Text("Hello, $name!")
}
@Preview
используется для предварительного просмотра пользовательских интерфейсов в Android Studio без необходимости запускать приложение на устройстве или эмуляторе.
@Preview
@Composable
private fun UserProfilePreview() {
UserProfile(name = "John Doe", age = 25)
}
@ReadOnlyComposable
Используется для указания, что определённая Composable
-функция не изменяет состояние и только читает данные. Это позволяет Compose эффективно оптимизировать ререндеринг, зная, что внутри функции не происходит изменений состояния.
@Composable
@ReadOnlyComposable
fun DisplayUserName(user: User) {
Text("User: ${user.name}")
}
@Stable
Используется для обозначения того, что объект или класс поддерживает стабильное состояние и не изменяется в процессе работы, но в отличие от @Immutable
, он может изменяться, однако изменения происходят контролируемо.
@Stable
data class User(val name: String, val age: Int)
@Immutable
Используется для указания, что класс или тип данных является неизменяемым (immutable). Это означает, что после создания объекта его состояние не может измениться.
@Immutable
data class User(val name: String, val age: Int)
State Functions
key
remember создает объект один раз и возвращает нам его все последующие вызовы. Но иногда нам необходимо пересоздать объект из remember
. Если приводить аналогию с кэшем, то нам надо сбросить кэш и заново получить данные. Пока key не меняется, remember каждый раз возвращает нам ранее созданный объект. Но если при очередном вызове remember у нас сменился key
, то remember снова выполняет код по созданию объекта. И теперь каждый раз будет возвращать нам этот новый объект, пока key
снова не поменяется.
val exampleWithKey: Example = remember(paramKey) { Example() }
by
Сейчас для работы с значением State мы используем его поле value
. Но это можно сделать немного проще с помощью специальных делегатов. Теперь State
можно использовать как обычную var переменную и для чтения значения и для записи.
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
val checked: Boolean by remember { mutableStateOf(false) }
println(checked)
remember
С помощью этой функции мы создаем объект внутри Composable-функции только 1 раз при первом запуске. Получаем этот объект во всех последующих перезапусках. Работает как кэш. Если объекта еще нет, то она его создаст. А если он уже был создан, она его вернет нам. При каждом перезапуске мы получаем и используем один и тот же объект.
class Example
@Composable
fun ComposeFunc(
paramKey: Boolean
) {
val example: Example = remember { Example() }
}
rememberSaveable
Версия remember
которая сохраняет значение при изменении конфигурации.
var checked: Boolean by rememberSaveable { mutableStateOf(false) }
rememberUpdateState
Используется когда мы хотим сохранить обновленную ссылку на переменную в длительном side effect без необходимости перезапуска при рекомпозиции.
@Composable
fun TwoButtonScreen() {
var buttonColour by remember { mutableStateOf("Unknown") }
Button(onClick = { buttonColour = "Red" })
Button(onClick = { buttonColour = "Black" })
Timer(buttonColor = buttonColour)
}
@Composable
fun Timer(
buttonColour: String
) {
println("Composing timer with colour : $buttonColour")
val buttonColorUpdated by rememberUpdatedState(newValue = buttonColour)
LaunchedEffect(key1 = Unit) {
startTimer(5000L) {
println("Timer ended")
println("[1] Last pressed button color is $buttonColour") // bad
println("[2] Last pressed button color is $buttonColorUpdated") // good
}
})
}
suspend fun startTimer(time: Long, onTimerEnd: () -> Unit) {
delay(timeMillis = time)
onTimerEnd()
}
State Builders
mutableStateOf
В комбинации remember
+ mutableStateOf
, функция mutableStateOf
создает State
, а функция remember
делает, так, чтобы этот State
не сбрасывался при каждом перезапуске функции.
val checked: MutableState<Boolean> = remember { mutableStateOf(false) }
println(checked.value)
mutableIntStateOf
Держатель значения для примитивного типа Int
.
var page: Int by remember { mutableIntStateOf(0) }
mutableLongStateOf
Держатель значения для примитивного типа Long
.
var time: Long by remember { mutableLongStateOf(0L) }
mutableFloatStateOf
Держатель значения для примитивного типа Float
.
var value: Float by remember { mutableFloatStateOf(0F) }
mutableDoubleStateOf
Держатель значения для примитивного типа Double
.
var value: Double by remember { mutableDoubleStateOf(0.0) }
mutableStateListOf
Cоздает мутируемый список, который поддерживает реактивное обновление пользовательского интерфейса при изменении элементов внутри этого списка. Этот список является аналогом стандартного MutableList
, но с возможностью интеграции в реактивную систему Compose. Когда вы используете список, созданный с помощью mutableStateListOf
, любые изменения в списке (добавление, удаление или изменение элементов) автоматически вызывают перерисовку тех частей интерфейса, которые зависят от этого списка. Это полезно для создания динамических интерфейсов, где состояние списка влияет на отображение UI-компонентов.
• Время доступа к элементам списка и выполнения операций, таких как добавление или удаление, аналогично обычному ArrayList
, то есть время доступа к элементам — O(1), добавление в конец — O(1), удаление — O(n).
• Пространственная сложность зависит от количества элементов в списке, как и в обычном списке.
@Composable
fun DynamicListExample() {
// Создаем состояние списка с помощью mutableStateListOf
val items: SnapshotStateList<String> = remember { mutableStateListOf("Item 1", "Item 2", "Item 3") }
Column(modifier = Modifier.padding(16.dp)) {
// Отображаем каждый элемент списка
items.forEach { item ->
Text(text = item, modifier = Modifier.padding(4.dp))
}
// Добавляем кнопку для добавления нового элемента в список
Button(onClick = {
items.add("Item ${items.size + 1}")
}) {
Text("Add Item")
}
}
}
derivedStateOf
Использовать когда входные данные меняются чаще чем нужна рекомпозиция. Пример: изменение положения скролла. Действует анологично оператору distinctUntilChanged
в Kotlin Flow
. Подписывается на изменения состояний, которые были прочитаны за первый проход. Примеры:
• Порог прокрутки (scrollPosition > 0).
• Количество элементов больше порог(items > 0).
• Валидация формы (username.isValid)
val listState = rememberLazyListState()
val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }
produceState
Используется для создания состояния, которое управляется асинхронной операцией, например, сетевым запросом или долгой вычислительной задачей. Он позволяет получать значения и обновлять состояние в реактивном стиле.
• Инициализация состояния: produceState
позволяет задать начальное значение и автоматически обновлять его при изменении зависимостей.
• Обновление состояния: Когда происходит изменение зависимостей, асинхронная операция запускается снова, и состояние обновляется.
val someData = produceState(initialValue = null) {
value = fetchData() // Асинхронная операция
}
Snapshot
Концепция, которая используется в Compose для управления состоянием и его отслеживания. Она обеспечивает механизм для наблюдения за изменениями данных и синхронизирует эти изменения с интерфейсом пользователя (UI). Основная цель механизма снапшотов — сделать работу с состоянием в Compose реактивной, то есть когда данные изменяются, UI автоматически обновляется. Снимки состояния позволяют изолировать и отслеживать изменения, сделанные в реактивных данных, и гарантируют, что эти изменения будут безопасно применяться, особенно в многопоточных приложениях.
// Compose позволяет вручную управлять снапшотами, используя методы вроде Snapshot.takeSnapshot,
// чтобы работать с состоянием более тонко, когда это необходимо.
@Composable
fun ManualSnapshotExample() {
val state = remember { mutableStateOf("Initial") }
Column {
Text(text = "Current state: ${state.value}")
Button(onClick = {
// Создаём новый снапшот
Snapshot.takeSnapshot {
state.value = "Changed in Snapshot"
}
}) {
Text("Change State in Snapshot")
}
}
}
SnapshotStateList
Это специальный тип списка, созданный для работы с системой состояния Compose. По сути, mutableStateListOf
возвращает SnapshotStateList
, который отслеживает изменения и интегрируется с Compose. Обычные списки List
или MutableList
не будут автоматически обновлять пользовательский интерфейс при изменении данных. Например, если вы измените элементы внутри обычного List, вам нужно будет вручную обновить состояние, чтобы заставить Compose перерисовать элементы UI. SnapshotStateList
поддерживает систему снимков (Snapshot
) в Compose, чтобы отслеживать изменения состояния.
snapshotFlow
Специальная функция в Compose, которая позволяет наблюдать за изменениями состояния в системе снапшотов и преобразовывать эти изменения в поток данных (Flow
). Это полезный механизм для интеграции состояния Compose с асинхронной моделью потоков, например, с Flow
, когда вам нужно реагировать на изменения состояния и обрабатывать их в асинхронных операциях, таких как сетевые запросы, обновления UI и другие задачи. Когда вы используете реактивное состояние в Compose, например, через mutableStateOf
, изменения этого состояния автоматически управляются системой снапшотов. С помощью snapshotFlow
можно следить за этими изменениями и отправлять их как элементы Flow, что даёт вам больше гибкости для работы с состоянием в асинхронной среде.
@Composable
fun SnapshotFlowExample() {
val count = remember { mutableStateOf(0) }
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
// Преобразуем изменения состояния в Flow с помощью snapshotFlow
snapshotFlow { count.value }
.collect { newValue ->
// Обрабатываем новое значение из snapshotFlow
println("Collected new value: $newValue")
}
}
Column {
Text(text = "Counter: ${count.value}")
Button(onClick = {
count.value += 1
}) {
Text("Increase Counter")
}
}
}
ViewCompositionStrategy
Определяет стратегию автоматического удаления композиции. Для ComposeView
и AbstractComposeView
. Нужен для интеропа Compose и View.
• DisposeOnDetachedFromWindowOrReleasedFromPool
. Используется по умолчанию. Композиция будет автоматически удалена при отсоединении View
от Window
если только она не является частью контейнера пула такого как RecyclerView
.
• DisposeOnDetachedFromWindow
- композиция будет автоматически удалена при отсоединении View
от Window
.
• DisposeOnLifecycleDestroyed(LifecycleOwner)
- удаляет композицию когда возвращенный LifecycleOwner
уничтожается.
• DisposeOnViewTreeLifecycleDestroyed
- удаляет композицию при переходе Lifecycle в состояние Destroyed
.
BackHandler
Позволяет перехватывать и обрабатывать нажатие кнопки «Назад» внутри @Composable
-функции.
@Composable
fun MyScreen() {
// Обработка нажатия кнопки "Назад"
BackHandler(enabled = isHandlerEnabled) {
// Действия, которые должны выполниться при нажатии кнопки "Назад"
}
// Основное содержимое экрана
Text(text = "Текст")
}
nestedScrollConnection
Сспособ реагировать на фазы цикла вложенной прокрутки.
• Пример: дочерний экран со списком внутри Pager
на родительском экране с AppBar
. При скролле будет прокручиваться список дочернего экрана, когда перемотка будет завершена - будет прокручиваться AppBar
родительского экрана.
val nestedScrollConnection = rememberNestedScrollInteropConnection()
Scaffold(
modifier = Modifier.nestedScroll(nestedScrollConnection)
) {}
Strong Skipping Mode
https://developer.android.com/develop/ui/compose/performance/stability/strongskipping |
Механизм оптимизации, который пропускает выполнение кода композиции, если входные данные для функции не изменились. Он позволяет существенно снизить нагрузку на систему, избегая ненужных перерисовок.
@NonSkippableComposable
Указывает, что конкретная Composable
-функция не может быть пропущена (skipped) во время повторного отображения.
@NonSkippableComposable
@Composable
fun CriticalComposable(data: Int) {
// Эта функция будет вызвана при каждом recomposition
println("Recomposing with data: $data")
}
@DontMemoize
Указывает, что результат работы функции не должен кэшироваться в памяти. Compose автоматически оптимизирует работу, сохраняя результаты некоторых вычислений в кэше (memoization). Однако это не всегда желательно.
@DontMemoize
@Composable
fun NonCachedComposable(): String {
return UUID.randomUUID().toString() // Всегда генерируется новое значение
}
Оптимизация
Вопросы на собесе (37)
remember (4)
- Что из себя представляет remember?
Функция, которая сохраняет результат вычисления в памяти, чтобы избежать повторных вычислений при перерисовке компонента, сохраняя значение между составными вызовами.
- Различие между remember и rememberSaveable?
remember
сохраняет состояние при пересоздании композиций, аrememberSaveable
сохраняет состояние при пересоздании, включая изменение конфигурации (например, поворот экрана).
- Как сохранить данные между рекомпозициями?
Использовать
remember
.
- Сохранится ли состояние с remember при изменении конфигурации?
Нет, так как
remember
сохраняет данные только во время жизни текущей композиции.
- Что из себя представляет remember?
@Composable-функция (7)
- Как работает @Composable-функция?
Функция с аннотацией
@Composable
описывает часть UI, которая добавляется в дерево композиций и может реагировать на изменения состояния. При каждом изменении состояния система вызывает функцию заново, чтобы обновить соответствующие элементы интерфейса.
- Как часто вызывается @Composable-функция?
Функция
@Composable
вызывается каждый раз, когда изменяется её состояние или параметры. Если параметры функции изменяются, она будет вызвана снова для обновления пользовательского интерфейса, даже без изменения состояния.
- Гарантирован ли порядок выполнения @Composable-функций?
Не гарантирован, так как Compose может оптимизировать выполнение и вызывать функции в произвольном порядке.
- Что делает объект Composer под капотом у @Composable-функций?
Управляет состоянием и перерисовкой
@Composable
-функций, отслеживая их изменения и обновляя только измененные части UI. Он строит и изменяет дерево композиций, оптимизируя обновление интерфейса для повышения производительности.
- Жизненный цикл @Composable-функции?
• Вызов - функция вызывается, когда необходимо отобразить UI.
• Рекомпозиция - если изменяется состояние, вызывается повторный запуск функции для обновления UI.
• Размещение - Compose устанавливает композируемые элементы на экране.
• Отмена - Если
@Composable
-функция больше не требуется (например, при удалении из иерархии), она отменяется и освобождает ресурсы.
- Как происходит процесс отрисовки фрейма?
• Изменение состояния - когда изменяется состояние приложения, вызываются функции, обновляющие UI.
• Рекомпозиция - Compose перерисовывает только те
@Composable
-функции, которые зависят от изменившегося состояния.• Создание дерева компонентов - на основе текущего состояния создается дерево UI-компонентов.
• Измерение - определяются размеры и позиции каждого компонента в дереве.
• Отрисовка - каждый компонент рисуется на канвасе с использованием методов отрисовки.
• Композиция и рендеринг - готовый UI отображается на экране.
- Что такое skippable и restartable в Compose?
Это характеристики композиции:
•
skippable
означает, что при рекомпозиции часть UI может быть пропущена, если её состояние не изменилось.•
restartable
указывает, что можно перезапустить выполнение композиции, начиная с этой точки, при изменении состояния.
- Как работает @Composable-функция?
Другие (26)
- Что такое Compose?
Библиотека для создания UI в Android с использованием декларативного подхода. Она позволяет описывать UI как функции, автоматически управляя состоянием и обеспечивая высокую производительность через рекомпозицию.
- Чем Compose отличается от View?
• Декларативный подход.
• Интеллектуальная перекомпановка.
• Типобезопасность Kotlin.
- Какие проблемы бывают в Compose?
• Снижение производительности из-за избыточных рекомпозиций.
• Ошибки в отображении и поведении UI из-за неправильной работы с состоянием.
- С какими трудностями можно столкнуться при переезде с View на Compose?
• Изменение текущей архитектуры/навигации/асинхронной работы.
• Внедрение UDF.
• Интеграция с существующими библиотеками (Dagger/Coil)
- Как понять, что код на Compose оптимально написан?
• Минимизация повторных композиций: используйте
remember
, чтобы кэшировать состояние и избегать ненужных пересчетов.• Правильное использование
key
для элементов списков: это помогает Compose отслеживать изменения и оптимизировать обновления.• Избегание тяжелых операций в
@Composable
функциях: старайтесь переместить сложную логику вне композиций.• Использование
derivedStateOf
для производных состояний, чтобы избежать лишних пересчетов.• Профилирование приложения с помощью инструментов, таких как Android Studio Profiler, для выявления узких мест.
- Какие этапы отрисовки есть в Compose?
• Composition (Компоновка): разбор и построение иерархии компонентов на основе функции
@Composable
. На этом этапе создаётся структура UI из компонентов, которые необходимо отобразить.• Layout (Разметка): определение размеров и позиций элементов на экране. Каждый компонент измеряет себя и свои дочерние элементы, определяя их размеры и положение.
• Drawing (Рисование): финальная отрисовка элементов на экране. На этом этапе компоненты визуально отображаются с учетом их размеров, позиции и стиля.
- Как ускорить Compose?
• Заменить XML-иконки на
ImageVector
.• Заменить Modifier.composed на
Modifier.Node
.• Использовать специальные типы для
MutableState
.• Использовать аннотации
@Stable
@Immutable
@ReadOnlyComposable
.• Подключить линтеры.
- Что такое рекомпозиция? (Как работает)
Рекомпозиция в Compose — это процесс обновления UI при изменении состояния. Когда состояние изменяется, Compose определяет, какие компоненты требуют обновления, и пересоздает только их. Это позволяет эффективно управлять интерфейсом, минимизируя переработку и повышая производительность.
- Как избегать ненужных рекомпозиций?
• Использовать
remember
для сохранения состояний между рекомпозициями.• Применять
key
в списках для отслеживания изменений элементов.• Разделять UI на мелкие компоненты и контролировать их состояние через
remember
иderivedStateOf
.
- Как Compose понимает, что стейт поменялся?
Через изменение входных параметров функции или отслеживаемых данных.
- Какие типы в Compose считаются стабильными?
Типы
@Stable
, неизменяемые (val
), встроенные (Int
,String
и пр.), а такжеdata class
с неизменяемыми полями.
- Почему List не считается стабильным типом в Compose?
List
не считается стабильным в Compose, так как его содержимое может изменяться, нарушая предсказуемость при перерисовке, что мешает оптимизации и контролю за изменениями.
- Как сделать типы в Compose стабильными?
С помощью аннотаций
@Stable
и@Immutable
.
- Как работают аннотации @Stable и @Immutable?
•
@Immutable
указывает, что объект полностью неизменен и его состояние не изменится после создания, что позволяет Compose оптимизировать рендеринг.•
@Stable
обозначает, что объект может изменяться, но изменения управляются контролируемо, что позволяет Compose правильно отслеживать такие изменения и обновлять UI при необходимости.
- Число лежит в mutableState без remember при клике увеличивается. Что произойдет через 3 нажатия?
Если число хранится в
mutableState
безremember
, то при каждом клике оно будет увеличиваться, но после пересоздания компонента (например, при изменении конфигурации) состояние будет потеряно. После трех нажатий число будет увеличено, но при пересоздании оно вернется к начальному значению, так какremember
не сохраняет состояние.
- Для чего нужен mutableState?
Используется для создания изменяемого состояния, которое может отслеживать изменения и автоматически обновлять UI при их возникновении. Это позволяет сохранять состояние компонентов и реагировать на взаимодействия пользователя, обеспечивая реактивный подход к построению интерфейса.
- Для чего используется derivedStateOf?
Используется для создания производного состояния, которое зависит от других состояний. Оно позволяет оптимизировать производительность, так как обновление производного состояния будет происходить только при изменении его зависимостей, а не при каждом обновлении родительского состояния.
- Для чего используется snapshotFlow?
Используется для создания потоков (
Flow
), которые реагируют на изменения состояния в Compose. Он позволяет получать значения из состояния в виде потоков и обрабатывать их асинхронно, что упрощает интеграцию с корутинами и позволяет отслеживать изменения состояния в реактивном стиле.
- Что такое SnapshotStateList?
Изменяемый список, который автоматически отслеживает изменения и триггерит рекомпозицию при обновлении. Он используется для управления состоянием списков, позволяя эффективно обновлять пользовательский интерфейс при добавлении, удалении или изменении элементов.
- Для чего используется produceState?
Используется для создания состояния, которое управляется асинхронной операцией, например, сетевым запросом или долгой вычислительной задачей. Он позволяет получать значения и обновлять состояние в реактивном стиле.
- Для чего нужен nestedScrollConnection?
nestedScrollConnection
используется для обработки вложенной прокрутки в Compose, позволяя компонентам взаимодействовать с прокруткой друг друга, что обеспечивает плавное поведение при взаимодействии с несколькими прокручиваемыми элементами.
- Разница между rememberCoroutineScope и remember { CoroutineScope }?
rememberCoroutineScope
привязан к жизненному циклуComposable
и отменяется при выходе из него, аremember { CoroutineScope }
создает независимый scope, который нужно вручную очищать, чтобы избежать утечек.
- Как под капотом устроен rememberCoroutineScope?
rememberCoroutineScope()
сохраняетCoroutineScope
, привязанный к жизненному циклуComposition
. Когда этот элемент уходит изComposition
,CoroutineScope
отменяется. Под капотом он создаетCoroutineScope
, используяremember
, а контекстом для корутин выступаетDispatchers.Main.immediate
иJob
, привязанный к текущемуComposition
.
- Что компилятор неявно пробрасывает во все Composable-функции?
Объект
Composer
, который управляет состоянием и рекомпозицией во время выполнения функции.
- В инспекторе отображается много рекомпозиций как понять в чем проблема?
Использовать Сompose compile metrics.
- Что произойдет при выполнении функции?
@Composable fun CounterSample() { Column { var count by mutableStateOf(0) if (count > 0) { Text("Counter is $count") } Button( onClick = { count++ }, enabled = count < 5 ) { Text("Increase counte") } } }
- IDE подсветит ошибку создания
mutableState
безremember
, придется добавить@SuppressLint("UnrememberedMutableState")
.
- При запуске отобразится только
Button
.
- При клике на кнопку ничего не произойдет,
count
не будет увеличен.
- IDE подсветит ошибку создания
- Что такое Compose?