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

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

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

Начать курс

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

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

Accompanist — это коллекция библиотек-дополнений (extensions) для Jetpack Compose, разработанная командой Google. Она предоставляет готовые решения для задач, которые либо ещё не вошли в стабильный API Compose, либо требуют сложной реализации "с нуля". Библиотека решает проблемы, с которыми сталкивается каждый разработчик Compose: работа с жестами, системными панелями, анимациями, веб-просмотрами, адаптацией под разные экраны и многое другое.

Зачем она нужна? Во-первых, Accompanist экономит время. Вместо того чтобы писать собственный обработчик жестов или кастомный модификатор для системных панелей, вы используете готовый, протестированный компонент. Во-вторых, библиотека следует официальным рекомендациям Google и часто становится прототипом для будущих стабильных API Compose. Например, HorizontalPager из Accompanist позже был добавлен в Compose Foundation как HorizontalPager.

На момент написания статьи Accompanist включает такие модули, как: systemuicontroller (управление статус-баром и навигацией), permissions (запрос разрешений в рантайме), webview (встраивание WebView), pager (горизонтальная/вертикальная прокрутка страниц), swiperefresh (жест pull-to-refresh), placeholder (скелетон-загрузка), adaptive (адаптивные лейауты), navigation-animation (анимации переходов) и другие.

2. Установка

Accompanist — это библиотека для Gradle (Android/Kotlin Multiplatform). Установка производится через добавление зависимостей в файл build.gradle.kts (или build.gradle) вашего модуля. Команды pip, npm, cargo или go get не используются, так как это не Python, JavaScript, Rust или Go.

Точная команда для добавления конкретного модуля (например, systemuicontroller):

// build.gradle.kts (Module)
dependencies {
    implementation("com.google.accompanist:accompanist-systemuicontroller:0.34.0")
    // Для других модулей:
    // implementation("com.google.accompanist:accompanist-permissions:0.34.0")
    // implementation("com.google.accompanist:accompanist-pager:0.34.0")
    // implementation("com.google.accompanist:accompanist-swiperefresh:0.34.0")
    // implementation("com.google.accompanist:accompanist-webview:0.34.0")
    // implementation("com.google.accompanist:accompanist-placeholder:0.34.0")
}

Важно: проверяйте актуальную версию на GitHub Releases. Версия 0.34.0 — последняя стабильная на момент написания статьи. Убедитесь, что ваша версия Kotlin и Compose совместимы с выбранной версией Accompanist.

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

Создадим экран, который меняет цвет статус-бара при нажатии на кнопку. Для этого используем модуль systemuicontroller.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.google.accompanist.systemuicontroller.rememberSystemUiController

@Composable
fun QuickStartExample() {
    val systemUiController = rememberSystemUiController()
    var isDark by remember { mutableStateOf(false) }

    // Устанавливаем цвет статус-бара в зависимости от состояния
    LaunchedEffect(isDark) {
        if (isDark) {
            systemUiController.setStatusBarColor(Color.Black)
        } else {
            systemUiController.setStatusBarColor(Color.White)
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(if (isDark) Color.DarkGray else Color.White),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally
    ) {
        Button(onClick = { isDark = !isDark }) {
            Text("Переключить цвет статус-бара")
        }
    }
}

Этот пример показывает, как легко управлять системными панелями с помощью Accompanist. Всего несколько строк кода — и вы меняете цвет статус-бара без написания платформенного кода.

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

Рассмотрим 8 ключевых компонентов Accompanist. Для каждого приведена сигнатура, описание и рабочий пример.

4.1. rememberSystemUiController()

Сигнатура: @Composable fun rememberSystemUiController(): SystemUiController

Что делает: Создаёт и запоминает экземпляр SystemUiController, который позволяет управлять цветом и прозрачностью статус-бара, навигационной панели и системными жестами.

Пример:

val controller = rememberSystemUiController()
// Устанавливаем тёмные иконки на светлом фоне
controller.setStatusBarColor(Color.White, darkIcons = true)
// Устанавливаем прозрачный статус-бар
controller.setStatusBarColor(Color.Transparent)

4.2. rememberPermissionState()

Сигнатура: @Composable fun rememberPermissionState(permission: String): PermissionState

Что делает: Управляет состоянием запроса одного разрешения (например, камера, местоположение). Возвращает объект PermissionState с методами launchPermissionRequest() и свойствами status, shouldShowRationale.

Пример:

val permissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
if (permissionState.status.isGranted) {
    Text("Разрешение получено")
} else {
    Button(onClick = { permissionState.launchPermissionRequest() }) {
        Text("Запросить разрешение")
    }
}

4.3. HorizontalPager

Сигнатура: @Composable fun HorizontalPager(count: Int, state: PagerState, modifier: Modifier, content: @Composable (page: Int) -> Unit)

Что делает: Создаёт горизонтальный контейнер с постраничной прокруткой. Поддерживает индикаторы, анимации и кастомные переходы.

Пример:

val pagerState = rememberPagerState(pageCount = { 3 })
HorizontalPager(state = pagerState) { page ->
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(200.dp)
            .background(Color(page * 100, page * 50, 255)),
        contentAlignment = Alignment.Center
    ) {
        Text("Страница ${page + 1}", color = Color.White)
    }
}
// Индикатор страниц
HorizontalPagerIndicator(pagerState = pagerState)

4.4. rememberSwipeRefreshState()

Сигнатура: @Composable fun rememberSwipeRefreshState(isRefreshing: Boolean): SwipeRefreshState

Что делает: Создаёт состояние для жеста pull-to-refresh. Оборачивает контент в SwipeRefresh и позволяет запускать обновление данных.

Пример:

var isRefreshing by remember { mutableStateOf(false) }
val state = rememberSwipeRefreshState(isRefreshing)
SwipeRefresh(state = state, onRefresh = {
    isRefreshing = true
    // Запускаем загрузку данных
    viewModel.refresh()
    isRefreshing = false
}) {
    LazyColumn {
        items(20) { index ->
            Text("Элемент $index", modifier = Modifier.padding(16.dp))
        }
    }
}

4.5. rememberWebViewState()

Сигнатура: @Composable fun rememberWebViewState(url: String): WebViewState

Что делает: Создаёт состояние для WebView. Позволяет загружать URL, управлять навигацией и отслеживать загрузку.

Пример:

val webViewState = rememberWebViewState("https://example.com")
AndroidView(factory = { context ->
    WebView(context).apply {
        settings.javaScriptEnabled = true
        webViewClient = WebViewClient()
    }
}, update = { it.loadUrl(webViewState.url) })

4.6. placeholder() модификатор

Сигнатура: fun Modifier.placeholder(visible: Boolean, color: Color, shape: Shape): Modifier

Что делает: Добавляет эффект скелетон-загрузки (placeholder) к любому composable. Пока данные загружаются, отображается анимированная заглушка.

Пример:

var isLoading by remember { mutableStateOf(true) }
Box(
    modifier = Modifier
        .size(100.dp)
        .placeholder(
            visible = isLoading,
            color = Color.LightGray,
            shape = RoundedCornerShape(8.dp)
        )
) {
    if (!isLoading) {
        Text("Загруженные данные")
    }
}

4.7. AnimatedNavigation (модуль navigation-animation)

Сигнатура: fun NavController.navigate(route: String, animBuilder: NavAnimBuilder)

Что делает: Добавляет анимации при навигации между экранами. Позволяет задать анимацию входа, выхода, а также анимацию для pop.

Пример:

// В NavHost используем AnimatedNavHost
AnimatedNavHost(navController, startDestination = "home") {
    composable("home", enterTransition = { slideInHorizontally() }) {
        HomeScreen()
    }
    composable("details", enterTransition = { fadeIn() }) {
        DetailsScreen()
    }
}

4.8. FlowLayout (модуль flow)

Сигнатура: @Composable fun FlowLayout(mainAxisSize: SizeMode, mainAxisSpacing: Dp, crossAxisSpacing: Dp, content: @Composable FlowScope.() -> Unit)

Что делает: Реализует "текучий" лейаут, где элементы переносятся на новую строку, если не помещаются. Аналог FlexWrap во Flutter или CSS Flexbox.

Пример:

FlowLayout(
    mainAxisSpacing = 8.dp,
    crossAxisSpacing = 8.dp
) {
    repeat(10) {
        Chip(onClick = { }, label = { Text("Тег $it") })
    }
}

5. Реальный практический пример

Создадим экран профиля пользователя с загрузкой данных, pull-to-refresh, скелетон-загрузкой и управлением системными панелями. Пример объединяет несколько модулей Accompanist.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.placeholder.PlaceholderHighlight
import com.google.accompanist.placeholder.material.placeholder
import com.google.accompanist.placeholder.material.shimmer
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.delay

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                ProfileScreen()
            }
        }
    }
}

@Composable
fun ProfileScreen() {
    // Управление системными панелями
    val systemUiController = rememberSystemUiController()
    LaunchedEffect(Unit) {
        systemUiController.setStatusBarColor(Color.Transparent, darkIcons = false)
        systemUiController.setNavigationBarColor(Color.Transparent)
    }

    // Состояние загрузки и обновления
    var isLoading by remember { mutableStateOf(true) }
    var isRefreshing by remember { mutableStateOf(false) }
    var userName by remember { mutableStateOf("") }
    var userBio by remember { mutableStateOf("") }

    // Симуляция загрузки данных
    LaunchedEffect(Unit) {
        delay(2000)
        userName = "Иван Петров"
        userBio = "Android-разработчик, люблю Kotlin и Compose"
        isLoading = false
    }

    val swipeRefreshState = rememberSwipeRefreshState(isRefreshing)

    SwipeRefresh(                    

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