Навчальний простір / Kotlin
Програмування мобільних застосунків

Compose UI: стан, recomposition і керовані події

Compose описує UI через стан і події.

Зміст матеріалу

Що ви опануєте

Ви навчитеся будувати екран як функцію стану, відокремлювати state holder від UI та пояснювати recomposition. Приклад — додавання навчального завдання. Екран отримує текст і повідомляє про події; власник стану виконує зміну. Це продовжує базові Compose приклади першої лекції з чіткішими контрактами.

Composition і стан

Composition описує UI, створений виконанням composable функцій. Recomposition повторно виконує потрібні частини після зміни спостережуваного стану. Тіло composable може виконуватися багато разів; порядок і кількість виконань не слід використовувати як бізнес-лічильник. Виведення Text залежить від переданого стану.

mutableStateOf створює observable значення. remember зберігає його між recomposition у відповідній позиції composition. Після вилучення цієї позиції значення забувається. rememberSaveable може зберігати підтримувані дані для відновлення через saved instance state; це не довгострокове сховище й не заміна базі даних.

State hoisting

State hoisting переносить стан до спільного власника, передаючи значення вниз і callbacks угору. Керований composable отримує value та onValueChange. Це дозволяє перевіряти логіку окремо й повторно використовувати UI. Джерело істини повинно бути визначене; дві незалежні копії одного поля можуть розійтися.

kotlin
@Composable
fun TaskEntry(title: String, onTitleChange: (String) -> Unit, onAdd: () -> Unit) {
    Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
        OutlinedTextField(
            value = title,
            onValueChange = onTitleChange,
            label = { Text("Назва завдання") },
            singleLine = true
        )
        Button(onClick = onAdd, enabled = title.isNotBlank()) {
            Text("Додати")
        }
    }
}

Це фрагмент для Empty Activity Compose проєкту, який потребує imports Compose runtime, foundation layout, Material3 і dp. Тип (String) -> Unit означає функцію, що отримує новий текст і не повертає предметного значення. TaskEntry не змінює стан сам: він повідомляє власника. Disabled button є UI перевіркою; остаточна валідація має залишатися у логіці дії.

Власник стану

kotlin
@Composable
fun TaskEntryRoute() {
    var title by rememberSaveable { mutableStateOf("") }
    var added by rememberSaveable { mutableStateOf(0) }
    Column {
        TaskEntry(title, { title = it }, {
            if (title.isNotBlank()) {
                added += 1
                title = ""
            }
        })
        Text("Додано: $added")
    }
}

Потрібні getValue/setValue для by. Callback перевіряє текст і створює нові значення. Recomposition оновлює залежні Text і поле. У прикладі зберігається лише кількість; реальні назви потребують окремої моделі та збереження. Важливо назвати межу демонстрації, щоб відновлення лічильника не сприймалося як повноцінний список задач.

Layout і Modifier

Column розміщує елементи вертикально, Row — горизонтально, Box — з накладанням. Modifier є послідовністю операцій layout, drawing і input. Порядок padding, size, background і clickable впливає на область та вигляд. Перевіряйте потрібний результат на конкретному компоненті замість припущення, що всі перестановки рівнозначні.

dp задає density-independent розмір, sp враховує масштаб тексту. Для доступності використовуйте підписи, достатню область дотику й семантику. Текстова кнопка вже має назву; іконка без тексту потребує змістовного contentDescription. Декоративне зображення може мати null description, щоб не створювати зайвого озвучення.

Списки та ключі

LazyColumn створює потрібні видимі елементи списку. Стабільний key допомагає пов’язати стан рядка з предметним елементом під час зміни порядку. Позиційний індекс може бути непридатним після вставлення. Ключ має бути унікальним і стабільним у межах колекції.

Якщо звичайний MutableList змінюється без observable механізму, UI може не отримати потрібної recomposition. Використайте новий immutable List у observable holder або підтримувану snapshot state collection із визначеною політикою. Приклад завдань із data class дозволяє оновлювати елемент через copy за ID.

Effects

Побічна дія, наприклад завантаження, не повинна запускатися довільно під час кожного виконання UI тіла. LaunchedEffect запускає coroutine для визначеного ключа й скасовує її при вилученні або зміні ключа. DisposableEffect надає cleanup для підключених ресурсів. Вибір ключа визначає повторення, тому випадковий новий об’єкт як key може створювати зайві запуски.

ViewModel часто координує дані екрана, а UI лише відображає стан. Effect не є універсальним місцем для всієї бізнес-логіки. Потрібно визначити, чи дія належить життєвому циклу UI чи предметному сценарію. Це допомагає уникати дублювання запитів і втрати результату.

Типові помилки та практика

Помилки: мережевий запит у composable тілі, два джерела істини, стан у неправильній позиції, нестабільні keys і !! на введенні. Preview показує заданий стан, але не перевіряє всі transitions. Створюйте кілька previews: порожній, заповнений, помилка та велике масштабування тексту.

Практичний аналіз

Складіть стани поля: порожнє, коректне, помилка після спроби. Визначте події введення й додавання. Як зберегти назви після перезапуску? Який рівень збереження потрібний? Поясніть різницю remember, rememberSaveable і довгострокового сховища.

Рекомпозиція, стан та одноразові ефекти

Composable-функція описує інтерфейс для поточного стану й може виконуватися повторно. Рекомпозиція не є повідомленням про нову подію користувача. Тому запуск мережевого запиту прямо в тілі функції може повторитися при зміні іншого стану. Операцію пов’язують із callback або контрольованим ефектом з явним ключем.

Remember зберігає значення між рекомпозиціями, доки відповідний вузол залишається у композиції. RememberSaveable додатково підтримує відновлення збережуваних значень через механізм saved instance state. Це не постійна база даних: довготривалі записи користувача потрібно зберігати відповідним сховищем. Великі або складні об’єкти не варто без обґрунтування переносити у Bundle.

State hoisting переносить стан до спільного власника й передає UI значення та події. Наприклад, поле отримує text і onTextChange, а власник визначає, як змінити text. Це полегшує preview та перевірку різних станів, бо компонент не залежить від прихованого джерела даних. Важливо зберігати одне узгоджене джерело конкретного факту.

Для сценарію форми випишіть порожній, коректний та помилковий стани. Перевірте доступність кнопки, текст допомоги та повідомлення для кожного. При збільшенні шрифту довгі повідомлення повинні залишатися читабельними; вертикальне компонування й прокручування допомагають уникнути обрізання. Семантика елементів потрібна для озвучування та тестування. Клік по кнопці має створювати одну зрозумілу подію, після якої стан повністю пояснює показаний результат.

Схема процесу

Compose UI: стан, recomposition і керовані подіїОберіть елемент, щоб побачити пояснення

Власник зберігає джерело істини.

Самоперевірка

Перевірте розуміння
Що є безпечним місцем для UI мережевої дії?
Як обґрунтувати відповідь своїми словами?
Composable може повторно виконуватися. Побічні дії потребують явного запуску, скасування та політики повторення.

Підсумок

Compose описує UI через стан і події. Hoisting, стабільні keys та визначені effects роблять поведінку передбачуванішою. Рівень збереження обирають за потрібною тривалістю життя даних.

Джерела

Опрацювали матеріал?

Збережіть цей крок у своєму прогресі.

← Повернутися до дисципліни
© 2026 Анастасія Іскандарова-МалаЕлектронний навчальний посібник · ДДТУ

Пошук у посібнику

Спробуйте «C++», «RAII» або «бази даних».