供稿来自:@王柯、@张文森、@王林、@费高联
背景&契机
- 随着纯血鸿蒙的推出,客户端跨平台需求被推到了前所未有的高度,业内都在如火如荼地做着各类跨平台改造。(字节开源Lynx、腾讯开源ovCompose、B站KMP实践、京东Taro)
- 我们鸿蒙端书城功能是落后双端很多的,需要尽快追齐。以上两点作为契机,我们进行了本次书城改造,对鸿蒙端来说是做新需求,对双端来说,是做次重构。开发中大量运用了AI,跨端方案选型为Flutter 七猫跨端方案选型,本文主要是总结下经验,希望这套开发方案能更多地推广,最后极大幅度提升客户端开发效率。
成果


现状 & 改进
客户端AI为什么不好用?影响研发效率的有哪些客观原因?

为什么AI更容易理解声明式UI?
总的来说声明式UI更简洁,且各端近年主推都是声明式UI。(安卓Compose、苹果SwiftUI、鸿蒙ArkUI、Flutter)

提效的思路

实践
工程AI化目录结构如下
.cursor/
├── docs
│ ├── book-store
│ │ ├── knowledge
│ │ │ └── book-store-ui-solution.md
│ │ └── request
│ │ ├── book-store-discover-request.md
│ │ ├── book-store-leader-board-request.md
│ │ ├── book-store-must-read-request.md
│ │ ├── bs-config-request.md
│ │ ├── bs-novel-request.md
│ │ ├── bs-rank-request.md
│ │ ├── bs-tag-books-request.md
│ │ ├── bs_listening_request.md
│ │ ├── bs_novel_load_more_request.md
│ │ └── bs_tab_listening_load_more_request.md
│ ├── common
│ │ └── widget-overview.md
│ └── templates
│ └── generate-request-dto.md
├── mcp.json
└── rules
├── code-quality.mdc
├── code-spec.mdc
├── gitflow.mdc
└── preferences.mdc
Rules

Docs
文档统一以.md
形式,Agent会话中按需取用。
- BookStore (每个模块新建一个目录用于存放对应知识,可以有Reader、AD、Community、User等):
- Knowledge: 存放书城技术文档,如类结构、目录结构等
- Request: 存放接口定义
- Templates: 存放通用模版代码
- Common: 存放项目公共知识
现代化项目架构:数据分层
刚才我们讲到:cursor在原生开发上水土不服的原因之一就是项目缺少明显的架构分层,导致类文件代码膨胀,AI难以正确理解。
现代化的前端界面开发的共识就是数据驱动,这种思想也充分体现了软件设计中的关注点分离原则,提升了软件的可维护性,有利于将前端工程中的复杂状态管理标准化。
书城Flutter开发中,我们将数据分成了3层:
DTO
: 服务端返回的网络数据POJO
:本地持久化的数据VO
:UI
层使用的数据
传统的原生开发者往往喜欢在UI层直接使用服务端 or 本地数据库的数据,这么做并不合理,将业务逻辑与用户界面代码混在一起,只会使代码更加难以测试、调试和维护。
我们以书城单个feed流为例:

如此,当我们看到某个类,就知道其用于什么场景,需要拓展/调整功能,也可以快速搜索定位。
此外,Repository层的代码、BookVO以及Widget层的代码,cursor都可以根据实际情况精准生成,我们需要做的,仅仅是将dto代码映射为vo层的mapTo
工作,下面我们举🌰说明。
面向模版开发:精准生成request及dto代码
没有cursor时,为了解决无聊的重复代码编写工作,我们往往会定义许多模版代码,使用 ctrl c + ctrl v,然后根据具体业务改吧改吧就能用了,比如接口请求的request以及dto类,这个方法在cursor中能变得更加好用:
- 在.cursor/docs中创建“代码模版”,指导其生成对应代码;
- 根据不同业务配置模版代码中的变量,控制cursor智能能生成对应业务代码。
以小说tab接口为例:
# generate-request-dto.md
用于指导根据`curl`以及对应请求响应返回的`json`生成对应的request文件以及对应的dto文件。
## 步骤
### 1.生成 dto 文件
首先需要根据用户输入的json文件生成对应的`dto`文件,文件存放至 `%mf`路径下,若该路径不存在,请先创建对应文件夹目录(%mf为占位符,由用户输入)。
注意:dto相关字段需手动从map中解析,不使用`json_serializable`及`json_annotation`等package,注意嵌套json对象的解析,原则上dto中的字段类型不能为Map类型。
### 2.生成 request 文件
其次,需要你根据`curl`以及上一步生成的`dto`类生成对应的`request`文件,文件存放至 `%rf`路径下,若该路径不存在,请先创建对应文件夹目录(%rf为占位符,由用户输入)。
生成的request文件需要严格遵循下面模版:
```dart
import 'package:loktar/network/request.dart';
import 'package:loktar/channel/network_channel.dart';
class %r extends Request<%m> {
final %t %s;
%r(this.%s);
@override
Response<%m> fromJson(Map<String, dynamic> json) {
final error = json['error'] != null ? ResultError.fromJson(Map<String, dynamic>.from(json['error'])) : null;
final data = json['data'] != null ? %m.fromJson(Map<String, dynamic>.from(json['data'])) : null;
return Response<%m>(dataObject: data, resultError: error);
}
@override
bool get isSign => true;
@override
HttpMethod get method => HttpMethod.get;
@override
Map<String, dynamic> get parameters => {
};
@override
DomainName get domain => DomainName.gw;
@override
String get path => %p;
@override
Map<String, String> get headers => {};
}
```
## request文件相关说明
### 占位符说明:
[省略内部细节逻辑]
### 请求参数说明
[省略内部细节逻辑]
使用时,根据具体接口,我们创建具体文档输入模版中的变量,指导cursor生成对应代码:
# bs-novel-request.md
书城推荐tab接口请求返回json以及curl示例。
你需要根据该json以及curl生成`bs_feed_dto.dart`以及`bs_novel_request.dart`两个文件:
- %m: BSFeedListDTO
- %r: BSTabNovelRequest
- %mf: lib/book_store/dto/bs_feed_dto.dart
- %rf: lib/book_store/request/bs_novel_request.dart
详细的生成规则请严格遵循 @.cursor/docs/generate-request-model.md 要求
## json示例
```json
[省略内部细节逻辑]
```
## curl
```
[省略内部细节逻辑]
```
将bs-novel-request.md拖入cursor的聊天界面,cursor会帮我门生成对应的代码:

此次Flutter书城开发过程中,共有10
个接口的 request及其dto代码,约2100
行代码,全由cursor自动生成,由于模版提供了明确的边界,这些代码完全不用修改就可直接使用。
业务组件沉淀:图生代码的最佳实践
传统的Android View体系由于组件继承体系的限制,使得自定义业务组件复杂度高,成为奢望。反观Flutter这种声明式的UI框架,通过不同的widget间的自由组合,使得我们可以沉淀出项目自己的组件库,同时这种声明式ui将组件状态与样式充分解耦,做到布局即代码,消除XML与Java/Kotlin之间的逻辑割裂。
在开局一张图,用Cursor生成代码?一文中,我们也提到过这种声明式UI对于cursor生成代码的友好。
以小说tab中的相关推荐书籍举个🌰:

代码秽土转生:利用Cursor翻译现有代码
书城flutter项目开发时,由于缺少项目文档,很多情况下我们需要“逆向开发”:先梳理 Android/iOS代码 逻辑,再输出Flutter代码,这个过程往往复杂又痛苦。
但是借助Cursor ,我们只需要把 Native 代码通过 @file 输送给 Cursor,AI 工具就能快速理解 Native 代码,并结合现有的业务逻辑,完成Native 代码到 Flutter 代码的转化。
以发现页负反馈弹框为例:

展望
推广整体策略
新页面:直接使用AI + Flutter化的方案开发 (如正在做的勋章等)老页面:Flutter开发后,先在鸿蒙端上线,再推全到双端
书城Flutter化二期

免费小说客户端后续改造

下阶段目标
AI + Flutter生态完全铺开后,客户端开发综合提效150%。(200% - 100%) *1.5=150%
总结 & 风险
- 互补:脱离AI时,对于客户端研发,Flutter是个新的框架,需要不少成本去学习,但有AI加持,这个过程大大加速了,本次实践中,大多数伙伴也是从未写过一行Flutter代码的。
- 化学反应:AI + Flutter整体契合度非常高,写新需求+修bug体验都非常好。
- 潜在的风险:诚然,这套开发模式提效潜力较大,但Flutter本身是个复杂度极高的框架,随着渗透率的不断提高,未来会遇到各种复杂的问题,技术栈如何维护、下一步的演进方向是什么,是需要思考的问题。
FAQ
- 是否只能用于新需求开发,复杂项目维护、修bug是否用不了? 良好的软件工程设计下,可以。
- Flutter不能接商业化吧?插页广告要接怎么办? 三端都可以接商业化,使用PlatformView,我们在鸿蒙端也做过了类似验证。
- iOS 26推出了液态玻璃效果,Flutter是否天塌了? 技术上并非不可实现,后续通过三方UI组件方式推进,参考Flutter应如何实现液态玻璃