【KMP】- 一《Flutter 也是跨平台,为什么还要KMP + CMP?》
学习 KMP 不是和 Flutter 二选一,而是在掌握"跨平台应用开发能力"之后,补充"跨平台业务底座设计能力"。Flutter、KMP、CMP 三者解决的问题角度不同,可以组合使用,其中最有价值的组合是:Flutter 负责跨平台 UI,KMP 负责跨平台业务底座。
一句话概括:Flutter 更擅长统一跨平台 UI,KMP 更适合沉淀跨平台业务底座;两者不仅可以选择其一,也可以组合使用。
概念澄清:Flutter、KMP、CMP 分别是什么
|
概念 |
本质 |
典型结构 |
|---|---|---|
|
Flutter |
一套完整的跨平台应用开发框架(Dart 语言 + Flutter Widget + 路由/状态管理/插件/渲染体系) |
lib/ 下集中 UI 与业务逻辑,android/ ios/ web/ 等为平台壳 |
|
KMP |
Kotlin Multiplatform:用 Kotlin 在多平台间共享业务代码,不强制共享 UI |
sharedLogic 的 commonMain + androidMain + iosMain + jsMain |
|
CMP |
Compose Multiplatform:建立在 KMP 之上,进一步解决 Compose UI 的跨平台复用 |
sharedLogic(业务)+ sharedUI(Compose UI)+ 各平台入口 App |
三者组合关系
纯 Flutter = Flutter UI + Dart 业务逻辑 纯 KMP = 原生 UI(Compose/SwiftUI 等)+ Kotlin 业务逻辑 KMP + CMP = Compose UI + Kotlin 业务逻辑 KMP + Flutter = Flutter UI + Kotlin 业务逻辑
因此,"Flutter 和 KMP 谁更好"本身是一个不够准确的问题,它们不是替代关系。
Flutter 与 KMP 的目标差异
两者都能参与跨平台开发,但出发点不同:
-
Flutter 倾向:用一套完整框架开发整个应用,共享范围通常覆盖 UI + 页面交互 + 业务逻辑 + 数据层。
-
KMP 倾向:共享适合共享的 Kotlin 代码(业务逻辑 + 数据层 + 领域模型),UI 是否共享另行决定,平台保留自己的开发方式。
以"设备巡检系统"为例:纯 Flutter 方案中,登录、列表、详情、扫码、地图、表单、网络请求、状态管理全部由 Flutter 承担;纯 KMP 方案中,sharedLogic 承担登录逻辑、设备模型、Repository、二维码解析、表单校验、网络与 Token 管理,而 CameraX、ML Kit、高德地图(Android)与 AVFoundation、MapKit(iOS)留在平台侧。
为什么学了 Flutter 还要学 KMP:四个原因
原因一:KMP 更适合渐进式接入现有 Android 项目
已有大量 Kotlin 业务代码的 Android 项目引入 Flutter,需要处理页面改造、跨层跳转、模型传递、插件包装、生命周期协调等大量问题;而引入 KMP 可以分三阶段逐步下沉纯 Kotlin 代码,Android 页面和平台能力继续保留:
-
第一阶段:迁移数据模型(Device、DeviceStatus、GeoPoint、ScanResult、InspectionForm);
-
第二阶段:迁移纯业务逻辑(ScanParser、LoginValidator、InspectionValidator、DeviceStatusMapper);
-
第三阶段:迁移公共架构能力(ApiResult、AppError、Repository、UseCase、Network、TokenStorage)。
原因二:Android 开发者可以继续使用 Kotlin
学习 Flutter 意味着引入 Dart + Flutter 这一整套新技术体系,大型项目中可能出现两套业务模型、双层通信、双生态维护。KMP 让 commonMain、androidMain、iosMain、App 层、CMP UI 全部使用 Kotlin,复用协程、Flow、Compose、Repository、UseCase、Gradle 多模块等既有能力,只需新增学习 Source Set、Kotlin/Native、expect/actual、跨平台依赖与 Swift 互操作。
原因三:KMP 不强制放弃原生 UI
已有 Android Compose 组件库和 iOS SwiftUI 组件库的团队,可以用纯 KMP 保留各自的平台 UI,只共享公共业务规则。强平台页面(如地图)可以 Android 用高德地图 SDK、iOS 用 MapKit,但共用同一套 GeoPoint、MapMarkerModel 业务模型。
原因四:KMP 更适合建设跨平台业务底座
KMP 模块可以服务多个终端:Android App、iOS App、Flutter App、CMP App、桌面工具等。例如 core-model 负责公共模型、core-network 负责网络与错误转换、core-qrcode 只负责解析规则不负责相机、core-map 只负责坐标与点位业务不负责地图 UI。此时 KMP 的定位从"帮一个 App 跨平台"升级为"建设可被不同客户端复用的业务核心"。
KMP + Flutter 组合架构
整体分层
Flutter(UI 层) ├── 页面 / Widget / 路由 / 主题 / 动画 / 页面状态适配
│
▼
Flutter Plugin / Platform Channel(桥接层)
│
▼
KMP sharedLogic(业务底座)
├── model / result / network / repository / usecase
├── validation / error / storage
│
▼
androidMain / iosMain(平台实现)
职责边界
|
层 |
职责 |
|---|---|
|
Flutter |
页面、Widget、路由、动画、主题、用户交互、加载/错误状态展示、调用 KMP UseCase、模型向 Dart 展示模型转换 |
|
KMP |
model、result、network、repository、usecase、validator、parser、token、error、storage 抽象(Domain + Data + Core Business) |
|
平台层 |
SDK、系统能力、硬件能力(相机、地图、权限等) |
更严谨的定位:Flutter = UI + Presentation Adapter;KMP = Domain + Data + Core Business;平台层 = SDK + 系统能力。"Flutter 只负责 UI"不代表零逻辑,展示层仍需要少量点击转发、路由、加载态、Dialog/Toast、状态组合等逻辑,但不建议 Flutter 和 KMP 各维护一套完整业务状态(例如同时存在 KMP DeviceViewModel 和 Flutter DeviceBloc)。
桥接层:Flutter 不能直接 import commonMain
Dart 通常不能直接引用 KMP 的 commonMain,Flutter 调用的是 Android/iOS 宿主层暴露的接口,宿主层再调用 KMP 业务底座。需要额外设计:Dart 与 Kotlin 模型转换、异步方法转换、异常转换、Flow 与 Stream 适配、生命周期管理、Flutter 插件接口、Android 与 iOS 桥接层。
KMP 与 Flutter 的状态管理:两种方案
|
对比项 |
方案一:KMP 暴露 UseCase |
方案二:KMP 暴露完整页面状态 |
|---|---|---|
|
结构 |
KMP UseCase → Flutter Riverpod/Bloc → Widget |
KMP ViewModel → StateFlow → Flutter Stream Adapter → Widget |
|
优点 |
桥接简单、Flutter 页面开发自然、适合第一阶段落地 |
页面状态多平台共享、业务状态集中 |
|
缺点 |
页面状态无法完全共享,Flutter 仍需维护展示层状态 |
Flow/Stream 需适配、生命周期复杂、桥接层更重、调试链路更长 |
文章建议:入门 Demo 先采用方案一(KMP 提供 Repository 和 UseCase,Flutter 负责页面展示状态),业务底座稳定后再考虑共享 ViewModel 和完整 UI State。
强平台能力的设计示例
扫码功能:拆成平台能力 + 公共业务逻辑
平台能力(留在平台侧):相机预览、二维码识别、权限申请、生命周期。公共业务逻辑(进入 commonMain):二维码内容解析、设备编号提取、二维码类型判断、扫码结果校验。流程为 Flutter 扫码页 → 扫码插件(Android 用 CameraX + ML Kit,iOS 用 AVFoundation)→ 得到 rawContent → 调用 KMP ScanParser → 返回 ScanResult → Flutter 展示设备信息。
地图功能:共享模型,不共享 SDK
KMP 共享 GeoPoint、MapMarkerModel 以及点位数据转换、坐标校验、设备与点位关联规则、地图业务范围计算;Flutter 或平台侧负责地图组件、Marker 绘制、缩放、定位权限和具体地图 SDK。也可按项目情况决定:普通地图页面交给 Flutter 插件,强平台地图能力直接用原生页面。
KMP + Flutter 的优势与代价
优势
-
Flutter 保留高 UI 复用率(页面、Widget、路由、主题、动画、普通交互跨端共享);
-
核心业务逻辑继续使用 Kotlin,已有 Android 代码更容易迁入 commonMain;
-
业务底座不与 Flutter 强绑定,KMP Core 还可服务原生 App、CMP App、桌面端等;
-
可复用已有 Android 资产——只要代码能脱离 Android SDK,就有机会迁入 KMP。
代价
项目中会同时存在 Kotlin、Dart、Swift、Gradle、Xcode、Flutter Plugin、KMP Framework,需要处理模型转换、协程与 Future 转换、Flow 与 Stream 转换、异常转换、双端插件实现、Framework 打包、多层调用链调试、多套依赖版本管理。调用链越长,调试成本越高。
该组合更适合:已有大量 Kotlin 业务代码、确实需要 Flutter 共享 UI、KMP Core 还要服务其他客户端、业务底座有长期复用价值、团队能承担桥接层维护成本。如果只是开发普通 Flutter App 且没有现成 Kotlin 资产,业务逻辑全部用 Dart 实现通常更简单。
四种跨平台路线对比
|
路线 |
结构 |
适合场景 |
|---|---|---|
|
纯 Flutter |
UI + State + Repository + Network + Storage + Plugin 全在 Flutter |
从零开始、页面型应用、双端 UI 高度一致、平台能力较少、团队已掌握 Flutter |
|
纯 KMP |
sharedLogic 共享业务,androidApp/iosApp 用原生 UI |
已有原生双端项目、希望渐进式共享业务逻辑、保留原生 UI、原生能力占比高 |
|
KMP + CMP |
sharedLogic + sharedUI(Compose)+ 各平台入口 |
团队以 Kotlin 为主、已掌握 Compose、希望共享业务和普通 UI、可接受强平台页面单独实现 |
|
KMP + Flutter |
Flutter 负责 UI,KMP 负责核心业务底座 |
已有大量 Kotlin 业务代码、希望 Flutter 共享 UI、KMP Core 还需服务原生 App、能承担桥接成本 |
案例:设备巡检 Demo 的选型
系列 Demo 包含登录、设备列表、设备详情、扫码识别、地图点位、巡检表单、网络请求、错误处理、平台日志、Token 存储,主线采用:KMP Core + 部分 CMP UI + 强平台能力平台化。sharedLogic 负责模型与业务规则,sharedUI 尝试负责登录页、列表页、详情页、表单页与通用状态页,Android 平台侧负责 CameraX、ML Kit、高德地图、权限、Service、WebView、蓝牙,iOS 平台侧负责 AVFoundation、MapKit、权限、Keychain。后续若改用 Flutter 作为 UI,sharedLogic 可继续复用,底层 KMP Core 不需要推倒重写——这正是建设独立业务底座的价值。
技术选型决策框架
不要判断"Flutter 和 KMP 谁能淘汰谁",而是回答三个问题:
-
业务核心放在哪里?
-
UI 放在哪里?
-
平台差异放在哪里?
文章倾向:设备巡检这类项目采用 KMP Core 负责公共业务底座,CMP 或 Flutter 负责可共享的普通 UI,地图、扫码、定位、蓝牙等强平台能力留在平台侧。
学习路线建议
-
第一阶段:KMP 基础——commonMain、androidMain、iosMain、expect/actual、平台依赖;
-
第二阶段:sharedLogic——model、result、network、storage、repository、usecase、error;
-
第三阶段:KMP 多模块组件化——core-model、core-network、core-storage、feature-auth、feature-device;
-
第四阶段:CMP——共享组件、登录页、设备列表、表单页面、平台 UI 混合;
-
第五阶段:其他 UI 宿主——Android 原生 UI、iOS 原生 UI、CMP、Flutter UI。
不要一开始就追求 Android、iOS、Web 所有代码百分之百共享,更重要的是建立稳定的业务边界。
关键代码摘录
公共结果封装(commonMain)
sealed class ApiResult<out T> { data class Success<T>(val data: T) : ApiResult<T>() data class Failure(val code: String, val message: String) : ApiResult<Nothing>() }
Repository 与 UseCase 抽象
interface DeviceRepository { suspend fun getDeviceList(): ApiResult<List<Device>> } class GetDeviceListUseCase( private val repository: DeviceRepository ) { suspend fun execute(): ApiResult<List<Device>> { return repository.getDeviceList() } }
共享页面状态(方案二)
sealed interface DeviceUiState { data object Loading : DeviceUiState data class Success(val devices: List<Device>) : DeviceUiState data class Error(val message: String) : DeviceUiState }
业务规则示例:扫码解析与表单校验
data class ScanResult( val rawContent: String, val deviceId: String?, val type: String? ) class ScanParser { fun parse(rawContent: String): ScanResult { // 公共二维码解析规则,不依赖相机与平台 SDK } }
class InspectionValidator { fun validate(form: InspectionForm): ValidationResult { if (form.deviceId.isBlank()) { return ValidationResult.Failure(message = "设备编号不能为空") } if (form.items.isEmpty()) { return ValidationResult.Failure(message = "巡检项目不能为空") } return ValidationResult.Success } }
地图业务模型(平台各自实现地图 UI)
data class GeoPoint(val latitude: Double, val longitude: Double) data class MapMarkerModel( val id: String, val title: String, val point: GeoPoint )
术语速查表
|
术语 |
含义 |
|---|---|
|
KMP |
Kotlin Multiplatform,Kotlin 跨平台业务逻辑共享方案 |
|
CMP |
Compose Multiplatform,基于 KMP 的 Compose UI 跨平台共享方案 |
|
commonMain |
KMP 公共代码源集,放不依赖平台 SDK 的模型、网络、Repository、UseCase、校验等 |
|
androidMain / iosMain |
各平台差异实现源集(如日志、DataStore/Keychain、平台信息) |
|
expect / actual |
KMP 声明平台差异接口(expect)与各平台实现(actual)的机制 |
|
sharedLogic |
共享业务逻辑模块;sharedUI 为共享 UI 模块(CMP) |
|
Presentation Adapter |
Flutter 层负责的少量展示适配逻辑(点击转发、加载态、模型转换等) |
总结
Flutter、KMP、CMP 都参与跨平台开发,但技术路线不同:Flutter 是用 Dart 构建完整跨平台应用;KMP 是用 Kotlin 建设可被多平台复用的业务底座;CMP 在 KMP 基础上解决 Compose UI 的多平台共享。三者不是替代关系,关键选型依据是业务核心、UI 与平台差异各放在哪一层,以及团队愿意承担多少桥接成本。
更多推荐



所有评论(0)