Compose Multiplatform для Kotlin: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Compose Multiplatform для Kotlin: Полное руководство

Добро пожаловать в самое подробное руководство по Compose Multiplatform (CMP) — современной технологии от JetBrains, которая позволяет создавать нативные пользовательские интерфейсы для Android, iOS, Desktop (Windows, macOS, Linux) и Web, используя единый код на языке Kotlin. В этой статье мы разберем всё: от установки до продвинутых техник и лучших практик.

1. Что это и зачем нужна?

Compose Multiplatform — это декларативный фреймворк для построения пользовательских интерфейсов, основанный на Jetpack Compose от Google, но расширенный для работы на нескольких платформах. В отличие от традиционных подходов (например, отдельные XML-верстки для Android и SwiftUI для iOS), CMP позволяет писать UI-логику один раз и компилировать её в нативный код для каждой целевой платформы.

Зачем это нужно? Во-первых, это радикальное сокращение времени разработки: вместо поддержки двух или более кодовых баз вы работаете с одной. Во-вторых, консистентность UI: бизнес-логика и внешний вид приложения идентичны на всех платформах, что упрощает тестирование и поддержку. В-третьих, доступ к нативным API: вы можете использовать platform-specific код (например, камеру или геолокацию) через механизм expect/actual, не покидая экосистему Kotlin.

Compose Multiplatform идеально подходит для стартапов, продуктовых команд и разработчиков, которые хотят охватить максимальную аудиторию без дублирования усилий. Библиотека активно развивается JetBrains и сообществом, и на 2025 год является одним из самых перспективных решений в мире кроссплатформенной разработки.

2. Установка

Поскольку Compose Multiplatform — это не библиотека в традиционном смысле (как pip-пакет), а скорее набор плагинов и конфигураций для Gradle, установка производится через систему сборки Kotlin Multiplatform. Вот точная последовательность действий для создания нового проекта:

Шаг 1: Создание проекта через IntelliJ IDEA (рекомендуется)

Откройте IntelliJ IDEA (Community или Ultimate), выберите File → New → Project. В мастере выберите шаблон Compose Multiplatform. Укажите имя проекта и нажмите "Finish". IDE автоматически сгенерирует структуру и все необходимые зависимости.

Шаг 2: Ручная настройка (для существующих проектов)

Добавьте в корневой build.gradle.kts следующий код:

plugins {
    kotlin("multiplatform") version "2.0.21"
    id("org.jetbrains.compose") version "1.7.1"
    id("org.jetbrains.kotlin.plugin.compose") version "2.0.21"
}

kotlin {
    androidTarget()
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = true
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material3)
                implementation(compose.ui)
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("androidx.activity:activity-compose:1.9.3")
            }
        }
    }
}

Важно: Версии плагинов и Kotlin должны быть совместимы. Актуальные версии всегда можно найти на GitHub репозитории Compose Multiplatform.

3. Быстрый старт — минимальный рабочий пример

После создания проекта откройте файл commonMain/kotlin/App.kt и замените его содержимое на следующий код:

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun App() {
    var count by remember { mutableStateOf(0) }
    MaterialTheme {
        Column(
            modifier = Modifier.fillMaxSize().padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text("Вы нажали кнопку $count раз")
            Spacer(Modifier.height(16.dp))
            Button(onClick = { count++ }) {
                Text("Нажми меня")
            }
        }
    }
}

Этот код создаёт простейший счётчик: текст, отображающий количество нажатий, и кнопку. Чтобы запустить приложение на Android, используйте таск ./gradlew installDebug; для Desktop — ./gradlew run; для iOS — откройте Xcode проект и запустите симулятор.

4. Основные методы, функции и классы

Ниже приведены ключевые компоненты Compose Multiplatform с сигнатурами, описанием и примерами. Все примеры можно скопировать и вставить в ваш проект.

4.1. @Composable

Сигнатура: @Composable annotation

Что делает: Аннотация, которая указывает, что функция является компонуемой — то есть может быть вызвана только из контекста композиции и может читать/изменять состояние.

Пример:

@Composable
fun Greeting(name: String) {
    Text("Привет, $name!")
}

4.2. Column

Сигнатура: @Composable fun Column( modifier: Modifier = Modifier, verticalArrangement: Arrangement.Vertical = Arrangement.Top, horizontalAlignment: Alignment.Horizontal = Alignment.Start, content: @Composable ColumnScope.() -> Unit )

Что делает: Размещает дочерние элементы вертикально сверху вниз. Аналог LinearLayout с вертикальной ориентацией.

Пример:

Column(
    modifier = Modifier.fillMaxWidth().padding(16.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp)
) {
    Text("Первый элемент")
    Text("Второй элемент")
    Text("Третий элемент")
}

4.3. Row

Сигнатура: @Composable fun Row( modifier: Modifier = Modifier, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, content: @Composable RowScope.() -> Unit )

Что делает: Размещает дочерние элементы горизонтально слева направо. Аналог LinearLayout с горизонтальной ориентацией.

Пример:

Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.SpaceEvenly
) {
    Button(onClick = { /* действие 1 */ }) { Text("A") }
    Button(onClick = { /* действие 2 */ }) { Text("B") }
    Button(onClick = { /* действие 3 */ }) { Text("C") }
}

4.4. Box

Сигнатура: @Composable fun Box( modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.TopStart, propagateMinConstraints: Boolean = false, content: @Composable BoxScope.() -> Unit )

Что делает: Контейнер, который накладывает дочерние элементы друг на друга (как FrameLayout). Полезно для создания наложений, бейджей и т.д.

Пример:

Box(modifier = Modifier.size(200.dp)) {
    Image(
        painter = painterResource(Res.drawable.background),
        contentDescription = "Фон",
        modifier = Modifier.fillMaxSize()
    )
    Text(
        "Текст поверх картинки",
        modifier = Modifier.align(Alignment.Center),
        color = Color.White
    )
}

4.5. remember

Сигнатура: @Composable inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T

Что делает: Сохраняет значение между рекомпозициями. Если ключи (переданные как аргументы) не изменились, возвращает сохранённое значение, иначе пересчитывает.

Пример:

val randomNumber = remember { Random.nextInt(1, 100) }
Text("Случайное число: $randomNumber") // не изменится при рекомпозиции

4.6. mutableStateOf

Сигнатура: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()): MutableState<T>

Что делает: Создаёт наблюдаемое состояние. При изменении значения все компонуемые функции, которые его читают, автоматически перерисовываются.

Пример:

var text by remember { mutableStateOf("") }
TextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Введите текст") }
)

4.7. LaunchedEffect

Сигнатура: @Composable fun LaunchedEffect( vararg keys: Any?, block: suspend CoroutineScope.() -> Unit )

Что делает: Запускает корутину в области композиции. Автоматически отменяет её при выходе из композиции или изменении ключей. Идеально для side-эффектов (загрузка данных, таймеры).

Пример:

var seconds by remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
    while (true) {
        delay(1000)
        seconds++
    }
}
Text("Прошло $seconds секунд")

4.8. Modifier

Сигнатура: sealed class Modifier (основные методы: .fillMaxSize(), .padding(), .clickable(), .background(), .size() и т.д.)

Что делает: Объект, который модифицирует внешний вид и поведение компонуемого элемента. Модификаторы применяются цепочкой.

Пример:

Text(
    "Нажми на меня",
    modifier = Modifier
        .clickable { println("Клик!") }
        .padding(16.dp)
        .background(Color.LightGray, shape = RoundedCornerShape(8.dp))
        .fillMaxWidth()
)

4.9. LazyColumn

Сигнатура: @Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = Arrangement.Top, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit )

Что делает: Вертикальный список с ленивой загрузкой элементов. Отображает только видимые элементы, что критично для производительности при больших списках.

Пример:

val items = (1..1000).toList()
LazyColumn {
    items(items) { item ->
        Text("Элемент #$item", modifier = Modifier.padding(8.dp))
    }
}

4.10. Scaffold

Сигнатура: @Composable fun Scaffold( modifier: Modifier = Modifier, topBar: @Composable () -> Unit = {}, bottomBar: @Composable () -> Unit = {}, snackbarHost: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {}, content: @Composable (PaddingValues) -> Unit )

Что делает: Реализует базовую структуру Material Design: верхняя панель, нижняя панель, плавающая кнопка, снэкбар. Автоматически управляет отступами.

Пример:

Scaffold(
    topBar = {
        TopAppBar(title = { Text("Мое приложение") })
    },
    floatingActionButton = {
        FloatingActionButton(onClick = { /* действие */ }) {
            Icon(Icons.Default.Add, contentDescription = "Добавить")
        }
    }
) { paddingValues ->
    Text("Контент", modifier = Modifier.padding(paddingValues))
}

5. Реальный практический пример (100-200 строк кода)

Давайте создадим полноценное приложение — список задач (Todo List) с возможностью добавления, удаления и отметки выполнения. Этот пример демонстрирует работу с состоянием, списками, вводом текста и модификаторами.

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp

data class TodoItem(
    val id: Int,
    val text: String,
    val isCompleted: Boolean = false
)

@Composable
fun TodoApp() {
    var todos by remember { mutableStateOf(listOf<TodoItem>                    

Рекомендаций