Какие UI-компоненты можно сделать общими для Android и Desktop в KMP проекте
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Общие UI-компоненты в KMP для Android и Desktop
В KMP (Kotlin Multiplatform) проектах с использованием Compose Multiplatform можно создавать кроссплатформенные UI-компоненты, которые работают на Android, Desktop (и других платформах). Ключевой принцип — разделение кода на common (общую) и platform-specific (платформенно-специфическую) части. Вот основные категории компонентов, которые эффективно обобщаются:
1. Базовые компоненты композиции
Это примитивы Compose, которые доступны на всех платформах и составляют основу UI:
// Общий код в commonMain
@Composable
fun CustomButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Button(
onClick = onClick,
modifier = modifier,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
)
) {
Text(text = text)
}
}
- Text, Button, TextField — базовые элементы ввода и отображения
- Column, Row, Box — контейнеры для компоновки
- Image (с загрузкой через общие ресурсы)
- Card, Surface — контейнеры с оформлением
2. Составные компоненты и виджеты
Более сложные компоненты, объединяющие несколько базовых:
- Диалоги и алерты с кастомным содержимым
- Списки (LazyColumn/LazyRow) с общим механизмом отображения данных
- Панели навигации и верхние панели (TopAppBar)
- Вкладки (TabRow) с переключаемым контентом
- Индикаторы загрузки и состояния пустых экранов
3. Компоненты с состоянием и логикой
Компоненты, инкапсулирующие бизнес-логику через Compose ViewModel или StateFlow:
// Общая ViewModel в commonMain
class SearchViewModel() : ViewModel() {
private val _searchText = MutableStateFlow("")
val searchText: StateFlow<String> = _searchText
fun onSearchTextChanged(text: String) {
_searchText.value = text
}
}
// Компонент поиска
@Composable
fun SearchBar(viewModel: SearchViewModel) {
val searchText by viewModel.searchText.collectAsState()
OutlinedTextField(
value = searchText,
onValueChange = viewModel::onSearchTextChanged,
label = { Text("Поиск") }
)
}
4. Стилизованные тематические компоненты
Компоненты, использующие общую Design System:
// Общая тема в commonMain
object AppTheme {
val colors = AppColors(
primary = Color(0xFF6200EE),
surface = Color(0xFFFFFFFF)
)
val typography = AppTypography(
h1 = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold)
)
}
@Composable
fun ThemedCard(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Card(
modifier = modifier,
backgroundColor = AppTheme.colors.surface,
elevation = 4.dp
) {
content()
}
}
Практические рекомендации по организации
Структура модулей:
shared/
├── commonMain/
│ ├── ui/ # Общие компоненты
│ ├── theme/ # Общая тема
│ └── di/ # Общая DI
├── androidMain/ # Android-специфичные реализации
└── desktopMain/ # Desktop-специфичные реализации
Что НЕ стоит обобщать:
- Нативные контролы, специфичные для платформ (например, BottomSheet на Android может отличаться от Desktop)
- Компоненты с платформенно-зависимыми жестами (долгое нажатие, масштабирование)
- Элементы, требующие платформенных API (камера, GPS, файловая система)
Паттерны для работы с платформенными отличиями:
// Использование expect/actual для платформенных реализаций
expect class PlatformIcon
@Composable
expect fun PlatformSpecificComponent(
modifier: Modifier = Modifier
)
// В Android-модуле
actual class PlatformIcon(context: Context) {
// Android-специфичная реализация
}
@Composable
actual fun PlatformSpecificComponent(modifier: Modifier) {
// Android-версия компонента
}
Преимущества общего подхода:
- Единая кодовая база — сокращение дублирования кода на 70-80%
- Согласованный UX — идентичное поведение на всех платформах
- Ускорение разработки — изменения применяются сразу везде
- Упрощённое тестирование — общие UI-тесты для всех платформ
Ограничения и решения:
- Различия в input — используйте абстракции для обработки ввода
- Разные гайдлайны — создавайте адаптивные компоненты с модификаторами
- Производительность — тестируйте на целевых платформах
В качестве инструментов рекомендую: Compose Multiplatform для UI, MVIKotlin/ Decompose для архитектуры, Koin или Kodein-DI для dependency injection. Начните с простых stateless-компонентов, постепенно переходя к сложным stateful-виджетам, всегда проверяя рендеринг на каждой целевой платформе.