前言
最近我在考虑一个问题:
跨端开发,能不能既保留共享代码带来的效率,又尽可能保留 Android 和 iOS 的原生交互体验? 在对比 uniapp 、uniappx 、Flutter 、React Native 、Kuikly后
我把目光最终放到了 Kotlin Multiplatform。
但打开 KMP 以后,熟悉的感觉又来了。
commonMain、androidMain、iosMain、Compose Multiplatform、Kotlin/Native......
如果按照几年以前我学习新技术的方式,大概率是:

-
看官方文档。
-
收藏几篇教程。
-
再看两个 Demo。
-
B站大学相遇
-
最后 Hello World 跑起来了,但真正的项目还是没开始。
这一次我换了一种方式。
我没有先学完 KMP。
而是直接打开 TRAE Work,对它说:
我想通过一个真实的 Kotlin Multiplatform UX 项目学习 KMP。
我想知道:
Kotlin Multiplatform + Compose Multiplatform,在共享大量 UI 和业务代码时,Android 和 iOS 能不能仍然保留接近原生 App 的交互手感?
于是,TRAE Work 开始了 MotionFlow 的制作。
01|我没有让 TRAE Work 上来就写代码
MotionFlow 是一个很简单的旅行灵感 App。
我故意只设计了三个页面:
- Explore:旅行目的地浏览
- Detail:目的地详情
- Planner:旅行计划
但功能不想做得太"Demo"。
我真正想验证的是这些东西:
横向 Pager、卡片缩放、拖拽 Bottom Sheet、Haptic、收藏状态、系统分享,以及 Android / iOS 的平台能力。
所以第一步,我没有让 TRAE Work 直接生成代码。
而是要求它先查 KMP、Compose Multiplatform 以及 Android/iOS 当前的实现方式,再给我一份完整技术方案。

它最后生成了一份完整的 MotionFlow KMP 方案。
里面把能力分成了两类:
适合共享的:
UI、动画、导航状态、数据模型、业务逻辑。
应该保留平台实现的:
Haptic、图片选择、系统分享以及必要的 Android/iOS 平台能力。

这一步反而让我先理解了 KMP 最重要的一件事:
KMP 的目标不是把所有代码都塞进 commonMain。
真正重要的是:
什么值得共享,什么应该留给平台。
02|写第一行业务代码之前,我让 4 个"虚拟同事"先开了次会
技术方案出来以后,我又让 TRAE Work 重新审了一遍。
这一次,我要求它同时站在四种角色上:
- Android 原生工程师
- iOS 原生工程师
- KMP 架构师
- 移动端 UX 工程师
去检查:
哪些东西共享过头了?
哪些平台差异根本不应该被抹平?
哪些 Compose 能实现,但原生方案体验会更好?

我很喜欢这种用法。
以前学一个新技术时,我最容易陷入的问题是:
刚学会什么,就想什么都用它实现。
而 TRAE Work 在这里更像是在提前帮我踩刹车。
比如 Haptic。
最终的思路不是:
Android 和 iOS 都震 30ms。
而是:
commonMain 只定义:
Selection
Impact
Success
至于 Android 到底怎样反馈,iOS 又应该使用什么触觉效果,各自在平台层实现。
共享的是业务语义,不是强行让两个平台一模一样。
03|然后才真正开始创建 KMP 项目
架构确认以后,我才让 TRAE Work 开始创建 MotionFlow。
它先检查了我的开发环境:
JDK、Android SDK、Xcode、CocoaPods......
然后开始生成 KMP 项目骨架。

当然,没有"一次生成直接成功"这种童话。
搭项目过程中先后遇到了 SDK、Kotlin/Native 缓存、Navigation 版本、Objective-C 长度环境、Framework target 等问题。
其中有一次 Android SDK 目录本身还有权限问题,TRAE Work 最后调整了 compileSdk 才继续往下走。

我特意让它创建了一个:
LEARNING_LOG.md
每解决一次真正的问题,就记进去:
出了什么错
为什么出错
改了什么
最后怎么解决
等项目骨架完成时,这里面已经记录了多次真实踩坑。
这比看十篇"5 分钟学会 KMP"有用得多。
因为很多时候:
一个技术真正开始被理解,恰恰是在它第一次跑不起来的时候。
04|第一次让我觉得"这个 App 开始有样子了"的,是 HorizontalPager
项目骨架打通之后,我开始做第一个真正的页面:
Explore。
最初只是普通列表。
我让 TRAE Work 把它重新改成 HorizontalPager:
- 横向滑动
- 页面吸附
- 当前卡片放大
- 相邻卡片缩小
- 页面指示器
- 点击进入详情页
而且这些代码全部放在 commonMain。
没有引入第三方轮播组件。

Android 编译通过。
iOS 编译通过。
commonTest 也通过。
接着 TRAE Work 又帮我补齐 Xcode 项目,把 App 安装进 iOS Simulator。
第一次启动时,还经历了一点小插曲。
模拟器明明已经启动了,但截图里还是桌面。
TRAE Work 又继续检查进程、重新拉起 App、等待 Compose UI 加载。
直到最后:
MotionFlow 真正在 iPhone Simulator 上跑了起来。


看到 Kyoto 卡片、缩放效果和下面的页面指示器出现在 iPhone 上的时候,我第一次有了一种很明确的感觉:
这已经不再是"我正在学 KMP"了。
我是真的在做一个 KMP App。
这两件事的心态完全不一样。
05|真正让我理解 KMP 的,反而不是 UI
Explore 跑起来以后,我继续让 TRAE Work 做 Detail。
这一阶段开始加入:
- 收藏状态
- Hero 区域
- 可拖拽 Bottom Sheet
- Haptic
- 分享接口
其中我最喜欢的一部分,是 PlatformServices 的设计。
在 commonMain 中,我只关心:
scss
triggerHaptic(type)
shareDestination(destination)
具体到了 Android:
Haptic 走 Android 平台实现。
到了 iOS:
Haptic 走 iOS 的 Feedback Generator。
分享也是一样。
Android 用 Android 的方式。
iOS 用 iOS 的方式。
这一下我终于真正理解了 KMP 那几个看起来很抽象的目录。
以前看到:
commonMain
androidMain
iosMain
我只能背定义。
现在它们突然有了非常具体的意义:
commonMain 说"我要做什么"。
平台层决定"在我的系统上应该怎么做"。
这可能是我这次学习 KMP 最大的收获。
06|Bottom Sheet 也让我重新理解了"跨端"
Detail 页面里,我还让 TRAE Work 实现了一个可拖拽 Bottom Sheet。
它没有为了省事直接堆第三方库。
而是用 Compose 自己做拖拽状态和吸附逻辑:
Collapsed
↓
Half
↓
Expanded
拖动时实时更新位置。
松手以后,根据当前位置自动吸附。
吸附完成再触发对应的 Haptic。

这里有一个我之前一直容易想错的地方。
我以前会把"跨端"理解成:
两个平台最好所有东西完全一致。
但做完 MotionFlow 以后,我反而越来越觉得:
代码共享和体验一致,并不是一回事。
一个好的跨端方案应该是:
业务逻辑可以共享。
UI 可以尽可能共享。
但真正和平台习惯强相关的能力,该交给 Android 就交给 Android,该交给 iOS 就交给 iOS。
共享不是目的。
把重复劳动降下来,同时不牺牲体验,才是目的。
07|这次 TRAE Work 最有价值的地方,不是"帮我写代码"
回过头看整个过程,TRAE Work 当然写了很多代码。
但如果只是"一键生成项目",我觉得这篇文章其实没什么值得写的。
真正让我觉得它适合拿来学习一个陌生技术栈的,是整个过程:
查官方资料
↓
设计架构
↓
评审共享边界
↓
检查本机环境
↓
创建项目
↓
处理构建错误
↓
Android / iOS 编译
↓
运行 iOS Simulator
↓
继续完成真实交互
以前我学习新框架最大的问题不是看不懂。
而是:
从"我大概知道它是什么",到"我真的做出了一个东西",中间有一道非常长的断层。
环境问题。
版本问题。
Gradle 问题。
Xcode 问题。
平台 API 问题。
任何一个地方卡住,都很容易重新回去继续"看教程"。
而 TRAE Work 在这次项目里真正帮我缩短的,就是这段距离。
08|如果让我重新学一次新技术,我还会这么做
这次 MotionFlow 没有后端。
没有数据库。
甚至连网络请求都没有。
但我反而觉得它比一个功能很多的 Demo 更适合学习 KMP。
因为整个项目一直围绕一个问题:
KMP 到底怎么在共享代码和平台体验之间做取舍?
为了回答这个问题,我真正碰到了:
- Compose Multiplatform
- commonMain
- androidMain
- iosMain
- HorizontalPager
- Navigation
- PlatformServices
- Haptic
- Bottom Sheet
- Kotlin/Native
- Android/iOS 编译链
这些东西不再是文档里的名词。
而是项目里真正用到的东西。
所以如果以后再学习一个陌生框架,我大概不会再从:
"先把所有基础知识学完。"
开始。
而会先问:
我能不能先设计一个足够小、但真正能碰到核心能力的项目,然后让 AI 陪我把它跑通?
最后
一开始,我只是想学 Kotlin Multiplatform。
如果按照原来的方式,我现在可能还在看:
commonMain 到底是什么?
但这一次,我没有先把 KMP 学完。
我直接让 TRAE Work 和我一起做了 MotionFlow。
从一份需求。
到架构方案。
到本机环境。
到 Gradle 和 Xcode。
到 Android/iOS 双端编译。
再到 HorizontalPager、Haptic 和可拖拽 Bottom Sheet。
最后,一张真正的旅行卡片出现在了 iPhone Simulator 上。
那一刻我突然觉得:
AI 辅助学习技术最有价值的地方,可能不是让我们更快"知道"。
而是:
让"我想学这个",更快变成"我已经做出了第一个实例东西"。
而对我来说,
MotionFlow 才刚刚开始,只是做了一个小小小实例demo。
本次app其他还有很多页面没有展示,但是我只想验证 KMP 的 UX 效果,页面多少无所吊谓
至少这一次,我已经不再只是收藏等于学会了。 
下次再见!🌈
