我为什么用 Flutter 做中大型业务(选型与边界)
这是我 Flutter 实战系列的第一篇。
这篇不讲“语法入门”,只讲一件事:为什么我最终从 uniappx 迁移到 Flutter,并把它作为中大型业务主技术栈。
1. 问题背景:业务场景 + 现象
我负责的是一个偏“重交互”的业务项目,核心特征如下:
- 页面多、模块多(房间、聊天、支付、活动、广告、用户体系等)
- UI 复杂(大量自定义组件、动态布局、动画与状态叠加)
- 实时性要求高(音视频、WebSocket、多状态同步)
- 要求 iOS / Android 双端持续快速迭代
项目早期技术路线是跨平台方案,经历过对 RN、uniapp、uniappx 的调研和实践。最终我选择了 从 uniappx 迁移到 Flutter 重构。
迁移前我们遇到的“现象级问题”非常典型:
- 打包与构建流程重、慢、复杂,研发与测试反馈回路拉长
- 生态不成熟,关键能力缺插件或插件质量不稳定
- 复杂功能需要频繁手写原生桥接,跨端效率优势被抵消
- 复杂 UI 还原成本高,尤其在细节一致性和性能稳定性上
- 性能瓶颈明显,高频刷新场景更容易抖动/卡顿
2. 原因分析:核心原理 + 排查过程
我把问题拆成“工程效率、生态、性能、UI 可控性、长期维护成本”五个维度分析。
2.1 为什么放弃 RN(React Native)
核心原理层面:
- RN 采用 JS 逻辑 + 原生渲染的桥接模型,复杂场景下桥接通信成本不低
- 对“高频状态更新 + 动画 + 实时互动”类页面,性能调优窗口较窄
- 关键问题一旦落到原生侧,需要团队具备双端原生深入能力
排查过程中的实际感受:
- 功能实现速度并不总是快,很多时候在“桥接 + 生命周期 + 差异行为”上花掉时间
- 团队成员能力结构不一致时,排障成本会被放大
结论:RN 适合很多业务,但对我们这类“重实时 + 重复杂交互”的中大型项目,长期维护压力偏高。
2.2 为什么放弃 uniapp / uniappx
先说结论:uniappx 有进步,但在我们项目类型上仍不够“工程化可控”。
放弃 uniapp 的主要原因
- 复杂 UI 还原和性能稳定性在高要求场景下不理想
- 组件/插件能力与项目深度需求之间存在落差
放弃 uniappx 的主要原因(本项目真实痛点)
- 打包、构建繁重
- 构建链条复杂,调试反馈周期偏长
- 生态差距明显
- 业务常用能力的成熟插件不足,或兼容性/可维护性一般
- 大量插件需要手撸原生
- 本应“跨平台省成本”,但实际变成“跨平台 + 原生双倍维护”
- 性能瓶颈
- 在多模块叠加、复杂渲染与高频刷新场景下,优化上限受限
- 复杂 UI 还原难度高
- 高精度视觉稿和动态特效落地成本高,且双端一致性难控
结论:uniappx 对轻中型业务有其价值,但对于我们这种中大型、复杂交互项目,风险和隐性成本偏高。
3. 解决方案:方案对比 + 最终选择
3.1 备选方案对比(简版)
| 维度 | RN | uniapp / uniappx | Flutter |
|---|---|---|---|
| 复杂 UI 还原 | 中 | 中-偏低 | 高 |
| 实时互动性能 | 中 | 中-偏低 | 高 |
| 生态与稳定插件 | 中-高 | 中 | 高 |
| 原生依赖成本 | 中-高 | 中-高 | 中 |
| 团队长期维护 | 中 | 中-偏低 | 高 |
3.2 最终选择:Flutter
我最终选 Flutter,不是因为“它万能”,而是它在我们的业务约束下更均衡:
- 渲染层可控,复杂 UI 还原能力强
- 动画与交互表达力好,统一性强
- 生态成熟,主流业务能力可快速落地
- 架构可塑性好,适合中大型分层治理
- 与原生互通路径清晰,关键能力可兜底
3.3 同时明确边界(很重要)
Flutter 也不是银弹,下面这些边界要提前接受:
- 超深原生能力(特别是系统级、厂商定制能力)仍需原生开发
- 包体、启动、内存等仍要做工程化治理
- 团队要统一代码规范与架构,不然项目后期照样会乱
4. 关键代码:最小必要代码片段
这篇偏“选型”,代码只给最小骨架,展示我在 Flutter 项目里如何做“中大型可维护分层”。
4.1 模块化目录(示意)
lib/
modules/
room/
pages/
widgets/
view-model/
state/
server/
wallet/
pages/
widgets/
view-model/
server/
common/
api/
http/
router/
startup/
4.2 网络层与业务层分离(示意)
class OrderServer {
static Future<Map<String, dynamic>?> applePayOrder(Map<String, dynamic> body) async {
final response = await request('/pay/apple/order', params: body, isLoading: false);
return response;
}
}
class WalletViewModel extends StateNotifier<WalletState> {
WalletViewModel() : super(WalletState.initial());
Future<void> createAppleOrder(String productId) async {
final data = await OrderServer.applePayOrder({
'productId': productId,
'businessType': 'diamond',
});
// 业务状态只在 VM 内收敛
state = state.copyWith(lastOrderNo: data?['orderNo']);
}
}
4.3 页面只做“状态消费与事件分发”(示意)
class WalletPaidProductPage extends ConsumerWidget {
const WalletPaidProductPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(walletProvider);
final vm = ref.read(walletProvider.notifier);
return ElevatedButton(
onPressed: () => vm.createAppleOrder(state.selectedProductId),
child: const Text('确认充值'),
);
}
}
这套分层的目的:把“页面复杂度”压下来,把“业务复杂度”放进可测试、可演进的 ViewModel/Server 层。
5. 效果验证:数据 / 截图 / 日志
以下是我迁移后重点关注的验证项
5.1 工程效率
- 本地调试反馈速度是否提升
- 构建成功率是否更稳定
- CI 构建失败率是否下降
5.2 体验与性能
- 列表滑动帧率是否稳定
- 复杂页面首帧时间是否可接受
- 高并发状态更新时是否出现明显抖动
5.3 交付质量
- 同类线上问题重复率是否下降
- 原生桥接故障占比是否下降
- 新需求从评审到上线的周期是否缩短
5.4 建议附上的证据
flutter run --profile关键日志- DevTools 截图(CPU / Memory / Frame)
- 发版前后 crash 统计对比图
- 构建时长对比表
6. 可复用结论:通用经验 + 避坑清单
6.1 通用经验(可直接复用)
- 选型先看业务形态,不先看“技术热度”
- 跨平台效率不等于全生命周期效率(开发快 ≠ 维护快)
- 中大型项目优先考虑“可治理性”(分层、规范、测试、CI)
- 复杂交互项目,渲染与性能上限是关键指标
- 要有原生兜底能力,但不要把 60% 工作都逼回原生
6.2 避坑清单(血泪版)
- 不要在选型初期只做 TodoDemo,对真实业务压测太少
- 不要忽略插件维护活跃度和社区成熟度
- 不要让页面直接承载业务逻辑(后期必崩)
- 不要晚做性能基线,越晚成本越高
- 不要把“迁移”当成纯技术任务,必须同步业务节奏和测试资源
结语
我选择 Flutter 的核心原因不是“它最先进”,而是它在我们项目里做到了:
- 复杂 UI 可控
- 性能可优化
- 工程可治理
- 长期可维护