Что такое CompositionLocal в Jetpack Compose?
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое CompositionLocal?
CompositionLocal — это механизм в Jetpack Compose для неявной передачи данных через дерево композиции без необходимости явно передавать их через параметры каждой составной функции (composable). Это аналог Context в традиционном Android View-мире или Dependency Injection в других фреймворках, но адаптированный для реактивной парадигмы Compose.
Основная проблема, которую решает CompositionLocal
Представьте, что у вас есть глубокое дерево composable-функций, и корневому компоненту нужно передать данные (например, тему, конфигурацию локали, ресурсы) глубоко вложенному дочернему компоненту. Без CompositionLocal вам пришлось бы передавать эти данные через параметры всех промежуточных composable-функций, даже если они сами их не используют — это называется "prop drilling".
Пример проблемы (без CompositionLocal):
@Composable
fun App(userPreferences: UserPreferences, theme: Theme, locale: Locale) {
MainScreen(userPreferences, theme, locale)
}
@Composable
fun MainScreen(userPreferences: UserPreferences, theme: Theme, locale: Locale) {
// Не использует theme, но должен передать её дальше
UserProfile(userPreferences, theme, locale)
}
@Composable
fun UserProfile(userPreferences: UserPreferences, theme: Theme, locale: Locale) {
// Использует только locale, но получил все параметры
Text(text = "Hello", color = theme.primary, fontSize = locale.fontSize)
}
Как работает CompositionLocal
CompositionLocal создаёт скопированное значение, доступное всем composable-функциям вниз по дереву композиции от точки его предоставления. Он состоит из двух ключевых частей:
- CompositionLocal<T> — контейнер, объявляющий тип данных.
- Provider (
CompositionLocalProvider) — компонент, который предоставляет значение для этого контейнера.
Пример создания и использования:
// 1. Создание CompositionLocal (обычно в object или верхнем уровне)
val LocalAppTheme = compositionLocalOf<Theme> {
error("Theme не предоставлен")
}
val LocalLocale = staticCompositionLocalOf<Locale> {
Locale.getDefault()
}
// 2. Предоставление значения в корне или конкретной части дерева
@Composable
fun App() {
CompositionLocalProvider(
LocalAppTheme provides DarkTheme(),
LocalLocale provides Locale("ru")
) {
MainScreen() // Больше не передаём theme и locale явно
}
}
// 3. Использование в любом дочернем composable без явной передачи
@Composable
fun UserProfile() {
val theme = LocalAppTheme.current // Получаем текущее значение
val locale = LocalLocale.current
Text(
text = "Привет",
color = theme.primary,
fontSize = locale.fontSize
)
}
Ключевые особенности и лучшие практики
- Типы CompositionLocal:
* **`compositionLocalOf`** — пересчитывает подписчиков при изменении значения
* **`staticCompositionLocalOf`** — пересчитывает ВСЕ дочерние composables при изменении (более эффективен для редко меняющихся данных)
- Область применения — CompositionLocal следует использовать для данных, которые действительно являются скопированными (глобальными в пределах поддерева):
* Тема оформления (`LocalContext`, `LocalConfiguration`)
* Ресурсы (`LocalContext.resources`)
* Навигация (в некоторых архитектурах)
* Состояние воспроизведения медиа
* Пользовательские предпочтения
- Чего следует избегать:
* Не используйте для передачи бизнес-логики или состояний, специфичных для конкретного экрана
* Не злоупотребляйте — чрезмерное использование усложняет понимание потока данных
* Не используйте как замену правильно организованному состоянию (`ViewModel`, `StateFlow`)
Встроенные CompositionLocal в Compose
Jetpack Compose предоставляет множество готовых CompositionLocal:
@Composable
fun Example() {
val context = LocalContext.current
val configuration = LocalConfiguration.current
val density = LocalDensity.current
val focusManager = LocalFocusManager.current
// Использование встроенных значений
Button(onClick = { /* */ }) {
Text("Кнопка")
}
}
Когда использовать CompositionLocal
- Тема и стилизация — самый распространённый случай
- Локализация и ресурсы
- Доступ к системным сервисам (Context, Resources)
- Конфигурация устройства (плотность пикселей, размер экрана)
- Логирование и мониторинг (передача логгера или аналитики)
Важное замечание: Современные практики рекомендуют использовать CompositionLocal вместе с Dependency Injection (например, через Hilt), где DI-фреймворк предоставляет зависимости, а CompositionLocal распространяет их по дереву Compose.
Пример с пользовательской темой
// Объявление своей темы
data class CustomTheme(
val primaryColor: Color,
val typography: Typography,
val padding: Dp
)
val LocalCustomTheme = compositionLocalOf<CustomTheme> {
error("CustomTheme не предоставлена")
}
// Предоставление
@Composable
fun MyApp() {
val myTheme = CustomTheme(
primaryColor = Color.Blue,
typography = Typography(),
padding = 16.dp
)
CompositionLocalProvider(LocalCustomTheme provides myTheme) {
Content()
}
}
// Использование
@Composable
fun Content() {
val theme = LocalCustomTheme.current
Column(
modifier = Modifier.padding(theme.padding)
) {
Text(
text = "Заголовок",
style = theme.typography.h1,
color = theme.primaryColor
)
}
}
CompositionLocal — это мощный инструмент, который при правильном использовании значительно упрощает архитектуру Compose-приложений, уменьшая шаблонный код и делая данные доступными именно там, где они нужны.