@Composable: що це, анотація Compose та сфера застосування

Автор: IT Sectr Опубліковано: 2026-06-27 Час читання: 8 хв

Анотація @Composable — фундаментальний елемент Jetpack Compose, який перетворює звичайну функцію Kotlin на декларативний будівельний блок інтерфейсу користувача. Без цієї анотації неможливо створити жоден екран у сучасній Android-розробці. За даними Google Android Developers, 2026, понад 80% нових проєктів на Kotlin використовують Compose для побудови UI, а @Composable є найчастіше використовуваною анотацією в екосистемі.

Головне

  • @Composable — анотація Kotlin, яка дозволяє функції описувати UI декларативно
  • Composable-функції можуть викликати лише інші Composable-функції, дотримуючись контексту композиції
  • Перезапуск Composable-функцій відбувається при зміні вхідних параметрів або стану
  • Порядок виклику Composable-функцій не гарантується — Compose оптимізує перебудову UI
  • Іменування Composable-функцій слідує правилу PascalCase, як і будь-які компоненти в Compose

Що таке @Composable в Jetpack Compose

@Composable — це анотація мови Kotlin, яка маркує функцію як призначену для опису інтерфейсу користувача у фреймворку Jetpack Compose. Коли компілятор Kotlin зустрічає цю анотацію, він генерує додатковий код, що дозволяє функції працювати в контексті композиції — системи керування UI-деревом.

Анотація @Composable була представлена Google у 2021 році разом з першою стабільною версією Jetpack Compose 1.0. До її появи розробка інтерфейсів під Android велася виключно через XML-розмітку та View-систему. @Composable кардинально змінила підхід: замість опису UI в окремому файлі розмітки розробник пише інтерфейс безпосередньо на Kotlin.

Головна відмінність @Composable від звичайних функцій Kotlin — здатність читати та реагувати на зміни стану. Коли змінна, яку читає Composable-функція, змінює значення, система автоматично планує перезапуск (рекомпозицію) цієї функції. Це позбавляє розробника від ручного оновлення UI через findViewById та setText.

Внутрішня механіка @Composable спирається на концепцію слота — спеціальної ділянки пам'яті, яка виділяється для кожної функції в складі композиції. У цьому слоті зберігаються значення, передані у функцію, а також службова інформація, необхідна для порівняння при подальших викликах.

Як оголосити Composable-функцію

Для оголошення Composable-функції достатньо додати анотацію @Composable перед ключовим словом fun. При цьому функція повинна знаходитися в пакеті, що імпортує анотацію з androidx.compose.runtime. Ім'я функції рекомендується писати з великої літери — це загальноприйняте погодження в спільноті Compose, яке візуально відрізняє UI-компоненти від звичайних функцій.

kotlin
import androidx.compose.runtime.Composable

@Composable
fun Greeting(name: String) {
    var count by remember { mutableStateOf(0) }
    Column {
        Text("Вітаю, $name!")
        Button(onClick = { count++ }) {
            Text("Натиснуто $count разів")
        }
    }
}

Параметри Composable-функції можуть бути будь-якими — примітивні типи, рядки, лямбди і навіть інші Composable-функції, що передаються через Slot API. Рекомендується робити параметри імутабельними (val), щоб уникнути побічних ефектів при рекомпозиції. Всі змінювані дані повинні керуватися через state-механізми Compose.

Composable-функції не можуть повертати довільне значення як звичайні функції — їх єдине завдання побудувати або оновити фрагмент UI-дерева. Однак існують спеціальні патерни на кшталт State Hoisting, при якому Compose-функція приймає стан і колбеки через параметри, залишаючись чистою та перевикористовуваною.

Правила Composable-функцій в Kotlin

Система Compose накладає кілька суворих обмежень на те, як повинні виглядати та поводитися Composable-функції. Перше правило: Composable-функція може викликати лише інші Composable-функції або звичайні функції, що не мають побічних ефектів. Це забезпечує передбачуваність композиції та коректну роботу оптимізацій Compose.

Друге правило стосується порядку виконання. Compose має право викликати Composable-функції в будь-якому порядку, тому код у тілі такої функції не повинен покладатися на послідовність виклику сусідніх функцій. Кожна Composable-функція має бути самодостатньою на рівні своєї позиції в UI-дереві.

Третє правило — заборона побічних ефектів всередині самого тіла Composable-функції. Операції на кшталт запису в базу даних, відправлення мережевих запитів або зміни зовнішніх змінних повинні виконуватися лише всередині спеціальних ефектів LaunchedEffect, DisposableEffect або SideEffect. Порушення цього правила призводить до непередбачуваної поведінки при рекомпозиціях.

Четверте правило: Composable-функції повинні бути ідемпотентними. Повторний виклик з тими ж аргументами повинен виробляти той же UI. Ця вимога необхідна для коректної роботи skipping-оптимізації, при якій Compose пропускає перемальовування функцій, чиї вхідні дані не змінилися.

kotlin
// Правильно: чиста Composable-функція без побічних ефектів
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
    Card(modifier = Modifier.clickable { onClick() }) {
        Text(text = user.name)
    }
}

// Неправильно: побічний ефект всередині тіла
@Composable
fun WrongCard(userId: String) {
    // val result = viewModel.loadUser(userId)  // НЕ ДОЗВОЛЕНО
    Text("Завантаження...")
}

Приклади використання @Composable

Розглянемо практичний приклад створення екрану профілю з використанням @Composable анотації. Тут ми демонструємо комбінування кількох Composable-функцій, роботу зі станом та модифікаторами — ключовими елементами будь-якої Compose-розмітки.

kotlin
@Composable
fun ProfileScreen(userId: String) {
    var isFollowed by remember { mutableStateOf(false) }

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        ProfileHeader(userId = userId)
        Spacer(modifier = Modifier.height(16.dp))
        StatsRow(posts = 42, followers = 1280)
        Spacer(modifier = Modifier.height(24.dp))
        FollowButton(
            isFollowed = isFollowed,
            onToggle = { isFollowed = !isFollowed }
        )
    }
}

@Composable
fun ProfileHeader(userId: String) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        AsyncImage(model = "https://example.com/avatars/$userId",
            contentDescription = "User avatar")
        Spacer(modifier = Modifier.width(12.dp))
        Text(text = "Користувач #$userId", style = MaterialTheme.typography.headlineMedium)
    }
}

@Composable
fun StatsRow(posts: Int, followers: Int) {
    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
        StatItem("Posts", posts)
        StatItem("Followers", followers)
    }
}

@Composable
fun StatItem(label: String, value: Int) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(text = "$value", style = MaterialTheme.typography.headlineSmall)
        Text(text = label, style = MaterialTheme.typography.bodySmall)
    }
}

У прикладі кожна Composable-функція відповідає за свою частину екрану: ProfileScreen керує загальним станом і композицією дочірніх функцій, ProfileHeader відображає аватар та ім'я, а StatsRow — блок зі статистикою. Такий підхід слідує принципу єдиної відповідальності та спрощує повторне використання компонентів.

Типи Composable-функцій та їх призначення

У Jetpack Compose існує три основні типи Composable-функцій. Перший тип — контейнери (Row, Column, Box, LazyColumn), які визначають розташування дочірніх елементів. Другий — елементи відображення (Text, Image, Icon, Button), які рендерять конкретні UI-компоненти. Третій — користувацькі Composable-функції, які комбінують вбудовані компоненти у перевикористовувані блоки.

Контейнери відрізняються від звичайних елементів тим, що приймають content-лямбду — останній параметр типу @Composable () -> Unit. Саме цей механізм дозволяє будувати вкладені UI-дерева: кожен контейнер породжує дочірню композицію зі своїм контекстом та областю пам'яті.

Користувацькі Composable-функції поділяються на два підтипи: розумні (smart) і прості (dumb). Розумні функції керують станом та логікою — вони містять виклики remember, LaunchedEffect та інших API Compose. Прості функції приймають всі дані через параметри і лише відображають їх. Розділення на розумні та прості компоненти покращує тестованість та перевикористання коду.

ТипПрикладПризначення
КонтейнерColumn, Row, BoxКерування розташуванням дочірніх елементів
ЕлементText, Image, ButtonВідображення контенту та обробка введення
КористувацькийProfileCard, UserListКомбінація стандартних компонентів

@Composable та повторне використання компонентів

Головна перевага @Composable анотації — можливість створювати перевикористовувані UI-компоненти без успадкування та складних ієрархій класів. На відміну від View-системи, де кожен кастомний елемент вимагав створення Java-класу з конструкторами, Composable-компонент — це просто функція Kotlin з параметрами.

Для забезпечення перевикористання застосовується патерн Slot API, при якому Composable-функція приймає content-лямбди для різних областей свого макету. Наприклад, компонент Card може приймати окремі content для заголовка, тіла та нижньої частини, що робить його універсальним для будь-яких екранів застосунку.

Модифікатори (Modifier) відіграють ключову роль у перевикористанні: вони дозволяють налаштовувати відступи, розміри, кліки та анімації без зміни самого компонента. Рекомендується завжди передавати Modifier як параметр Composable-функції за замовчуванням: Modifier = Modifier — це стандартна практика, прийнята в офіційних бібліотеках Google.

kotlin
@Composable
fun SectionCard(
    modifier: Modifier = Modifier,
    title: String,
    content: @Composable () -> Unit
) {
    Card(modifier = modifier) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = title, style = MaterialTheme.typography.titleMedium)
            Spacer(modifier = Modifier.height(8.dp))
            content()
        }
    }
}

Завдяки Slot API компонент SectionCard можна використовувати на різних екранах з різним вмістом — формами, списками, текстовими блоками. Комбінування модифікаторів та Slot API робить Compose-компоненти максимально гнучкими без втрати типобезпеки, яку забезпечує Kotlin.

Часті запитання

Чим @Composable відрізняється від звичайної функції Kotlin?

@Composable функція виконується в контексті композиції та може читати стан, автоматично перезапускаючись при його зміні. Звичайні функції Kotlin не мають доступу до механізмів відстеження стану та не беруть участі в побудові UI-дерева.

Чи можна викликати Composable-функцію зі звичайної функції?

Ні, Composable-функції можна викликати лише з інших Composable-функцій, оскільки потрібен спеціальний контекст композиції. Для інтеграції Compose-коду зі звичайним Kotlin використовується метод setContent { } у Activity або ComposeView у View-системі.

Чому Composable-функції пишуть з великої літери?

Це угода про іменування, прийнята в спільноті Compose. Велика літера візуально відрізняє UI-компоненти від звичайних функцій, слідуючи правилам іменування класів. Це не вимога компілятора, але рекомендована практика в документації Google.

Скільки Composable-функцій може бути на одному екрані?

Обмежень на кількість немає. На практиці великий екран може містити 50–100 Composable-функцій, включаючи вбудовані компоненти (Text, Button) та користувацькі. Compose оптимізує дерево функцій та виконує лише ті, чиї вхідні дані змінилися.

Чи може Composable-функція повертати значення?

Зазвичай Composable-функції повертають Unit, оскільки їх завдання — побудувати UI. Однак існують спеціалізовані функції на кшталт remember та derivedStateOf, які позначені @Composable і повертають значення. Це виняток, а не правило.

Підсумки

  • @Composable — анотація для декларативного опису UI в Jetpack Compose
  • Composable-функції викликаються лише всередині інших Composable-функцій у спеціальному контексті
  • Ідемпотентність — кожне повторне виконання з тими ж аргументами дає однаковий UI
  • Побічні ефекти заборонені в тілі функції — лише через LaunchedEffect та SideEffect
  • Slot API та Modifier забезпечують перевикористання компонентів без успадкування
  • Container-функції (Row, Column, LazyColumn) приймають content-лямбди для вкладених елементів
  • Рекомендація: передавайте Modifier як параметр кожної користувацької Composable-функції з дефолтним значенням

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також