从 Android 组件化到 KMP 跨平台底座(二):Flutter 也是跨平台,为什么还要学习 KMP + CMP? 前言学习 KMP 之前我已经接触并学习过 Flutter。Flutter 可以使用一套 Dart 代码开发 Android、iOS甚至还可以扩展到 Web 和桌面端。页面可以共享业务逻辑也可以共享。那么问题就来了Flutter 已经可以跨平台了为什么还要继续学习 KMP进一步说Compose Multiplatform 也可以共享 UI它和 Flutter 又有什么区别一开始很容易把这个问题理解成Flutter 和 KMP 到底谁更好但随着理解深入会发现这个问题本身就不够准确。因为 Flutter、KMP 和 CMP 虽然都与跨平台有关但它们解决问题的角度并不完全相同。更重要的是Flutter 和 KMP 不一定是竞争关系它们也可以组合使用。例如Flutter 负责跨平台 UI KMP 负责跨平台业务底座所以这一篇不是简单比较 Flutter 和 KMP 谁更强而是要讲清楚Flutter、KMP、CMP 分别解决什么问题已经学了 Flutter为什么 KMP 仍然值得学习Flutter 和 KMP 是否只能二选一KMP Flutter 到底应该怎么设计KMP CMP 和 KMP Flutter 有什么区别Android 开发者应该如何选择跨平台路线一、先分清 Flutter、KMP 和 CMP在讨论技术选型之前首先要把三个概念分清楚。1. Flutter 是什么Flutter 是一套完整的跨平台应用开发框架。开发者主要使用DartFlutter WidgetFlutter 路由Flutter 状态管理Flutter 插件体系Flutter 渲染体系来完成多个平台的应用开发。一个典型的 Flutter 项目可能是flutter_app/ ├── lib/ │ ├── core/ │ ├── data/ │ ├── domain/ │ ├── feature/ │ ├── router/ │ └── main.dart │ ├── android/ ├── ios/ ├── web/ ├── macos/ └── windows/其中主要的 UI 和业务逻辑都写在lib/例如登录页面设备列表设备详情网络请求Repository状态管理路由本地缓存通常都可以使用 Dart 和 Flutter 实现。因此Flutter 更像是提供一套完整的跨平台应用开发体系。2. KMP 是什么KMP 的全称是 Kotlin Multiplatform。它允许开发者使用 Kotlin在多个平台之间共享代码。典型项目结构可能是sharedLogic/ ├── commonMain/ ├── androidMain/ ├── iosMain/ └── jsMain/其中commonMain负责公共代码例如数据模型网络请求RepositoryUseCase业务规则数据解析错误处理表单校验Token 管理状态定义平台差异代码则分别放在androidMain iosMain jsMain例如androidMain ├── Android 日志实现 ├── DataStore 存储实现 └── Android 平台信息 iosMain ├── iOS 日志实现 ├── Keychain 存储实现 └── iOS 平台信息KMP 本身并不强制所有平台使用同一套 UI。因此使用纯 KMP 时可以是Android UICompose 或 XML iOS UISwiftUI 或 UIKit Web UIReact、Vue 或其他 Web 框架 业务逻辑KMP sharedLogic所以KMP 更像是建设一套可以被多个平台复用的 Kotlin 业务底座。3. CMP 是什么CMP 的全称是 Compose Multiplatform。它建立在 Kotlin Multiplatform 之上进一步解决 Compose UI 的跨平台复用问题。只有 KMP 时可以是sharedLogic 负责共享业务逻辑 Android 使用 Compose iOS 使用 SwiftUI加入 CMP 后可以变成sharedLogic 负责共享业务逻辑 sharedUI 负责共享 Compose UI androidApp iosApp webApp desktopApp 负责平台入口因此三者可以先这样理解Flutter Dart 业务逻辑 Flutter UI KMP Kotlin 跨平台业务逻辑 KMP CMP Kotlin 跨平台业务逻辑 Compose 跨平台 UI但是这个关系还不完整。因为 Flutter 也可以只负责 UI底层业务逻辑交给 KMP。于是还存在第四种组合KMP Flutter Kotlin 跨平台业务底座 Flutter 跨平台 UI二、Flutter 和 KMP 的目标并不完全相同Flutter 和 KMP 都可以参与跨平台开发但它们的出发点不同。Flutter 更倾向于使用一套完整的跨平台框架开发整个应用。KMP 更倾向于共享适合共享的 Kotlin 代码同时保留不同平台自己的开发方式。假设现在要开发一个设备巡检系统包含登录设备列表设备详情扫码识别设备地图点位巡检表单Token 管理网络请求错误处理如果使用纯 Flutter整体可能是Flutter App ├── 登录页面 ├── 设备列表页面 ├── 设备详情页面 ├── 巡检表单页面 ├── 扫码页面 ├── 地图页面 ├── 网络请求 ├── Repository ├── 状态管理 └── 本地存储UI 和主要业务逻辑都由 Flutter 负责。如果使用纯 KMP整体可能是sharedLogic ├── 登录业务逻辑 ├── 设备数据模型 ├── DeviceRepository ├── 二维码结果解析 ├── 地图点位模型 ├── 巡检表单校验 ├── 网络请求 └── Token 管理 androidApp ├── Compose ├── CameraX ├── ML Kit └── 高德地图 iosApp ├── SwiftUI ├── AVFoundation └── MapKit两种方案都实现了跨平台但是共享范围不同。Flutter 通常会共享UI 页面交互 业务逻辑 数据层纯 KMP 通常会共享业务逻辑 数据层 领域模型UI 是否共享可以另外决定。三、Flutter 更像“统一开发整个应用”Flutter 的主要特点是 UI 和业务逻辑通常都运行在 Flutter 体系中。例如设备模型可以使用 Dart 定义class Device { final String id; final String name; final DeviceStatus status; const Device({ required this.id, required this.name, required this.status, }); }页面使用 Flutter Widgetclass DeviceListPage extends StatelessWidget { const DeviceListPage({super.key}); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(设备列表), ), body: const DeviceListView(), ); } }网络请求、状态管理、路由也会进入 Flutter 技术体系Dart Flutter Widget Dio Riverpod / Bloc / Provider go_router Flutter Plugin MethodChannel这种方式的优势很明显UI 复用率高多个平台页面容易保持一致一套代码可以维护多个客户端适合从零开始的跨平台项目普通列表、表单和内容页面开发效率较高但它也意味着项目的主要技术体系会逐渐转向 Flutter。原有 Android 项目中的Compose 页面XML 页面Android ViewModelKotlin RepositoryAndroid LibraryKotlin 业务模型并不能直接变成 Flutter 代码。它们要么继续留在原生层要么重新使用 Dart 实现要么通过桥接暴露给 Flutter。四、KMP 更像“共享底座平台分别开发”KMP 不要求整个应用都使用同一套 UI。它更关注哪些代码真正适合跨平台共享例如设备模型data class Device( val id: String, val name: String, val status: DeviceStatus ) enum class DeviceStatus { ONLINE, OFFLINE, WARNING }二维码解析逻辑data class ScanResult( val rawContent: String, val deviceId: String?, val type: String? ) class ScanParser { fun parse(rawContent: String): ScanResult { val params rawContent .substringAfter(?, rawContent) .split() .mapNotNull { val pair it.split() if (pair.size 2) { pair[0] to pair[1] } else { null } } .toMap() return ScanResult( rawContent rawContent, deviceId params[deviceId], type params[type] ) } }巡检表单校验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 } }这些逻辑都不依赖Android ContextActivityCameraX高德地图SwiftUIUIKit因此可以进入commonMain而扫码、地图、相机、权限等能力继续留在平台侧Android ├── CameraX ├── ML Kit ├── 高德地图 └── Android 权限 iOS ├── AVFoundation ├── Vision ├── MapKit └── iOS 权限所以KMP 更强调公共业务核心 │ ├── Android 平台实现 ├── iOS 平台实现 └── Web 平台实现五、已经学习 Flutter为什么还要学习 KMP对于 Android 开发者来说Flutter 和 KMP 带来的能力并不完全重复。学习 Flutter可以获得完整跨平台 App 开发能力Dart 语言能力Flutter UI 开发能力跨平台路由和状态管理能力Flutter 插件开发能力一套 UI 覆盖 Android 和 iOS 的能力学习 KMP则可以获得Kotlin 跨平台能力Android 与 iOS 业务逻辑共享能力原生项目渐进式改造能力多平台业务底座设计能力平台抽象和平台适配能力KMP 多模块组件化能力Kotlin 与 iOS 平台互操作能力Flutter 更关注如何使用统一框架开发多个平台的应用KMP 更关注多平台项目中 哪些代码应该共享 哪些代码应该留在平台侧所以学习 Flutter 后再学习 KMP不是在重复学习跨平台而是在补充另一种跨平台架构能力。六、原因一KMP 更适合渐进式接入现有 Android 项目假设现在已经有一个运行多年的 Android 项目androidApp/ ├── app ├── lib-network ├── lib-storage ├── lib-qrcode ├── lib-map ├── feature-auth ├── feature-device └── feature-inspection项目已经包含大量 Kotlin 业务代码Compose 或 XML 页面CameraX高德地图WebView蓝牙MQTTAndroid Service后台任务如果要使用 Flutter需要考虑哪些页面改成 FlutterFlutter 和原生页面如何跳转Kotlin 模型如何传递给 Dart原有 Android Library 如何包装Flutter 和原生生命周期如何协调原生能力如何通过插件或者通道暴露但如果引入 KMP可以先新增sharedLogic然后逐步迁移纯 Kotlin 代码。第一阶段迁移数据模型Device DeviceStatus GeoPoint ScanResult InspectionForm第二阶段迁移纯业务逻辑ScanParser LoginValidator InspectionValidator DeviceStatusMapper第三阶段迁移公共架构能力ApiResult AppError Repository UseCase Network TokenStorage原来的 Android 页面和平台能力可以继续使用。因此KMP 更适合从已有 Android 项目中逐步提取跨平台业务底座。它不是要求一次性重写整个应用而是允许业务逻辑逐步下沉。七、原因二Android 开发者可以继续使用 KotlinAndroid 开发者学习 Flutter需要增加一套新的技术体系Kotlin / Java Dart / Flutter这本身没有问题。多掌握一种语言和框架可以拓宽技术能力。但是在大型项目中同时维护两套语言体系也意味着Kotlin 和 Dart 可能各有一套业务模型Android 原生层和 Flutter 层需要通信团队需要同时掌握两套生态调试链路可能跨越多个运行层公共逻辑需要决定到底放在哪一层KMP 则可以让 Android 开发者继续使用熟悉的 KotlincommonMainKotlin androidMainKotlin iosMainKotlin Android AppKotlin CMP UIKotlinAndroid 开发者已有的很多能力都可以继续复用Kotlin协程FlowComposeRepositoryUseCaseGradle多模块组件化依赖倒置接口抽象需要新增学习的主要是Source SetKotlin/NativeiOS 平台 APIexpect 和 actual跨平台依赖Swift 与 Kotlin 互操作KMP 模块边界因此KMP 更像是在 Kotlin 基础上扩大平台边界。八、原因三KMP 不强制放弃原生 UI已有 Android 团队可能已经积累了大量 Compose 组件AppButton AppDialog AppTopBar DeviceCard StatusTag InspectionForm MapMarkerInfoWindowiOS 团队也可能已经有成熟的 SwiftUI 组件库。在这种情况下团队未必希望把全部 UI 都换成同一套跨平台方案。使用纯 KMP 时可以采用Android UICompose iOS UISwiftUI Web UIReact 或 Vue 业务逻辑KMP sharedLogic这种方式可以保证Android 保留 Android 的平台体验iOS 保留 iOS 的平台体验平台团队继续使用熟悉的 UI 技术公共业务规则只维护一份强平台页面可以深入使用系统 API例如地图页面。Android 使用高德地图Compose / Android View │ ▼ 高德地图 Android SDKiOS 使用 MapKitSwiftUI / UIKit │ ▼ MapKit但它们共用相同的点位模型data class GeoPoint( val latitude: Double, val longitude: Double ) data class MapMarkerModel( val id: String, val title: String, val point: GeoPoint )所以KMP 可以共享业务但不强迫所有平台使用相同的 UI。九、原因四KMP 更适合建设跨平台业务底座Flutter 项目同样可以分层和组件化例如core data domain feature presentation但是从已有 Android 组件化项目继续演进时KMP 更容易形成明确的跨平台业务底座core-model core-result core-network core-storage core-platform core-qrcode core-map feature-auth feature-device feature-inspection这些模块不仅可以服务于一个 App还可以服务于多个终端Android App iOS App Flutter App Compose Multiplatform App Desktop App 其他 Kotlin 项目例如core-model负责公共业务模型。core-network负责网络请求、序列化和错误转换。core-storage负责 Token、用户信息和缓存能力。core-qrcode负责二维码解析规则不负责相机。core-map负责经纬度模型、坐标转换和点位业务不负责地图 UI。公司后续如果存在设备巡检 App 维保 App 物业管理 App 管理人员 App 桌面管理工具它们都可以依赖同一套 KMP Core。这时KMP 的定位已经不只是“帮助一个 App 跨平台”而是建设一套可以被不同客户端复用的业务核心。十、Flutter 和 KMP 并不一定二选一前面主要讲了纯 Flutter 和纯 KMP。但实际上还有一种很有价值的架构KMP Flutter也就是Flutter 负责跨平台 UI KMP 负责跨平台业务逻辑整体结构可以设计成Flutter ├── 页面 ├── Widget ├── 路由 ├── 主题 ├── 动画 └── 页面状态适配 │ ▼ Flutter Plugin / Platform Channel │ ▼ KMP sharedLogic ├── model ├── result ├── network ├── repository ├── usecase ├── validation ├── error └── storage │ ▼ androidMain / iosMain ├── Android 平台实现 └── iOS 平台实现这种模式下Flutter 不负责核心业务规则 KMP 不负责 Flutter UI两者各自负责自己更擅长的部分。十一、KMP Flutter 中Flutter 只负责什么如果把 Flutter 定位为 UI 层那么 Flutter 主要负责页面Widget路由动画主题用户交互加载状态展示错误状态展示页面状态适配调用 KMP UseCase例如设备列表页面Flutter DeviceListPage │ ▼ 调用 GetDeviceListUseCase │ ▼ KMP Repository │ ▼ KMP Network │ ▼ 返回设备列表 │ ▼ Flutter 渲染页面Flutter 层不需要重新实现网络错误转换设备状态判断Token 刷新二维码内容解析巡检表单业务校验DTO 到领域模型转换这些逻辑都可以由 KMP 负责。十二、KMP Flutter 中KMP 负责什么KMP 可以负责model result network repository usecase validator parser token error storage abstraction例如sealed class ApiResultout T { data class SuccessT( val data: T ) : ApiResultT() data class Failure( val code: String, val message: String ) : ApiResultNothing() }Repositoryinterface DeviceRepository { suspend fun getDeviceList(): ApiResultListDevice }UseCaseclass GetDeviceListUseCase( private val repository: DeviceRepository ) { suspend fun execute(): ApiResultListDevice { return repository.getDeviceList() } }Flutter 不需要关心底层具体使用Ktor数据库Android DataStoreiOS KeychainAndroid 网络实现iOS 平台实现Flutter 只需要获得最终业务结果。十三、Flutter 能直接调用 commonMain 吗概念上Flutter 可以使用 KMP 的能力。但是在工程上Dart 通常不能直接import commonMainFlutter 和 KMP 之间通常需要一层桥接。Android 侧可能是Dart │ ▼ Flutter Plugin / MethodChannel │ ▼ Kotlin Android Bridge │ ▼ KMP sharedLogiciOS 侧可能是Dart │ ▼ Flutter Plugin / MethodChannel │ ▼ Swift Bridge │ ▼ KMP Framework也就是说Flutter 调用的是 Android 或 iOS 宿主层暴露出来的接口宿主层再调用 KMP 业务底座。因此KMP Flutter虽然可行但不是把两个工程目录简单放到一起就结束了。还需要设计Dart 和 Kotlin 的模型转换异步方法转换异常转换Flow 与 Stream 的适配生命周期管理Flutter 插件接口Android 和 iOS 桥接层十四、“Flutter 只负责 UI”并不代表 Flutter 零逻辑从架构定位上可以说Flutter 只负责 UI但是从工程实现上Flutter 层通常仍然需要少量展示层逻辑。例如点击事件转发页面跳转路由参数加载状态错误提示Dialog 显示Toast 显示页面级状态组合KMP 模型向 Dart 展示模型转换因此更严谨的说法应该是Flutter UI Presentation Adapter KMP Domain Data Core Business 平台层 SDK 系统能力 硬件能力不建议 Flutter 和 KMP 各维护一套完整业务状态。例如不要同时存在KMP DeviceViewModel Flutter DeviceBloc 两边各有一套业务规则否则很容易出现状态重复业务逻辑重复数据转换复杂问题难以定位十五、KMP 和 Flutter 的状态应该由谁管理主要可以有两种方案。方案一KMP 暴露 UseCaseFlutter 管理页面状态结构是KMP UseCase │ ▼ 返回业务结果 │ ▼ Flutter Riverpod / Bloc │ ▼ Flutter Widget例如KMP 负责获取设备列表 Flutter 负责 Loading、Success、Error 页面状态优点桥接比较简单Flutter 页面开发自然不需要复杂转换 Flow适合第一阶段落地缺点页面状态无法完全共享Flutter 仍然需要维护展示层状态对于入门项目这种方式更容易理解和落地。方案二KMP 暴露完整页面状态结构是KMP ViewModel │ ▼ StateFlow │ ▼ Flutter Stream Adapter │ ▼ Flutter WidgetKMP 可能定义sealed interface DeviceUiState { data object Loading : DeviceUiState data class Success( val devices: ListDevice ) : DeviceUiState data class Error( val message: String ) : DeviceUiState }优点页面状态也可以在多个平台共享Android、iOS、Flutter 可以使用同一套状态逻辑业务状态集中缺点Flow 和 Stream 需要适配生命周期处理更复杂桥接层更重调试链路更长对于这个系列的 Demo更适合先采用KMP 提供 Repository 和 UseCaseFlutter 负责页面展示状态。等业务底座稳定后再考虑是否共享 ViewModel 和完整 UI State。十六、扫码功能在 KMP Flutter 中怎么设计以扫码识别设备为例。扫码功能可以拆成两部分。第一部分是平台能力相机预览 二维码识别 权限申请 生命周期第二部分是公共业务逻辑二维码内容解析 设备编号提取 二维码类型判断 扫码结果校验整体流程可以是Flutter 扫码页面 │ ▼ 调用 Flutter 扫码插件 │ ├── AndroidCameraX ML Kit │ └── iOSAVFoundation │ ▼ 获得 rawContent │ ▼ 调用 KMP ScanParser │ ▼ 返回 ScanResult │ ▼ Flutter 展示设备信息KMP 中负责data class ScanResult( val rawContent: String, val deviceId: String?, val type: String? )class ScanParser { fun parse(rawContent: String): ScanResult { // 公共二维码解析规则 TODO() } }平台侧负责如何打开相机 如何申请权限 如何识别二维码Flutter 负责扫码页面 扫描框动画 结果展示 跳转设备详情这就是比较清晰的职责边界。十七、地图功能在 KMP Flutter 中怎么设计地图同样需要拆分。KMP 可以共享data class GeoPoint( val latitude: Double, val longitude: Double ) data class MapMarkerModel( val id: String, val title: String, val point: GeoPoint )还可以共享点位数据转换坐标业务校验设备与点位关联规则地图业务范围计算Marker 业务模型Flutter 或平台侧负责地图组件Marker 绘制地图缩放定位权限地图生命周期高德地图或 MapKit SDK整体可以是KMP 返回 MapMarkerModel 列表 │ ▼ Flutter 展示地图和 Marker │ ▼ Flutter 地图插件 │ ├── Android 地图 SDK └── iOS 地图 SDK这里也可以根据项目实际情况决定普通地图页面交给 Flutter 插件强平台地图能力直接使用原生页面KMP 只共享地图业务模型十八、KMP Flutter 的优势1. Flutter 保留高 UI 复用率Android 和 iOS 可以共享页面Widget路由主题动画普通交互2. 核心业务逻辑继续使用 Kotlin已有 Android Kotlin 代码更容易迁入commonMain例如接口结果封装设备模型二维码解析RepositoryUseCaseToken 管理表单校验3. 业务底座不与 Flutter 强绑定KMP Core 不仅可以服务 Flutter还可以提供给Android 原生 App iOS 原生 App Compose Multiplatform App 桌面端 其他 Kotlin 项目Flutter 只是 KMP Core 的一个 UI 宿主。4. 可以复用已有 Android 资产原有的 Kotlin 业务逻辑不一定需要全部重写成 Dart。只要代码可以脱离 Android SDK就有机会迁入 KMP。十九、KMP Flutter 的代价这套组合也不是没有成本。项目中会同时存在Kotlin Dart Swift Gradle Xcode Flutter Plugin KMP Framework需要额外处理Dart 与 Kotlin 模型转换协程与 Future 转换Flow 与 Stream 转换KMP 异常向 Flutter 错误转换Android 插件实现iOS 插件实现Framework 打包多层调用链调试多套依赖版本管理例如一次设备列表请求调用链可能是Flutter Widget │ ▼ Flutter Repository Adapter │ ▼ MethodChannel │ ▼ Android / iOS Bridge │ ▼ KMP UseCase │ ▼ KMP Repository │ ▼ KMP Network调用链越长调试成本越高。因此这种组合更适合已经有大量 Kotlin 业务代码团队确实需要 Flutter 共享 UIKMP Core 还要服务其他客户端业务底座有长期复用价值团队能够承担桥接层维护成本如果只是开发一个普通 Flutter App也没有现成 Kotlin 资产那么业务逻辑全部使用 Dart 实现通常更加简单。二十、Flutter 和 CMP 都能共享 UI有什么区别Flutter 和 CMP 都可以共享 UI但它们属于不同的技术体系。Flutter 使用Dart Flutter Widget Flutter 状态管理 Flutter 插件生态CMP 使用Kotlin Compose 协程 Flow KMP 依赖体系CMP 页面可能是Composable fun DeviceCard( device: Device, onClick: () - Unit ) { Card( onClick onClick ) { Column { Text(text device.name) Text(text device.status.name) } } }Flutter 页面可能是class DeviceCard extends StatelessWidget { final Device device; final VoidCallback onTap; const DeviceCard({ required this.device, required this.onTap, super.key, }); override Widget build(BuildContext context) { return Card( child: ListTile( title: Text(device.name), subtitle: Text(device.status), onTap: onTap, ), ); } }对于 Android Compose 开发者Compose │ ▼ Compose Multiplatform是一条比较自然的技术延伸路线。而 Flutter 则意味着Android 原生技术栈 │ ▼ Dart Flutter 技术栈因此KMP CMP Kotlin 业务逻辑 Compose UI KMP Flutter Kotlin 业务逻辑 Flutter UI两者都可以共享业务和 UI只是 UI 技术不同。二十一、四种常见跨平台路线综合来看实际项目中至少存在四种跨平台路线。路线一纯 FlutterFlutter App ├── UI ├── State ├── Repository ├── Network ├── Storage └── Platform PluginFlutter 同时负责 UI 和主要业务逻辑。适合从零开始的新项目页面型应用Android 和 iOS UI 高度一致平台专属能力较少团队已经掌握 Flutter希望使用一套 Dart 代码维护多个平台路线二纯 KMPsharedLogic ├── model ├── network ├── repository └── usecase androidApp └── Compose / XML iosApp └── SwiftUI / UIKitKMP 共享业务逻辑UI 使用平台原生方案。适合已有 Android 和 iOS 原生项目希望渐进式共享业务逻辑希望保留原生 UI平台差异较大原生能力占比较高Android 和 iOS 团队独立维护页面路线三KMP CMPsharedLogic └── 共享业务逻辑 sharedUI └── Compose Multiplatform UI androidApp iosApp webApp desktopAppKotlin 负责业务逻辑Compose 负责共享 UI。适合团队以 Kotlin 开发者为主已经掌握 Jetpack Compose希望共享业务逻辑和普通 UI希望建立完整 Kotlin 跨平台体系可以接受部分强平台页面单独实现路线四KMP FlutterFlutter ├── 页面 ├── Widget ├── 路由 ├── 主题 └── Presentation Adapter │ ▼ Flutter Plugin / Platform Channel │ ▼ KMP sharedLogic ├── model ├── result ├── network ├── repository ├── usecase ├── validation └── storageFlutter 负责 UIKMP 负责核心业务底座。适合已有大量 Kotlin 业务代码团队希望使用 Flutter 共享 UIKMP Core 还需要服务原生 App希望把 UI 框架与业务核心分离团队能够维护 Flutter 与 KMP 的桥接层二十二、设备巡检 Demo 应该选择哪种路线这个系列后续会统一围绕“设备巡检 KMP Demo”展开。Demo 包含登录设备列表设备详情扫码识别设备地图点位巡检表单网络请求错误处理平台日志Token 存储这个 Demo 的核心目标是讲清楚从 Android 组件化 走向 KMP 跨平台业务底座因此系列主线仍然会采用KMP Core 部分 CMP UI 强平台能力平台化具体来说sharedLogic负责Device DeviceStatus ScanResult GeoPoint InspectionForm ApiResult AppError LoginRepository DeviceRepository LoginUseCase GetDeviceListUseCase SubmitInspectionUseCase ScanParser InspectionValidator TokenStoragesharedUI可以尝试负责登录页 设备列表页 设备详情页 巡检表单页 通用加载状态 通用错误页面 普通弹窗Android 平台侧负责CameraX ML Kit 高德地图 Android 权限 Android Service WebView 蓝牙 系统能力iOS 平台侧负责AVFoundation MapKit iOS 权限 Keychain iOS 系统能力但是如果后续需要使用 Flutter 作为 UI这套sharedLogic同样可以继续复用。也就是说当前 Demo UI 可以使用 CMP 以后也可以换成 Flutter UI 底层 KMP Core 不需要全部推倒重写这正是建设独立业务底座的价值。二十三、Flutter、KMP 和 CMP 到底是什么关系现在可以得到一个更完整的关系。Flutter 既可以独立完成 UI 和业务逻辑 也可以只负责 UI 调用 KMP 业务底座KMP 可以只共享业务逻辑 平台分别开发 UI 也可以配合 CMP 共享 UI 还可以配合 Flutter 共享 UI所以它们并不是简单的替代关系。完整关系可以表示为纯 Flutter Flutter UI Dart 业务逻辑 纯 KMP 原生 UI Kotlin 业务逻辑 KMP CMP Compose UI Kotlin 业务逻辑 KMP Flutter Flutter UI Kotlin 业务逻辑真正需要选择的不是某个技术名字而是UI 放在哪里 业务逻辑放在哪里 平台能力放在哪里 团队愿意承担多少桥接成本二十四、到底应该怎么选择更适合纯 Flutter如果项目符合以下特点可以优先考虑纯 Flutter从零开始页面型业务较多Android 和 iOS 页面高度统一平台能力较少团队已经熟悉 Dart希望快速完成多端交付更适合纯 KMP如果项目符合以下特点可以优先考虑纯 KMP已有原生 Android 和 iOS 项目希望逐步共享业务逻辑不希望重写现有 UI平台差异明显原生能力占比较高希望建设跨平台 SDK更适合 KMP CMP如果项目符合以下特点可以考虑 KMP CMP团队主要使用 Kotlin已经掌握 Compose希望共享业务和 UI普通页面占比较高可以接受强平台能力单独实现希望建立 Kotlin 跨平台应用体系更适合 KMP Flutter如果项目符合以下特点可以考虑 KMP Flutter已经拥有大量 Kotlin 业务代码已经建设或者准备建设 KMP CoreUI 团队更熟悉 Flutter希望 Flutter 负责跨平台页面KMP Core 还需要提供给其他客户端能够承担桥接和模型转换成本二十五、对 Android 开发者的学习建议如果已经学习 Flutter再进入 KMP不需要把它们放在一起竞争。更合理的理解是Flutter 让我拥有完整跨平台 App 开发能力 KMP 让我拥有跨平台业务底座设计能力 CMP 让我把 Compose 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 ├── Compose Multiplatform └── Flutter UI不要一开始就追求Android、iOS、Web 所有代码百分之百共享。更重要的是建立稳定的业务边界。二十六、总结Flutter、KMP 和 CMP 都可以参与跨平台开发但它们采用了不同的技术路线。Flutter 更像使用 Dart 和 Flutter 构建完整的跨平台应用。KMP 更像使用 Kotlin 建设可以被多个平台复用的业务底座。CMP 则是在 KMP 基础上进一步解决Compose UI 如何在多个平台之间共享。已经学习 Flutter 后继续学习 KMP并不是重复投入。因为Flutter 跨平台应用开发能力 KMP 跨平台业务底座设计能力 CMP Compose 跨平台 UI 能力更重要的是Flutter 和 KMP 也不是只能二选一。它们可以组合成KMP Flutter Flutter 负责 UI 和少量展示层逻辑 KMP 负责模型、网络、Repository、UseCase 和业务规则 Android / iOS 负责系统 SDK 和平台能力因此更完整的结论应该是Flutter 既可以独立负责 UI 和业务逻辑 也可以只作为 KMP 之上的跨平台 UI 层。 KMP 既可以配合原生 UI 也可以配合 CMP 还可以配合 Flutter。真正的技术选型不是判断 Flutter 和 KMP 谁能淘汰谁而是根据项目回答三个问题业务核心放在哪里 UI 放在哪里 平台差异放在哪里对于设备巡检这类项目我目前更倾向于KMP Core 负责公共业务底座 CMP 或 Flutter 负责可共享的普通 UI 地图、扫码、定位、蓝牙等强平台能力 继续留在平台侧可以用一句话概括Flutter 更擅长统一跨平台 UIKMP 更适合沉淀跨平台业务底座两者不仅可以选择其一也可以组合使用。下一篇预告下一篇将正式进入 KMP 项目结构《KMP 项目结构一次讲清commonMain、androidMain、iosMain 到底放什么》我们会重点讲清楚KMP 项目中的 Source Set 到底是什么commonMain到底可以写什么androidMain应该放哪些 Android 平台实现iosMain为什么可以直接调用 iOS APIjsMain和wasmJsMain分别是什么expect和actual到底解决什么问题sharedLogic、sharedUI、androidApp、iosApp应该如何组织Android 开发者应该如何从 Module 思维转向 Source Set 思维