在这里插入图片描述

网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


引言

很多人第一次搭 Flutter 项目时,都感觉很舒服:
目录不多、层级清晰、代码集中,甚至几天就能把业务主流程跑起来。

但只要项目活得足够久,几乎一定会进入另一个阶段:结构没变复杂,但改动却越来越痛苦。

这并不是 Flutter 独有的问题,而是不同技术栈在“结构设计”上的共同演进轨迹。

项目初期的“干净感”,其实是一种错觉

大多数 Flutter 项目一开始都是典型的三层或四层结构:

lib/
 ├─ pages/
 ├─ widgets/
 ├─ models/
 ├─ services/

看起来非常合理:
页面放 pages,通用组件放 widgets,数据结构放 models,请求逻辑放 services。

问题在于——这个结构是按“技术类型”拆的,而不是按“业务边界”拆的。

当只有三五个页面时没问题,但当页面变成三十个、五十个:

  • widgets 目录会越来越臃肿
  • services 开始出现跨业务调用
  • models 逐渐变成“公共垃圾场”

结构依旧整齐,但修改一个功能要跨四五个目录跳转,维护成本迅速上升。

前端 Feature First 为什么能长期稳定

很多前端团队早就经历过类似阶段,所以逐渐演化出 Feature First 结构:

features/
 ├─ user/
 │   ├─ api/
 │   ├─ components/
 │   ├─ pages/
 │   └─ store/
 ├─ order/
 └─ home/

核心思想只有一句话:

按业务切,而不是按技术切。

这样带来的好处非常直接:

  • 新人只看一个目录就能理解一个完整功能
  • 删除一个业务不会影响其他模块
  • 依赖关系天然被收敛在 feature 内部

当然,它也不是没有代价:

  • 公共依赖管理更复杂
  • 模块之间通信需要额外设计
  • 构建速度可能下降

但在中大型项目周期里,这些成本远小于后期维护混乱带来的损失。

iOS 模块化为什么清晰,却很难推广

iOS 生态更早进入模块化阶段,常见做法是:

  • 按业务拆分独立 Module
  • 通过路由或协议通信
  • 每个模块独立编译

优点非常明显:

  • 编译隔离
  • 依赖边界清晰
  • 复用能力强

但真正落地时,团队往往会遇到两个现实问题:

第一是开发成本高。

拆一个模块不仅是目录移动,还涉及:

  • 工程配置
  • 依赖管理
  • 版本控制

第二是业务变化太快。

很多需求生命周期只有几个月,过度模块化反而增加了维护负担。

所以你会看到一个很真实的现象:

架构越“完美”,越难在快速迭代的团队里长期坚持。

Flutter 在项目拆分上的真实难点

Flutter 既不像前端有成熟的 Feature 生态,也不像 iOS 有完整的模块化工程体系。

最常见的卡点其实只有三个。

依赖很容易无边界扩散

Dart import 没有强约束,只要路径能写到,就能直接引用。

结果就是:

  • UI 直接调 service
  • service 反向依赖 widget
  • 工具类逐渐变成“万能入口”

结构看似没问题,但依赖图已经悄悄失控

公共组件越来越难抽

很多团队都会经历这个过程:

  1. 先写业务页面
  2. 发现可以复用
  3. 抽到 common/widgets
  4. 参数越来越多

最后得到一个:

谁都能用,但谁改都怕崩的组件。

这其实不是代码问题,而是抽象时机过早

拆 package ≠ 真正模块化

Flutter 支持 package,看起来像天然模块化方案。
但现实是:

  • package 之间仍然可以强依赖
  • 版本管理成本高
  • 本地调试流程复杂

如果没有清晰的业务边界设计,拆多少 package 都只是形式变化。

一套更适合长期演进的目录实践

真正能让 Flutter 项目活得久的结构,通常长这样:

lib/
 ├─ core/        // 全局能力:网络、路由、主题
 ├─ shared/      // 纯通用组件(极少)
 ├─ features/
 │   ├─ home/
 │   ├─ user/
 │   └─ order/
 └─ app.dart

这里有几个关键原则。

先限制 shared,而不是扩展它

shared 只允许放:

  • 无业务语义的 UI
  • 纯工具方法

一旦出现业务词汇,必须回到 feature 内。

这样才能避免“公共层膨胀”。

feature 内部自闭环

一个 feature 目录应该包含:

user/
 ├─ data/
 ├─ logic/
 ├─ ui/
 └─ user_router.dart

目标只有一个:

离开这个目录,就能完整删除一个功能。

如果做不到,说明边界还没切干净。

core 只做基础设施

core 常见内容:

  • 网络封装
  • 日志
  • 路由框架
  • 主题配置

绝不放具体业务逻辑。

否则 core 会迅速变成新的“巨石层”。

结构设计,本质是团队协作设计

很多人把目录结构当成技术问题,但真正决定结构能否长期稳定的,其实是三件事:

  • 需求变化速度
  • 团队规模
  • 代码评审文化

没有任何一种结构是“最优解”,只有是否匹配当前阶段

总结

Flutter 项目之所以常见“前期干净,后期难改”,并不是框架问题,而是:

我们太习惯按文件类型思考,而不是按业务边界思考。

当项目进入中后期,真正需要升级的从来不是目录层级,
而是对模块边界、依赖方向、复用时机的理解。

结构不是为了好看,而是为了让三年后的自己,依然敢改今天写下的代码。

Logo

加入「COC·上海城市开发者社区」,成就更好的自己!

更多推荐