00 · 学习路线图
这是全系列的第一篇,也是唯一一篇必读的索引。 它回答三个问题:按什么顺序学、每周该到什么程度、哪些坑文档已经替你踩过了。
一、30 秒速览:这个项目是什么
HaierEnergy 是一个生产级 Flutter App(包名 com.haier.energy.platform,Dart 包名 haierenergy),由 uni-app 老项目迁移而来。
| 维度 | 规模 |
|---|---|
| Dart 文件 | lib/ 449 个(data/ 346、ui/ 348、he_components/ 97) |
| 业务模块 | 设备管理、电站、家庭、参数下发、高级功能、用户中心 |
| 技术栈 | Riverpod 2.6 + GoRouter 14 + Freezed 2.5 + Retrofit 4 + Dio 5 |
| 测试 | test/ 目录镜像 lib/,ui/ 下 81 个测试文件 |
| 团队规模痕迹 | 有编码规范、守护脚本、Figma token 漂移检测、Apifox 契约校验 |
这意味着什么:你不是在看一个 Demo,而是在读一个真实的中大型工程。好处是学到的都是能直接用的;坏处是没人会手把手教你 Dart 语法——所以本系列第 01、02 章就是补这个的。
二、按周学习路径
假设你每天能投入 2~3 小时。如果你的时间不连续,按「阶段」而非「周」推进即可。
第 1 周:能读懂代码
| 天 | 内容 | 产出 |
|---|---|---|
| 1 | 01-Dart语言基础 前半(类型、空安全、final/const) | 能看懂任意一个 lib/ 下文件的变量声明 |
| 2 | 01 后半(异步、集合、级联、sealed) | 能读懂 lib/domain/models/result.dart |
| 3 | 02-Flutter核心基础 前半(Widget 树、Stateless/Stateful) | 能说出 ConsumerWidget 与 StatelessWidget 的区别 |
| 4 | 02 后半(约束布局、常见溢出、BuildContext) | 遇到 RenderFlex overflowed 知道往哪查 |
| 5 | 通读 lib/ui/home/views/agent_home_page.dart(先不求甚解) | 能指出哪几行是「取状态」、哪几行是「发请求」 |
周末自检:不查文档,能解释 Result<T> 是什么、AsyncValue 有哪三态。
第 2 周:补通用内功(项目里没写,但你不能不会)
项目用 Riverpod + GoRouter 这类重框架,导致下面这些通用知识在代码里几乎找不到踪影——但不学,你第三个页面就会卡住:
| 天 | 内容 | 为什么必须学 |
|---|---|---|
| 1 | 03-Flutter渲染原理与生命周期 | 项目大量 ConsumerWidget(无状态),你可能写完三个页面都没写过一次 initState |
| 2 | 04-Flutter布局与滚动进阶 | 一旦要做「滚动时折叠的详情页头」,ListView 就不够用了 |
| 3 | 05-Flutter交互与动画 | Flutter 没有 DOM 事件冒泡,手势靠「竞技场」抢,这是前端最大的思维差异点 |
| 4 | 06-Dart异步并发与进阶语法 | Dart 是单线程事件循环,Isolate 是唯一真并发手段 |
| 5 | 回头重读 agent_home_page.dart | 这次应该能看懂 80% 了 |
为什么「Dart 异步」排在 Flutter 三篇之后,而不是紧跟 Dart 基础? 因为事件循环、Isolate、Stream 需要「Flutter 单线程渲染模型」这个前置认知才好理解。放在
02之后立刻讲会非常枯燥且没有上下文;放在03~05之后,你已经见过渲染管线,再讲事件循环就有落点了。
第 3 周:摸清项目骨架
| 天 | 内容 | 产出 |
|---|---|---|
| 1 | 07-项目架构总览 | 能在目录树里 10 秒内定位到任意功能 |
| 2 | 08-状态管理Riverpod | 能自己写一个 @riverpod class |
| 3 | 09-数据层链路 | 能新增一个 Repository 方法 |
| 4 | 11-组件与主题 | 知道 88 个 He 组件里哪个能直接用,不再重复造轮子 |
| 5 | 12-分页与列表 | 能照 PagedListBody 写一个新的分页列表 |
周末自检:能独立创建一个带分页列表的新页面(可以先不接接口,用 Mock)。
第 4 周:能独立交付
| 天 | 内容 | 产出 |
|---|---|---|
| 1 | 10-页面开发实战 | 端到端走通「路由 → 模型 → ViewModel → 页面 → 接口」 |
| 2 | 13-路由与导航 | 能注册新路由、传参、处理返回 |
| 3 | 14-国际化 | 新增一条文案走通全流程 |
| 4 | 15-权限与设备能力 | 知道业务权限和系统权限是两套东西 |
| 5 | 16-工程化与调试 + 17-常见陷阱与速查表 | 能本地出包、能自己排查常见报错 |
毕业标准:从零新增一个完整业务模块(含列表页、详情页、接口对接、文案、权限),通过 bash scripts/precheck.sh。
三、章节依赖关系
flowchart LR
A["00 路线图"] --> B["01 Dart 基础"]
B --> C["02 Flutter 核心"]
C --> D["03 渲染原理"]
C --> E["04 布局滚动"]
C --> F["05 交互动画"]
D --> G["06 异步并发"]
C --> H["07 项目架构"]
H --> I["08 Riverpod"]
H --> J["09 数据层"]
I --> K["10 页面实战"]
J --> K
H --> L["11 组件主题"]
K --> M["12 分页列表"]
K --> N["13 路由导航"]
H --> O["14 国际化"]
H --> P["15 权限设备"]
L --> Q["16 工程化"]
M --> R["17 速查表"]
Q --> R三条可选捷径:
- 急着改 bug(第 1 天就要动代码):
02→07→17,先能跑起来再说 - 只要能写页面(不做架构决策):
01→02→08→10→11 - 要接接口:
01→07→09(可跳过 03~06)
四、练习怎么跑
本系列的练习分两种载体,不要搞混:
载体 A:lib/main_playground.dart(章节 03~06)
专为本系列新建的空白练习场,不属于业务代码,练完可删。
fvm flutter run -t lib/main_playground.dart把文档里的 Demo 粘进 PlaygroundPage 即可运行。
⚠️ 注意:
lib/main_test.dart不是练习场。它是「测试环境后端」的 App 启动入口 (appMain(forcedEnvironment: Environment.test)),会拉起整个 App。同理main_prod.dart是生产环境入口。这两个都是正式文件,不要拿来做实验。
playground 刻意只包 MaterialApp,不包 ProviderScope / 主题 / i18n delegate——所以:
| 在 playground 里写 | 结果 |
|---|---|
| 纯 Flutter Widget(Row/Column/ListView 等) | ✅ 正常 |
ref.watch / ref.read | ❌ No ProviderScope found |
He 组件、AppTokens.of(context) | ❌ 取不到(null) |
RuntimeI18n.ofNonNull(context) | ❌ assert 失败 |
这个限制不是缺陷而是设计:03~06 章全是通用知识,本来就不该依赖项目框架。
载体 B:test/ 下的 widget test(章节 07~17)
项目已有完整测试目录,镜像 lib/ 结构。改一改就能跑:
fvm flutter test test/ui/core/widgets/paged_list_body_test.dart重点可参考的现成用例:
test/ui/device/view_models/device_list_view_model_test.darttest/data/repositories/device/device_repository_test.darttest/core/i18n/runtime_i18n_engine_test.darttest/ui/core/widgets/paged_list_body_test.dart
五、项目红线(写代码前必须知道)
这些来自项目 AGENTS.md 与 docs/coding-standards.md,违反会在 code review 被打回:
| # | 红线 | 正确写法 |
|---|---|---|
| 1 | 所有命令加 fvm 前缀 | fvm flutter pub get(不是 flutter pub get) |
| 2 | 颜色/间距/字体必须走 token | AppTokens.spacingMd(不是 SizedBox(height: 16)) |
| 3 | 图片资源用强类型引用 | Assets.images.xxx.image()(不是 Image.asset('...')) |
| 4 | 业务权限走 hasPermission | ref.watch(authViewModelProvider).hasPermission('app:b:addPlant') |
| 5 | 日志用 AppLogger | AppLogger.I.d(...)(不是 print()) |
| 6 | 生成文件要提交 | *.g.dart、*.freezed.dart、lib/generated/assets.gen.dart 全进 git |
| 7 | 出包走脚本 | scripts/build.sh android --release(不直接 fvm flutter build) |
| 8 | 构建前跑预检 | bash scripts/precheck.sh |
六、依赖版本基线(写错了会报莫名其妙的错)
照着网上的教程敲代码报错时,先查这张表——版本不对是新手报错的头号原因。
| 依赖 | 本项目版本 | 提醒 |
|---|---|---|
| Flutter SDK | >=3.4.3 <4.0.0 | |
flutter_riverpod / riverpod_annotation | ^2.6.1 | 是 2.x 不是 3.x!语法差异极大 |
dio | ^5.7.0 | |
retrofit | ^4.4.1 | |
go_router | ^14.6.2 | |
freezed | ^2.5.7 | |
flutter_screenutil | ^5.9.3 | designSize Size(750, 1624) |
tdesign_flutter | ^0.2.7 | |
flutter_easyloading | ^3.0.5 |
Riverpod 2.x vs 3.x 的坑:网上大量教程是 3.x 写法。本项目是 2.6.1, 用 3.x 语法(如新的
Notifier初始化方式、Ref泛型参数)会直接编译失败。 本系列08-状态管理Riverpod严格按 2.6.1 撰写。
七、现有 docs/ 文档地图(含过时标注)
项目 docs/ 下已有 15 篇约 6600 行文档。本系列不修改它们,但你迟早会翻到,所以这里给一张地图。
值得读的
| 文件 | 行数 | 什么时候读 |
|---|---|---|
docs/coding-standards.md | 172 | 入职第一天就读,编码规范 |
docs/architecture.md | 375 | 读本系列 07 时对照看 |
docs/api-integration-flow.md | 474 | 要接接口时读,与本系列 09 互补 |
docs/freezed-model-guide.md | 369 | 写模型时读 |
docs/theme-guide.md | 219 | 用主题 token 时读 |
docs/figma-component-mapping.md | 122 | 对着设计稿还原 UI 时读 |
docs/project-overview.md | 43 | 想知道项目边界时读 |
⚠️ 已过时,以本系列为准
docs/README.md(1100 行)是一份「前端转 Flutter 入门指南」,但有三处内容已与代码不符:
| # | 文档写的 | 代码实际 | 影响 |
|---|---|---|---|
| 1 | package:aiwork/... | package:haierenergy/... | 照抄 import 直接编译失败 |
| 2 | AppLocalizations.of(context) | RuntimeI18n.ofNonNull(context) | 该项目走运行时词条,不走 gen-l10n |
| 3 | 学习路径指向 lib/ui/audit/completion/ | 该目录已不存在(现为 device/ paramSet/ plant_detail/) | 按图索骥会找不到文件 |
结论:docs/README.md 的思路和概念对照表仍有价值,但代码路径、包名、API 一律以本系列为准。
内容重复的(不必都读)
多语言相关有 4 篇,内容大量重叠:
docs/language.md(1127 行)docs/l10n-flow.md(821 行)docs/i18n-analysis.md(528 行)docs/l10n-architecture.md(475 行)
读本系列 14-国际化 即可,它以代码为准重新梳理了唯一正确的链路。
用途不明 / 迁移临时文档
docs/1.md(576 行)、docs/language copy.md(306 行,副本)、docs/device-page-migration-gap-analysis.md(370 行,迁移期缺口分析)、docs/ui_dto_import_audit.md(47 行)、docs/uniapp-migration-workflow.md(13 行)。
根目录另有冗余副本:1.md、AGENTS copy.md、CLAUDE.md。这些属于历史遗留,初学阶段忽略。
八、你已经会前端,所以这些概念可以直接迁移
| 前端 | 本项目对应 | 详见 |
|---|---|---|
| React / Vue 组件 | ConsumerWidget / ConsumerStatefulWidget | 02 |
useState | ref.watch(provider) | 08 |
useEffect(() => fetch(), []) | @riverpod class 的 build() | 08 |
useEffect 返回的清理函数 | ref.onDispose() | 08 |
dispatch(action) | ref.read(xProvider.notifier).method() | 08 |
refetch | ref.invalidateSelf() | 08 |
TypeScript interface | @freezed class | 01 |
Either<Error, T> | Result<T>(Ok / ErrorResult) | 09 |
| Axios 拦截器 | Dio 拦截器链 | 09 |
| Vue Router / React Router | GoRouter | 13 |
| CSS 变量 / 主题 token | AppTokens | 11 |
i18n.t('key') | RuntimeI18n.ofNonNull(context).t('key') | 14 |
React key | Flutter Key(ValueKey / GlobalKey) | 03 |
| DOM 事件冒泡 | 没有冒泡,手势竞技场 GestureArena | 05 |
| Web Worker | Isolate | 06 |
@media 响应式 | LayoutBuilder / MediaQuery + ScreenUtil | 04 |
CSS keyframes | AnimationController + Tween | 05 |
九、常见疑问
Q:我要不要先把 Flutter 官方教程过一遍? 不必。本系列 01~06 章覆盖了官方教程的核心内容,且全部用前端类比讲解,效率更高。官方教程可作为查漏补缺。
Q:代码看不懂是正常的吗? 前两周非常正常。这个项目有 449 个文件,第 1 周的目标是「能读懂」,不是「能全懂」。按路线图推进,第 3 周会突然通畅。
Q:可以直接跳到 10-页面开发实战 照着抄吗? 可以,但你会抄得不明所以,遇到报错无法排查。建议至少先过 01、02、08。
Q:练习一定要做吗?03~06 的练习强烈建议做——这些知识项目里没有现成参照物,只看不练等于没学。07~17 的练习可以用「读现有代码 + 跑现有测试」代替。