Multiplatform:用一套声明式 UI 连接多端

简介: Compose Multiplatform 是基于 Kotlin 的跨平台 UI 框架,延续 Jetpack Compose 声明式范式,支持 Android、iOS、桌面与 Web。它共享 UI 逻辑与状态,分层处理平台能力,适合工具类、中后台等 UI 高度一致的场景,助力 Android 团队高效拓展多端。(239 字)

Compose Multiplatform:用一套声明式 UI 连接多端

为什么会有 Compose Multiplatform

Android 开发者熟悉 Jetpack Compose 之后,很自然会遇到一个问题:既然 UI 已经可以用 Kotlin 和声明式方式表达,能不能把同一套 UI 思路带到桌面、iOS、Web 或更多平台?Compose Multiplatform 就是沿着这个方向发展出来的技术栈。

它不是简单地把 Android View 搬到其他平台,也不是让所有平台完全共用同一份应用代码。更准确地说,它提供了一套跨平台的 Compose Runtime、UI 组件模型和渲染能力,让开发者可以用 Kotlin 写共享界面和状态逻辑,再在不同平台接入各自的入口、权限、生命周期和系统能力。

对 Android 团队来说,它最大的吸引力有三个:语言和思维模型延续 Kotlin + Compose,业务页面可以在多个端复用,团队不必为每个平台完全重写一套 UI。它适合产品形态一致、交互差异不大、团队希望降低多端重复开发成本的场景。

它和 Jetpack Compose 的关系

Jetpack Compose 是 Android 官方的现代 UI 工具包,目标主要是 Android。Compose Multiplatform 则把 Compose 的核心思想扩展到更多平台。两者共享很多概念:Composable 函数、状态驱动 UI、remember、Modifier、Material 风格组件、预览和重组机制。

但它们也有明显区别。Android Compose 可以直接使用 Android SDK、Activity、ViewModel、Navigation、资源系统和平台组件;Compose Multiplatform 需要考虑多个平台,所以共享代码里不能随意依赖 Android 专属 API。涉及相机、定位、通知、文件选择、系统分享、支付、推送等能力时,通常要通过平台适配层实现。

可以把它理解为:Compose 的 UI 思维是共享的,平台能力仍然是分层的。写得好的多端项目,不是把平台差异藏起来假装不存在,而是把共性和差异放在合适的位置。

适合什么场景

Compose Multiplatform 比较适合这些类型的项目:

  • 工具类应用:例如笔记、待办、Markdown 编辑器、数据看板。
  • 中后台或桌面管理端:页面结构稳定,交互主要是表单、列表、筛选、编辑。
  • Android 团队主导的多端尝试:团队已有 Kotlin 和 Compose 经验。
  • 产品 UI 在多个平台高度一致:不需要大量平台原生交互。
  • 内部工具或新业务验证:希望快速覆盖桌面端和移动端。

它不太适合一开始就强行覆盖所有场景。如果应用高度依赖平台原生体验,例如复杂视频编辑、地图深度交互、系统级能力、强平台风格页面,跨平台共享 UI 的收益可能会被适配成本抵消。

推荐的工程结构

一个常见结构会把共享代码和平台入口分开:

composeApp/
  src/
    commonMain/
      kotlin/
        App.kt
        feature/
        ui/
        data/
    androidMain/
      kotlin/
        MainActivity.kt
    iosMain/
      kotlin/
        MainViewController.kt
    desktopMain/
      kotlin/
        main.kt

commonMain 放可共享的 Composable、状态管理、业务模型、Repository 接口和通用工具。androidMainiosMaindesktopMain 放平台入口和平台专属实现。

这种结构的关键是依赖方向:共享层定义能力,平台层提供实现。不要让 commonMain 直接依赖 Android 的 ContextActivityUri 或资源访问方式,否则代码很快会失去跨平台意义。

从一个共享 App 开始

共享 UI 通常从一个根 Composable 开始:

@Composable
fun App() {
    MaterialTheme {
        var count by rememberSaveable { mutableStateOf(0) }

        Column(
            modifier = Modifier.fillMaxSize().padding(24.dp),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Count: $count")
            Spacer(Modifier.height(12.dp))
            Button(onClick = { count++ }) {
                Text("Increase")
            }
        }
    }
}

Android 入口可以这样接入:

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

桌面端入口则可能是:

fun main() = application {
    Window(onCloseRequest = ::exitApplication, title = "Compose App") {
        App()
    }
}

你会发现,共享 UI 的写法和 Android Compose 非常接近。真正需要设计的是:哪些状态和页面可以共享,哪些能力必须交给平台层。

状态管理:共享逻辑,隔离平台

Compose Multiplatform 里仍然推荐使用单向数据流。页面接收状态,事件向上抛,业务层处理后更新状态。

data class LoginUiState(
    val account: String = "",
    val password: String = "",
    val loading: Boolean = false,
    val error: String? = null
)

class LoginPresenter(
    private val repository: LoginRepository
) {
    var state by mutableStateOf(LoginUiState())
        private set

    fun onAccountChange(value: String) {
        state = state.copy(account = value)
    }

    fun onPasswordChange(value: String) {
        state = state.copy(password = value)
    }
}

在 Android 项目中,我们常把 ViewModel 作为状态容器。多端项目也可以使用适合 Kotlin Multiplatform 的状态容器方案,或者自己封装轻量 Presenter。重点不是名字,而是让状态转换可测试、可复用,并且不要绑死在某个平台生命周期上。

平台差异怎么处理

跨平台项目一定会遇到平台差异。正确做法不是在共享 UI 里到处写平台判断,而是用接口隔离。

interface ShareService {
    fun shareText(text: String)
}

共享页面只依赖接口:

@Composable
fun ArticleScreen(shareService: ShareService) {
    Button(onClick = { shareService.shareText("Compose Multiplatform") }) {
        Text("Share")
    }
}

Android、iOS、Desktop 分别提供自己的实现。这样共享页面仍然干净,平台能力也能保留各自体验。

更复杂的能力可以使用 expect/actual

expect fun currentPlatformName(): String

然后在不同平台实现:

actual fun currentPlatformName(): String = "Android"

适配层要控制规模。接口越多,维护成本越高。只有确实需要跨平台调用的能力才抽象,不要为了抽象而抽象。

资源、主题与设计一致性

Compose Multiplatform 可以共享颜色、字体、间距、组件样式和部分资源。建议把设计系统放在共享层:

object AppSpacing {
    val small = 8.dp
    val medium = 16.dp
    val large = 24.dp
}

@Composable
fun PrimaryButton(text: String, onClick: () -> Unit) {
    Button(onClick = onClick, modifier = Modifier.height(48.dp)) {
        Text(text)
    }
}

共享设计系统的好处很直接:同一个按钮、同一个卡片、同一个空状态,不需要在不同端重复实现。但也要允许平台有自己的细节。例如桌面端可能需要 hover、右键菜单、快捷键;移动端更关注触摸区域、软键盘和手势。

好的多端 UI 不是所有像素完全一致,而是品牌和交互逻辑一致,同时尊重平台使用习惯。

导航与页面拆分

导航是多端项目容易变复杂的地方。简单项目可以自己维护一个页面栈:

sealed interface Screen {
    data object Home : Screen
    data class Detail(val id: String) : Screen
}

然后根据当前 Screen 渲染不同页面:

@Composable
fun AppRoot() {
    var screen by remember { mutableStateOf<Screen>(Screen.Home) }

    when (val current = screen) {
        Screen.Home -> HomeScreen(onOpenDetail = { screen = Screen.Detail(it) })
        is Screen.Detail -> DetailScreen(id = current.id, onBack = { screen = Screen.Home })
    }
}

业务复杂后,可以引入更完整的导航方案,但仍要注意:Android 的返回键、iOS 的手势返回、桌面端窗口行为并不完全一样。共享导航模型可以复用页面关系,平台入口仍要处理各自的系统交互。

网络、数据库与共享业务层

Compose Multiplatform 不只共享 UI,也可以共享业务模型、网络请求、序列化和部分数据层。常见组合包括 Ktor、kotlinx.serialization、SQLDelight、Settings、Koin 或其他多端友好的库。

Repository 可以放在共享层:

interface ArticleRepository {
    suspend fun loadArticles(): List<Article>
}

class RemoteArticleRepository(
    private val client: HttpClient
) : ArticleRepository {
    override suspend fun loadArticles(): List<Article> {
        return client.get("https://example.com/articles").body()
    }
}

这样 UI、状态和业务请求都可以复用。平台层只负责提供网络引擎、文件路径、数据库驱动或系统能力实现。

需要注意的是,共享层越重,构建、调试和平台差异处理也会越复杂。建议从 UI + 轻业务逻辑开始,再逐步把稳定的能力沉淀到共享层。

测试策略

多端项目更需要测试,因为共享代码的改动会影响多个目标平台。建议从这些地方开始:

  • 对状态容器写普通 Kotlin 单元测试。
  • 对 Repository 使用 Mock 网络响应测试解析和错误处理。
  • 对关键 Composable 做截图或语义节点测试。
  • 对平台适配层做少量集成测试。
  • 对核心用户路径做端到端冒烟测试。

共享逻辑的测试收益很高,因为一份测试能覆盖多个平台的公共行为。平台相关测试则要少而精,重点验证适配是否正确。

落地时最容易踩的坑

常见问题主要集中在这些方面:

  • 过早追求全量共享,导致平台体验变差。
  • 共享层依赖平台 API,后期拆分困难。
  • 抽象接口太多,简单功能也变得绕。
  • 忽视桌面端键盘、鼠标、窗口尺寸等交互差异。
  • 忽视 iOS 审核、权限、原生控件和团队协作成本。
  • 对构建链路、依赖版本和 Gradle 配置缺少治理。

更稳的做法是先选择一个边界清晰的模块试点,例如设置页、详情页、内部工具或新业务小应用。等团队熟悉工程结构、调试方式和发布流程后,再扩大共享范围。

和 Flutter、React Native 怎么取舍

如果团队主要是 Android/Kotlin 背景,已经在使用 Compose,那么 Compose Multiplatform 的学习成本相对低,代码和思维模型延续性很好。如果团队已有成熟 Flutter 或 React Native 体系,迁移成本就需要谨慎评估。

取舍时可以看几个问题:

  • 团队主力语言是不是 Kotlin?
  • 产品是否重视平台原生体验?
  • UI 是否高度一致?
  • 是否需要复用已有 Android Compose 页面?
  • iOS、桌面、Web 的投入预期有多高?
  • 构建、调试、测试和发布链路是否有人维护?

技术选择没有绝对答案。跨平台框架真正的成本往往不在写出第一个页面,而在长期维护、团队协作和平台差异处理。

实践建议

如果你准备在 Android 团队里尝试 Compose Multiplatform,可以按这个节奏推进:

  • 先用一个小模块验证工程链路。
  • 只共享页面、状态和稳定业务逻辑。
  • 平台能力用接口隔离,不要污染共享层。
  • 设计系统先统一基础组件,再逐步扩展复杂组件。
  • 对共享状态和 Repository 补单元测试。
  • 记录平台差异和适配约定,避免每个页面各写一套。

Compose Multiplatform 的价值,不是承诺“写一次到处完美运行”,而是让 Kotlin 和 Compose 的开发经验可以在更多平台复用。对 Android 开发者来说,它提供了一条自然的多端扩展路径:先把 UI 和状态写清楚,再把平台差异放到边界上处理。

当项目边界合适、团队能力匹配、平台差异可控时,它能明显减少重复开发,让多端应用的架构更统一。反过来,如果业务强依赖平台特性,就应该克制共享范围,把它用在真正能产生收益的部分。

相关文章
|
23天前
|
Linux 程序员 数据格式
【2026最新】Notepad++下载、安装和使用一篇搞定(附中文版安装包)
Notepad++ 是一款免费开源、轻量高效的 Windows 文本编辑器,支持 C/Python/HTML 等 80+ 语言语法高亮、代码折叠、正则替换、编码转换及插件扩展,专为程序员与文本处理用户打造,完美替代系统记事本。(239字)
|
8天前
|
人工智能 缓存 安全
Claude Code 封号真实原因曝光,这次彻底不装了,直接针对国内开发者的账号下手?
Claude Code 封号潮背后:逆向扒出客户端隐写区域标记,Anthropic 政策收紧叠加 DeepSeek 7 月涨价,国产替代更紧迫。
|
13天前
|
人工智能 JSON 自然语言处理
让教学更智慧:用阿里云百炼工作流,自动生成中小学教材内容#小有可为#有温度的AI
通过可视化工作流编排,将大模型推理能力转化为标准化的教学内容生成引擎。教师只需输入教材标题和适用学段,即可自动获得结构完整、符合课程标准的章节内容,大幅降低备课门槛,助力教育资源均衡化。
499 127
|
17天前
|
存储 人工智能 监控
QoderWork完全指南:从入门到精通,把“AI实习生”变成你的全能工作搭档
阿里云2026年推出的桌面端AI工作助手QoderWork,不止聊天,更可动手干活:本地运行、安全可控,支持文件整理、数据分析、PPT生成、网页开发等;内置专家套件、多Agent协作与自定义Skills,让AI真正成为你身边的“AI实习生”。
|
8天前
|
人工智能 安全 程序员
终于,Claude Code 封号的原因被曝光了!竟然针对中国用户,植入隐形代码?!
通俗易懂地揭秘 Claude Code 封号的手段,分享一些自己对 AI 编程困境的思考,Codex、Cursor、DeepSeek、智谱 GLM、甚至是豆包,都有所行动了
472 1
|
9天前
|
人工智能 安全 Cloud Native
Higress 新发布:AI Gateway 能力增强,Gateway API 及其推理扩展持续打磨
增强 AI 网关能力,持续打磨 Gateway API 及其推理扩展。
395 126
|
7天前
|
人工智能 编解码 物联网
2026 最新Stable Diffusion 本地部署教程 下载安装使用详细图解(含官网安装包)
Stable Diffusion(SD)是2022年发布的开源文生图模型,由Stability AI等联合开发。支持文生图、图生图、局部重绘等,依托VAE降低算力需求,可在消费级显卡运行。本文提供秋葉aaaki制作的Windows整合包(含图形界面与插件),开箱即用,零配置启动。