Skip to content

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 周:能读懂代码

内容产出
101-Dart语言基础 前半(类型、空安全、final/const)能看懂任意一个 lib/ 下文件的变量声明
201 后半(异步、集合、级联、sealed)能读懂 lib/domain/models/result.dart
302-Flutter核心基础 前半(Widget 树、Stateless/Stateful)能说出 ConsumerWidgetStatelessWidget 的区别
402 后半(约束布局、常见溢出、BuildContext)遇到 RenderFlex overflowed 知道往哪查
5通读 lib/ui/home/views/agent_home_page.dart(先不求甚解)能指出哪几行是「取状态」、哪几行是「发请求」

周末自检:不查文档,能解释 Result<T> 是什么、AsyncValue 有哪三态。

第 2 周:补通用内功(项目里没写,但你不能不会)

项目用 Riverpod + GoRouter 这类重框架,导致下面这些通用知识在代码里几乎找不到踪影——但不学,你第三个页面就会卡住:

内容为什么必须学
103-Flutter渲染原理与生命周期项目大量 ConsumerWidget(无状态),你可能写完三个页面都没写过一次 initState
204-Flutter布局与滚动进阶一旦要做「滚动时折叠的详情页头」,ListView 就不够用了
305-Flutter交互与动画Flutter 没有 DOM 事件冒泡,手势靠「竞技场」抢,这是前端最大的思维差异点
406-Dart异步并发与进阶语法Dart 是单线程事件循环,Isolate 是唯一真并发手段
5回头重读 agent_home_page.dart这次应该能看懂 80% 了

为什么「Dart 异步」排在 Flutter 三篇之后,而不是紧跟 Dart 基础? 因为事件循环、Isolate、Stream 需要「Flutter 单线程渲染模型」这个前置认知才好理解。放在 02 之后立刻讲会非常枯燥且没有上下文;放在 03~05 之后,你已经见过渲染管线,再讲事件循环就有落点了。

第 3 周:摸清项目骨架

内容产出
107-项目架构总览能在目录树里 10 秒内定位到任意功能
208-状态管理Riverpod能自己写一个 @riverpod class
309-数据层链路能新增一个 Repository 方法
411-组件与主题知道 88 个 He 组件里哪个能直接用,不再重复造轮子
512-分页与列表能照 PagedListBody 写一个新的分页列表

周末自检:能独立创建一个带分页列表的新页面(可以先不接接口,用 Mock)。

第 4 周:能独立交付

内容产出
110-页面开发实战端到端走通「路由 → 模型 → ViewModel → 页面 → 接口」
213-路由与导航能注册新路由、传参、处理返回
314-国际化新增一条文案走通全流程
415-权限与设备能力知道业务权限和系统权限是两套东西
516-工程化与调试 + 17-常见陷阱与速查表能本地出包、能自己排查常见报错

毕业标准:从零新增一个完整业务模块(含列表页、详情页、接口对接、文案、权限),通过 bash scripts/precheck.sh


三、章节依赖关系

mermaid
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 天就要动代码):020717,先能跑起来再说
  • 只要能写页面(不做架构决策):0102081011
  • 要接接口010709(可跳过 03~06)

四、练习怎么跑

本系列的练习分两种载体,不要搞混

载体 A:lib/main_playground.dart(章节 03~06)

专为本系列新建的空白练习场,不属于业务代码,练完可删。

bash
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.readNo ProviderScope found
He 组件、AppTokens.of(context)❌ 取不到(null)
RuntimeI18n.ofNonNull(context)❌ assert 失败

这个限制不是缺陷而是设计:03~06 章全是通用知识,本来就不该依赖项目框架。

载体 B:test/ 下的 widget test(章节 07~17)

项目已有完整测试目录,镜像 lib/ 结构。改一改就能跑:

bash
fvm flutter test test/ui/core/widgets/paged_list_body_test.dart

重点可参考的现成用例:

  • test/ui/device/view_models/device_list_view_model_test.dart
  • test/data/repositories/device/device_repository_test.dart
  • test/core/i18n/runtime_i18n_engine_test.dart
  • test/ui/core/widgets/paged_list_body_test.dart

五、项目红线(写代码前必须知道)

这些来自项目 AGENTS.mddocs/coding-standards.md,违反会在 code review 被打回:

#红线正确写法
1所有命令加 fvm 前缀fvm flutter pub get(不是 flutter pub get
2颜色/间距/字体必须走 tokenAppTokens.spacingMd(不是 SizedBox(height: 16)
3图片资源用强类型引用Assets.images.xxx.image()(不是 Image.asset('...')
4业务权限走 hasPermissionref.watch(authViewModelProvider).hasPermission('app:b:addPlant')
5日志用 AppLoggerAppLogger.I.d(...)(不是 print()
6生成文件要提交*.g.dart*.freezed.dartlib/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.3designSize 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.md172入职第一天就读,编码规范
docs/architecture.md375读本系列 07 时对照看
docs/api-integration-flow.md474要接接口时读,与本系列 09 互补
docs/freezed-model-guide.md369写模型时读
docs/theme-guide.md219用主题 token 时读
docs/figma-component-mapping.md122对着设计稿还原 UI 时读
docs/project-overview.md43想知道项目边界时读

⚠️ 已过时,以本系列为准

docs/README.md(1100 行)是一份「前端转 Flutter 入门指南」,但有三处内容已与代码不符

#文档写的代码实际影响
1package:aiwork/...package:haierenergy/...照抄 import 直接编译失败
2AppLocalizations.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.mdAGENTS copy.mdCLAUDE.md。这些属于历史遗留,初学阶段忽略。


八、你已经会前端,所以这些概念可以直接迁移

前端本项目对应详见
React / Vue 组件ConsumerWidget / ConsumerStatefulWidget02
useStateref.watch(provider)08
useEffect(() => fetch(), [])@riverpod classbuild()08
useEffect 返回的清理函数ref.onDispose()08
dispatch(action)ref.read(xProvider.notifier).method()08
refetchref.invalidateSelf()08
TypeScript interface@freezed class01
Either<Error, T>Result<T>(Ok / ErrorResult)09
Axios 拦截器Dio 拦截器链09
Vue Router / React RouterGoRouter13
CSS 变量 / 主题 tokenAppTokens11
i18n.t('key')RuntimeI18n.ofNonNull(context).t('key')14
React keyFlutter Key(ValueKey / GlobalKey)03
DOM 事件冒泡没有冒泡,手势竞技场 GestureArena05
Web WorkerIsolate06
@media 响应式LayoutBuilder / MediaQuery + ScreenUtil04
CSS keyframesAnimationController + Tween05

九、常见疑问

Q:我要不要先把 Flutter 官方教程过一遍? 不必。本系列 01~06 章覆盖了官方教程的核心内容,且全部用前端类比讲解,效率更高。官方教程可作为查漏补缺。

Q:代码看不懂是正常的吗? 前两周非常正常。这个项目有 449 个文件,第 1 周的目标是「能读懂」,不是「能全懂」。按路线图推进,第 3 周会突然通畅。

Q:可以直接跳到 10-页面开发实战 照着抄吗? 可以,但你会抄得不明所以,遇到报错无法排查。建议至少先过 010208

Q:练习一定要做吗?03~06 的练习强烈建议做——这些知识项目里没有现成参照物,只看不练等于没学。07~17 的练习可以用「读现有代码 + 跑现有测试」代替。


下一步

01 · Dart 语言基础