Irbisa · Шпаргалка
Compose Multiplatform
Compose для iOS/desktop/web, ресурсы, навигация, мост с платформенным UI
Что такое Compose Multiplatform и как он связан с Jetpack Compose?
Jetpack Compose называют Android-тулкитом для UI. Как тогда тот же исходник вообще рисуется на iOS, из каких слоёв всё состоит?
// Proof that the runtime is not a UI toolkit: compose a plain tree, no compose.ui at all.class Node(val name: String) { val children = mutableListOf<Node>()} class NodeApplier(root: Node) : AbstractApplier<Node>(root) {…В CMP-проекте один
App()в общем коде. Что нужно каждой платформе, чтобы вывести его на экран?// commonMain@Composablefun App(startRoute: String = "home") { AppTheme { AppNavHost(startRoute) }}…Где в проекте Compose Multiplatform лежат картинки, строки и шрифты и как на них ссылаться?
@Composablefun Header(name: String) { Image(painterResource(Res.drawable.logo), contentDescription = null) Text(stringResource(Res.string.hello, name)) Text("Branded", fontFamily = FontFamily(Font(Res.font.sora_bold)))}…Этот composable компилировался в
androidMain, но не собирается вcommonMain. Какие Android-only API он использует и чем заменить каждое?@Composablefun ShareRow(text: String) { val context = LocalContext.current Row { Image(painterResource(R.drawable.ic_share), contentDescription = null) Text(stringResource(R.string.share_title)) Button(onClick = { val send = Intent(Intent.ACTION_SEND).putExtra(Intent.EXTRA_TEXT, text).setType("text/plain") context.startActivity(Intent.createChooser(send, null)) }) { Text("Share") } }}Общему экрану нужен настоящий WebView на Android и iOS и просто ссылка на desktop. Как выразить это без
if (isIOS)в общем коде?// commonMain: the expectation is one function, not a platform type@Composableexpect fun PlatformWebView(url: String, modifier: Modifier = Modifier) // androidMain: actual repeats no default values@Composable…Как встроить настоящую нативную вью (MapKit, AVPlayer, WebView) в экран Compose Multiplatform?
// iosMain: a UIView inside the Compose tree@OptIn(ExperimentalForeignApi::class)@Composablefun MapWidget(modifier: Modifier = Modifier) { UIKitView( factory = { MKMapView() },…Пользователь выбирает другой город,
centerменяется, а карта не двигается. Почему и как это исправить?// iosMain@OptIn(ExperimentalForeignApi::class)@Composablefun CityMap(center: LatLng, modifier: Modifier = Modifier) { UIKitView( factory = { MKMapView().apply { setCenterCoordinate(CLLocationCoordinate2DMake(center.lat, center.lng), animated = false) } }, modifier = modifier )}Какую библиотеку навигации брать в Compose Multiplatform и что меняется в обработке «назад» и deep links вне Android?
// org.jetbrains.androidx.navigation:navigation-compose@Serializable object Home@Serializable data class Profile(val id: Long) @Composablefun App() {…Найдите баг: этот экран с фильтром работает на desktop, а на Android падает сразу при открытии.
// commonMaindata class Filter(val query: String = "", val onlyOpen: Boolean = false) @Composablefun OrdersScreen(orders: List<Order>) { var filter by rememberSaveable { mutableStateOf(Filter()) } Column { TextField(filter.query, onValueChange = { filter = filter.copy(query = it) }) Switch(filter.onlyOpen, onCheckedChange = { filter = filter.copy(onlyOpen = it) }) OrderList(orders.filter { it.matches(filter) }) }}Как в общем Compose-коде учитывать вырез, домашний индикатор и клавиатуру и по чему ветвиться вместо платформы?
@Composablefun HomeScaffold(content: @Composable () -> Unit) { val sizeClass = currentWindowAdaptiveInfo().windowSizeClass val wide = sizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) Scaffold(…VoiceOver читает общий экран как набор безымянных кусков и полностью пропускает рейтинг, нарисованный через Canvas. Что чинить?
// wrong: drawn, therefore invisible to VoiceOver and TalkBack@Composablefun Rating(stars: Int) { Canvas(Modifier.size(120.dp, 24.dp)) { drawStars(stars) }}…Как настроить быстрый цикл правок для общего Compose UI: превью в
commonMain, hot reload и где всё ещё нужен Xcode?// commonMainimport androidx.compose.ui.tooling.preview.Preview // multiplatform since CMP 1.10 @Preview(showBackground = true)@Composablefun OrderCardPreview() {…Какие Gradle-плагины подключает модуль Compose Multiplatform и почему обновление Kotlin ломает сборку?
plugins { alias(libs.plugins.kotlinMultiplatform) // org.jetbrains.kotlin.multiplatform alias(libs.plugins.composeCompiler) // org.jetbrains.kotlin.plugin.compose alias(libs.plugins.composeMultiplatform) // org.jetbrains.compose alias(libs.plugins.androidLibrary)}…Что реально можно выпустить на Compose Multiplatform для веба и что там до сих пор больно?
Нужно отдать тестировщикам подписанный .dmg и .msi desktop-таргета. Что делает конвейер упаковки Compose и где он обычно ломается?
compose.desktop { application { mainClass = "MainKt" buildTypes.release.proguard { configurationFiles.from(project.file("proguard.pro")) // keep rules for reflection }…Compose Multiplatform для iOS стабилен с 2025 года. Что именно это покрывает и чем он всё ещё отличается от нативного приложения на SwiftUI?
Во что реально обходится внедрение Compose Multiplatform в тестах, размере бинаря и повседневном тулинге?
У вас работающее SwiftUI-приложение. Как добавить один экран на Compose Multiplatform, не переписывая его?
// iosMain: the entire Compose surface the Swift app seesfun SettingsViewController( userId: String, onDone: () -> Unit): UIViewController = ComposeUIViewController { AppTheme { SettingsScreen(userId = userId, onDone = onDone) }…Состояние общего экрана переживает поворот на Android, но обнуляется каждый раз, когда SwiftUI показывает Compose-экран. Где это состояние на самом деле живёт?
// commonMain: nothing here is platform-specificclass OrderViewModel(private val repo: OrderRepo) : ViewModel() { val state = repo.orders .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), Orders.Empty)}…Один и тот же общий экран плавно скроллится на Android и дёргается на iOS. Как к этому подступиться?
Память растёт каждый раз, когда пользователь открывает и закрывает один Compose-экран в iOS-приложении, а Instruments никаких утечек не показывает. Что происходит?
// wrong: registers with the platform on every recomposition and never unregisters@OptIn(ExperimentalForeignApi::class)@Composablefun LocationBadge(state: LocationState) { NSNotificationCenter.defaultCenter.addObserverForName( name = "LocationChanged", `object` = null, queue = null…