@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("Hello, $name!")
        Button(onClick = { count++ }) {
            Text("Clicked $count times")
        }
    }
}

Параметры 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
// Correct: pure Composable function without side effects
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
    Card(modifier = Modifier.clickable { onClick() }) {
        Text(text = user.name)
    }
}

// Wrong: side effect inside the body
@Composable
fun WrongCard(userId: String) {
    // val result = viewModel.loadUser(userId)  // NOT ALLOWED
    Text("Loading...")
}

Примеры использования @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 = "User #$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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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