29 KiB
Flutter 学习全旅程讲解 — 从零到生产级项目
预计时长:40 分钟 | 项目:
new_learn| Flutter 3.44.x + Dart 3.12
目录
| 章节 | 主题 | 预估时长 |
|---|---|---|
| 一 | 环境安装与项目初始化 | ~4 min |
| 二 | Dart 3 语法核心要点 | ~4 min |
| 三 | Flutter 3 核心框架与分层架构 | ~5 min |
| 四 | 全局状态管理:Riverpod 方案 | ~4 min |
| 五 | Dio 网络请求生产级封装 | ~4 min |
| 六 | 屏幕适配:ScreenUtil 方案 | ~3 min |
| 七 | 多品牌主题配置体系 | ~3 min |
| 八 | 多环境变量配置体系 | ~3 min |
| 九 | 本地存储:Isar + 响应式 watch | ~3 min |
| 十 | Demo 组件封装思路汇总 | ~5 min |
| 十一 | 学习路径与心得总结 | ~2 min |
一、环境安装与项目初始化(~4 min)
1.1 工具链一览
| 工具 | 版本 | 用途 |
|---|---|---|
| Flutter SDK | 3.44.x | 编译、热重载、多端构建 |
| Dart SDK | 3.12 | 语言运行时 |
| Android Studio / VS Code | 最新 | IDE + 插件 |
| Android SDK | API 34+ | 安卓构建 |
| Xcode | 15+(Mac) | iOS 构建 |
本项目路径:
D:\dev\flutter_3.44.8\flutter(见 local.properties)
1.2 创建项目
flutter create --org com.example --platforms=android,ios,web,windows new_learn
1.3 项目核心依赖
完整依赖见 pubspec.yaml,关键项:
environment:
sdk: ^3.12.2
dependencies:
flutter: { sdk: flutter }
dio: ^5.10.0 # 网络请求
go_router: ^17.4.0 # 声明式路由
flutter_riverpod: ^3.3.2 # 状态管理
flutter_screenutil: ^5.9.0 # 屏幕适配
cached_network_image: ^3.4.1 # 网络图片缓存(ImageDemo)
infinite_scroll_pagination: ^5.1.1 # 分页列表
flutter_widget_from_html_core: ^0.17.2 # 富文本 HTML 渲染
shared_preferences: ^2.5.3 # 轻量本地存储
flutter_secure_storage: ^9.2.4 # 敏感凭据安全存储
isar_community: ^3.3.2 # 本地数据库
isar_community_flutter_libs: ^3.3.2
path_provider: ^2.1.5
wechat_assets_picker: ^9.8.0 # 相册多选/预览(原生端)
wechat_camera_picker: ^4.6.0 # 微信风格相机(原生端)
image_picker: ^1.2.3 # Web 兜底图片选择
permission_handler: ^11.3.1 # 权限管理
image: ^4.8.0 # 纯 Dart 图片处理(含 Web)
flutter_image_compress: ^2.5.1 # 原生压缩(Android/iOS)
photo_view: ^0.15.0 # 图片预览缩放
video_player: ^2.13.0 # 视频播放
chewie: ^1.13.1 # 视频 UI 封装
just_audio: ^0.10.6 # 音频播放
share_plus: ^10.1.4 # 系统分享
gal: ^2.3.3 # 保存到相册
package_info_plus: ^8.3.0 # 版本信息
url_launcher: ^6.3.1 # 外部跳转
smooth_sheets: ^1.1.1 # 常驻底部面板
crypto: ^3.0.6 # MD5 等加密
# 完整依赖与注释说明以 pubspec.yaml 为准(此处为学习旅程的“关键依赖速览”)
1.4 目录结构
lib/
├── main.dart # 启动入口(5步初始化)
├── core/ # 核心基础设施层
│ ├── app.dart # 根组件 TabApp
│ ├── config/ # 环境+品牌配置
│ ├── network/ # Dio 封装 + API
│ ├── providers/ # Riverpod 全局状态
│ ├── router/ # go_router 路由表
│ ├── theme/ # 多品牌主题
│ ├── storage/ # Isar + 安全存储
│ ├── services/ # 图片/选择/分享服务
│ └── components/ # 9个可复用组件
├── features/ # 业务功能层
│ ├── auth/ # 登录+隐私协议
│ ├── exam/ # 试卷练习
│ ├── my/ # 个人中心
│ └── ...
└── pages/demo/ # 28个独立学习Demo
讲解要点:这套
core / features / pages三层分离,对标前端utils / views / components,让基础设施和业务逻辑彻底解耦。
二、Dart 3 语法核心要点(~4 min)
2.1 与 JavaScript/TypeScript 的关键差异
| 概念 | JS/TS | Dart 3 |
|---|---|---|
| 类型声明 | let x: number |
int x / var x(类型推断) |
| 空安全 | 可选 ? |
健全空安全 String?,编译期拦截 |
| 异步 | Promise | Future + async/await |
| 集合 | Array/Map | List<T> / Map<K,V> / Set<T> |
| 类 | class | class + mixin 混入 |
| 模式匹配 | 无 | switch 表达式 + 解构(Dart 3 新特性) |
2.2 空安全(Sound Null Safety)
String name = 'Flutter'; // 不可为 null
String? nickname; // 可为 null,使用前必须判空
// 安全调用
int len = nickname?.length ?? 0; // ?. + ?? 兜底
核心规则:所有变量默认非空,
?才允许 null。编译期就帮你拦住空指针。
2.3 异步编程
fromJson 是构造函数,把字典转换成我们定义的 User 实体类对象,获得类型安全、点访问、编译检查。
Future<User> fetchUser() async {
final response = await dio.get('/user');
return User.fromJson(response.data);
}
2.4 模式匹配(Dart 3 特性)
// switch 表达式
String describe(Object obj) => switch (obj) {
int() => '整数',
String() => '字符串',
List<int>() => '整数列表',
_ => '未知',
};
2.5 类与混入
mixin Disposable {
void dispose();
}
class MyController with Disposable {
@override
void dispose() { /* 释放资源 */ }
}
讲解要点:Dart 3 的空安全是「健全」的,编译期保证非空变量不会拿到 null,比 TS 的可选链更彻底。模式匹配让分支逻辑更简洁。
三、Flutter 3 核心框架与分层架构(~5 min)
3.1 Widget 是什么?
核心认知:Widget 是「配置对象」不是「渲染 View」。
Widget(配置蓝图)→ Element(实例化树节点)→ RenderObject(真正测量布局绘制)
StatelessWidget:纯展示,props 不变 → 如Text、IconStatefulWidget:有内部状态,setState触发重建 → 如Checkbox、TextField
3.2 核心布局原理
Flutter 布局第一法则:父组件向子组件传递约束(Constraints),子组件根据约束决定自身大小(Size),然后向上返回。
父 Widget ──Constraints──> 子 Widget
↓ 决定自身大小
父 Widget <──Size────────── 子 Widget
- 主轴(MainAxis):Row 的水平方向 / Column 的垂直方向
- 交叉轴(CrossAxis):与主轴垂直的方向
3.3 启动流程链路
入口文件 main.dart:
main() 5步串行初始化
↓
1. WidgetsFlutterBinding.ensureInitialized() // 插件通道就绪
2. GoRouter.optionURLReflectsImperativeAPIs // 修复 web 地址栏同步
3. DioClient.instance.restoreToken() // 回填登录 Token
4. privacyAcceptedNotifier // 恢复隐私协议状态
5. AppDatabase.instance.init() // 初始化 Isar
↓
runApp(ProviderScope(child: TabApp())) // Riverpod 容器 + 根组件
根组件 app.dart:
TabApp (StatelessWidget)
└─ ScreenUtilInit // 屏幕适配
└─ MaterialApp.router // Material3 + 品牌种子色
├─ builder: PrivacyStartupGate // 启动层隐私拦截
└─ routerConfig: appRouter // 全局路由表
3.4 路由体系
路由表 app_router.dart:
GoRouter
├─ redirect: _guardLogin // 登录守卫(对标 vue-router beforeEach)
│ ├─ 未同意隐私 → /privacy-consent
│ ├─ 已同意+在隐私页 → /login 或 /
│ ├─ 未登录 → /login
│ └─ 已登录+在登录页 → /
├─ refreshListenable: authTick + privacyNotifier // 响应式守卫
├─ ShellRoute (4 tab 壳)
│ ├─ / → TaskPage
│ ├─ /recite → RecitePage
│ ├─ /class → ClassPage
│ └─ /my → MyPage
├─ /privacy-consent
├─ /login
└─ /myPoint
讲解要点:go_router 的
refreshListenable是核心 — 登录态变化时自动refresh()重算 redirect,不需要手动跳转,对标 Vue Router 的响应式beforeEach。
四、全局状态管理:Riverpod 方案(~4 min)
4.1 为什么选 Riverpod?
| 维度 | Provider(旧) | Riverpod 3.x |
|---|---|---|
| 依赖注入 | context.read() 耦合 BuildContext |
全局容器,不依赖 context |
| 测试 | 难 mock | ProviderContainer 轻量覆盖 |
| 安全 | 运行时报错 | 编译期检查(Provider 不可缺) |
| 异步 | 手写 FutureBuilder | AsyncNotifier 内建 loading/error/data |
4.2 本项目的两种状态方案
方案 A:ValueNotifier — 轻量全局信号
隐私协议状态 privacy_provider.dart:
final ValueNotifier<bool> privacyAcceptedNotifier = ValueNotifier<bool>(false);
为什么不用 Riverpod? go_router 的
refreshListenable需要Listenable,ValueNotifier 足够轻量,且main()中runApp前就能直接恢复状态。
方案 B:Riverpod AsyncNotifier — 全局用户状态
用户状态 user_provider.dart:
class CurrentUserNotifier extends AsyncNotifier<CurrentUserExtension?> {
@override
Future<CurrentUserExtension?> build() async {
// 冷启动异步从本地缓存回填
return await UserStorage.instance.readUser();
}
void set(user) {
state = AsyncData(user); // 有值
authTick.value++; // 触发路由守卫重算
}
}
// 全局 Provider
final currentUserProvider =
AsyncNotifierProvider<CurrentUserNotifier, CurrentUserExtension?>(...);
状态四态生命周期:
AsyncLoading() → 正在读缓存
AsyncData(user) → 有值(已登录)
AsyncData(null) → 明确未登录
AsyncError(e) → 异常
讲解要点:
authTick(ValueNotifier)是登录态变化的信号源,Riverpod 写状态、ValueNotifier 通知路由,两者协作实现「响应式登录守卫」。
五、Dio 网络请求生产级封装(~4 min)
5.1 封装架构
核心文件 dio_client.dart:
DioClient (单例)
├─ BaseOptions
│ ├─ baseUrl → 来自 AppConfig(品牌×环境组合)
│ └─ 超时:连接 15s / 接收 15s / 发送 15s
├─ 拦截器三件套
│ ├─ onRequest → 自动加 Authorization: Bearer token
│ ├─ onResponse → 日志打印
│ └─ onError → 401清Token + DioException→ApiException
├─ 泛型请求方法
│ ├─ get<T>(path, {cancelToken, queryParameters})
│ ├─ post<T>(path, {data, cancelToken, onSendProgress})
│ └─ put<T>(path, {data})
└─ restoreToken() → 冷启动回填 Token
5.2 统一异常翻译
// UI 层只需要 catch ApiException,不用管 DioException
ApiException _mapError(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return ApiException('网络连接超时,请检查网络');
case DioExceptionType.connectionError:
return ApiException('网络连接失败,请检查网络');
// ... badResponse / cancel / unknown
}
}
5.3 生产级能力(Demo 演示)
| 能力 | 实现方式 | Demo |
|---|---|---|
| 下载进度 | onReceiveProgress 回调 |
dio_pro_demo.dart |
| 取消请求 | CancelToken + dispose 自动取消 |
同上 |
| 失败重试 | 指数退避 _canRetry(仅网络层错误) |
同上 |
| Token 自动注入 | 拦截器 onRequest | dio_client.dart |
| 401 自动登出 | 拦截器 onError | dio_client.dart |
| OSS 直传 | 已有 authorization 头时跳过注入 | oss_uploader.dart |
5.4 数据模型 fromJson
// user_model.dart — 防御性解析,兼容后端返回字符串数字
factory CurrentUserExtension.fromJson(Map<String, dynamic> json) {
return CurrentUserExtension(
userId: json['userId'] as int,
userNo: json['userNo']?.toString() ?? '',
level: _toInt(json['level']), // 兼容 "1" 或 1
balance: _toDouble(json['balance']),
// ... 17 个字段
);
}
讲解要点:
fromJson用_toInt/_toDouble兜底,是真实项目中最常见的后端类型不稳定问题。UI 层永远只 catchApiException一个类型。
六、屏幕适配:ScreenUtil 方案(~3 min)
6.1 核心文件
适配模板 screen_adapt_template.dart:
AppAdapt 工具类
├─ tabletBreakpoint = 600 // Material 平板断点
├─ phoneDesign = Size(375, 812) // iPhone 逻辑尺寸
├─ designSizeOf(context) // 方案B:平板用设备自身尺寸
└─ fontSizeResolver // 方案A:字体放大封顶 1.2 倍
6.2 双方案策略
| 方案 | 目标 | 实现 |
|---|---|---|
| 方案 A | 大屏字体不过分放大 | fontSizeResolver 封顶 maxFontScale=1.2 |
| 方案 B | 平板布局不被拉伸 | 宽高都 ≥600dp 时用设备自身尺寸做 designSize,缩放回到 ~1 |
6.3 使用规则
// 字号:用 .spMin(大屏只缩不放)
Text('你好', style: TextStyle(fontSize: 16.spMin));
// 宽度/横向间距:用 .w
SizedBox(width: 16.w);
// 高度/纵向:用 .h
SizedBox(height: 48.h);
// 圆角:用 .r
BorderRadius.circular(8.r);
讲解要点:字号必须用
.spMin而非.sp—.sp在大屏会把字体放得过大,.spMin确保只缩小不放大。
七、多品牌主题配置体系(~3 min)
7.1 三层解耦设计
环境(接口在哪) → app_env.dart 编译期 --dart-define 定死
品牌(谁家 App) → app_brand.dart 发布期定死,7个品牌
主题(长什么样) → brand_theme.dart 运行时可切换
配置聚合 → app_config.dart 全项目唯一取配置入口
7.2 两级主题模型
模型文件 brand_theme.dart:
BrandTheme(一级品牌)
├─ name: 'SkyBlue'
├─ seedColor: Color(0xFF4FC3F7) // 种子色 → Material3 自动生成整套色板
└─ pages: Map<PageLevel, PageTheme>
├─ home: PageTheme { bg, headerBg, accent, title, banner }
├─ detail: PageTheme { ... }
└─ result: PageTheme { ... }
品牌主题文件:
- sky_blue_theme.dart — seedColor
0xFF4FC3F7 - red_theme.dart — seedColor
0xFFEF4444
7.3 新增主题三步走
1. 在 brands/ 下新建 xxx_theme.dart → 定义 seedColor + 三层级配置
2. 在 brand_theme.dart 中 import + 添加到 brandThemes 列表
3. 在 app_brand.dart 中给品牌指定 themeId
讲解要点:「模型文件不写颜色、品牌文件不写逻辑」— 新增主题零改页面代码。
colorSchemeSeed让 Material 3 自动从种子色生成完整配色。
八、多环境变量配置体系(~3 min)
8.1 编译期注入
环境配置 app_env.dart:
// 编译期注入,默认 prod
static const String env = String.fromEnvironment('ENV', defaultValue: 'prod');
static const String brand = String.fromEnvironment('BRAND', defaultValue: 'aixue');
8.2 三套环境地址
| 环境 | baseUrl | 用途 |
|---|---|---|
| dev | k12-student-api-test.23544.com:16400 |
本地开发联调 |
| test | axdb-test.cnzhyx.cn |
测试环境 |
| prod | ylax-api.cqbzyx.com(被品牌专属地址覆盖) |
生产 |
8.3 运行命令
# 开发环境 + 爱学蝶变品牌
flutter run --dart-define=ENV=dev --dart-define=BRAND=aixue
# 生产环境 + 楚才品牌
flutter run --dart-define=ENV=prod --dart-define=BRAND=chucai
# 构建 APK
flutter build apk --dart-define=ENV=prod --dart-define=BRAND=chucai
8.4 配置聚合层
app_config.dart — 全项目唯一取配置入口:
// baseUrl 规则:prod 用品牌专属地址覆盖,否则环境通用地址
static String get baseUrl =>
AppEnv.isProd ? (currentBrand.baseUrl ?? AppEnv.baseUrl) : AppEnv.baseUrl;
// 主题:按品牌查主题注册表,未知兜底第一个
static BrandTheme get theme =>
brandThemes.firstWhere((t) => t.name == currentBrand.themeId,
orElse: () => brandThemes.first);
讲解要点:
String.fromEnvironment是编译期常量,不占运行时开销。品牌 × 环境组合让一套代码出 7 个品牌的 App,只需改运行命令。
九、本地存储:Isar + 响应式 watch(~3 min)
9.1 存储组合
| 存储方式 | 用途 | 文件 |
|---|---|---|
flutter_secure_storage |
Token 安全存储 | user_storage.dart |
shared_preferences |
隐私协议同意状态 | privacy_storage.dart |
isar_community |
题库/错题/播放记录 | app_database.dart |
9.2 Isar 数据模型
@collection
class WrongQuestion {
Id id = Isar.autoIncrement;
late int questionId;
late int userId;
late String questionTitle;
late DateTime createdAt;
}
通过
build_runner生成.g.dart,提供类型安全的 CRUD。
9.3 平台分流实现
app_database.dart → 抽象接口
├─ app_database_io.dart → 原生平台 Isar 实现
└─ app_database_web.dart → Web 调试自动跳过(Isar 不支持 Web)
9.4 watch 响应式 — 错题自动刷新
Demo 演示 isar_watch_demo.dart:
// Isar watch:数据变化自动通知 UI 刷新
Stream<List<WrongQuestion>> watchWrongQuestions() {
return database.wrongQuestions
.where()
.userIdEqualTo(userId)
.watch(fireImmediately: true);
}
讲解要点:Isar 的
watch()是真正的响应式 — 数据库写入后自动通知 Stream 监听者,UI 无需手动刷新。对标前端的「数据驱动视图」,但是是本地数据库级别的。
十、Demo 组件封装思路汇总(~5 min)
10.1 28 个 Demo 的分类学习路径
第一阶段:基础组件
| Demo | 核心知识点 | 代码 |
|---|---|---|
| Text | 样式/截断/富文本/手势 | text_demo.dart |
| Image | 四种来源 + BoxFit + 错误兜底 | image_demo.dart |
| Input | controller + dispose + 密码显隐 | input_demo.dart |
| Align | 对齐 + Riverpod 全局信息 | align_demo.dart |
| Stack | 层叠 + 全 Positioned 尺寸坑 | stack_demo.dart |
| Wrap | 自动换行 + Chip + 自绘 CustomTag | wrap_demo.dart |
第二阶段:布局进阶
| Demo | 核心知识点 | 代码 |
|---|---|---|
| List | 懒加载/下拉刷新/触底加载 | list_demo.dart |
| Grid | count/builder/extent 三种构造 | grid_demo.dart |
| Sliver | CustomScrollView + 折叠 AppBar | sliver_demo.dart |
| Responsive | 手机单列 vs Pad 多列 | responsive_demo.dart |
第三阶段:路由与表单
| Demo | 核心知识点 | 代码 |
|---|---|---|
| Router | 命名路由表 + 三种跳转姿势 | router_home_page.dart |
| Detail | 构造传参 vs 命名路由 arguments | detail_page.dart |
| Form | 7 个校验知识点/两段式提交 | form_demo.dart |
| Picker | pop 带值返回 | picker_page.dart |
第四阶段:网络与数据
| Demo | 核心知识点 | 代码 |
|---|---|---|
| Dio | GET/POST/错误兜底三态 UI | dio_demo.dart |
| Dio Pro | 下载进度/CancelToken/重试 | dio_pro_demo.dart |
| Isar Watch | 错题本自动刷新 | isar_watch_demo.dart |
| Wrong Flow | 接口+Isar+watch 完整链路 | wrong_flow_demo.dart |
| OSS Upload | 客户端签名+PUT直传 | oss_upload_demo.dart |
第五阶段:主题与媒体
| Demo | 核心知识点 | 代码 |
|---|---|---|
| Theme | 两级主题模型 + 品牌换肤 | theme_demo.dart |
| Seed Palette | colorSchemeSeed 自动生成配色 | seed_palette_demo.dart |
| Animation | 隐式/显式/Hero/CustomPaint | animation_demo.dart |
| Rich Text | HTML 题目渲染 + audio 替换 | rich_text_demo.dart |
| Audio | just_audio 控制条 | audio_player_demo.dart |
| Share | 文本/图片/截图海报/存相册 | share_demo.dart |
| Smooth Sheet | 常驻多停靠底部面板 | smooth_sheet_demo.dart |
10.2 核心组件封装思路(9个可复用组件)
| 组件 | 封装思路 | 代码 |
|---|---|---|
| PrivacyStartupGate | 启动层隐私 Gate — 冷启动不闪登录页,视觉第一拦 | privacy_startup_gate.dart |
| CommonPagedList | 通用分页列表 — 基于 infinite_scroll_pagination,对标 z-paging | common_paged_list.dart |
| AudioPlayerCard | 通用音频控制条 — 播放/进度/倍速/循环,切后台自动暂停 | audio_player_card.dart |
| VideoPlayerView | 通用视频播放器内嵌版 — 无 Scaffold,可嵌入任意位置 | video_player_view.dart |
| ImagePreviewViewer | 通用图片预览 — photo_view 双指缩放 + Hero 动画 | image_preview_viewer.dart |
| RichTextContent | 富文本渲染 — HTML + audio/video 自动替换为播放器 | rich_text_content.dart |
| PersistentSnapSheet | 常驻底部面板 — smooth_sheets 封装 | persistent_snap_sheet.dart |
| AppUpdateDialog | 版本更新弹窗 — 强制/可选 + 平台分流跳转 | app_update_dialog.dart |
10.3 服务层封装思路(3个核心服务)
| 服务 | 封装思路 | 代码 |
|---|---|---|
| PickerService | 图片选择统一服务 — Web用image_picker / 原生用微信选择器 + 权限守卫 | picker_service.dart |
| ImageProcessor | 图片压缩 — 原生走flutter_image_compress / Web走纯Dart image包(Isolate隔离) | image_processor.dart |
| ShareService | 分享服务 — 截图Widget渲染树→PNG→分享/存相册 | share_service.dart |
10.4 封装设计原则
- 平台分流:同一接口,io/web 双实现,条件 import自动选择
- 资源释放:所有
TextEditingController/StreamSubscription/CancelToken/AnimationController在dispose中销毁 - 防御性解析:
fromJson兜底后端类型不稳定(_toInt/_toDouble) - 单一职责:组件只管渲染,数据逻辑下沉到 Provider/Service
- 配置驱动:环境/品牌/主题互不依赖,全项目统一走
AppConfig取配置
十一、学习路径与心得总结(~2 min)
11.1 推荐学习顺序
第一阶段:Widget 基础
Text → Image → Input → Align → Stack → Wrap
↓
第二阶段:布局与列表
List → Grid → Sliver → Responsive
↓
第三阶段:路由与表单
Router → Detail → Form → Picker
↓
第四阶段:网络与数据
Dio → Dio Pro → Isar Watch → Wrong Flow → OSS Upload
↓
第五阶段:主题与媒体
Theme → Seed Palette → Animation → Rich Text → Audio → Share → Smooth Sheet
11.2 从前端转 Flutter 的核心思维转变
| 前端思维 | Flutter 思维 |
|---|---|
| DOM 是真实节点 | Widget 是不可变配置,每帧重建 |
| 数据驱动视图(Vue/React) | setState 触发 Element 树 diff + 重建 |
| CSS 盒模型 | 约束传递模型(父给约束,子定大小) |
| 路由守卫 beforeEach | GoRouter redirect + refreshListenable |
| Pinia/Vuex 全局 store | Riverpod Provider + AsyncNotifier |
| axios 拦截器 | Dio 拦截器三件套 |
| rem/vw 适配 | ScreenUtil .w/.h/.spMin |
| 多环境 .env 文件 | --dart-define 编译期注入 |
11.3 本项目的技术亮点
- 一套代码出 7 个品牌 App — 编译期
--dart-define注入品牌+环境 - 响应式登录守卫 — Riverpod 写状态 + ValueNotifier 通知路由
- 双方案屏幕适配 — 手机用 375 设计稿 + 平板回退原始尺寸 + 字体封顶
- 两级主题模型 — 种子色自动生成色板 + 页面细节配置
- 生产级 Dio 封装 — 拦截器/统一异常/重试/取消/进度
- OSS 客户端直传 — V4 签名 + PUT 直传 + 进度
- Isar 响应式数据库 — watch 驱动 UI 自动刷新
- 28 个 Demo 全覆盖 — 从 Text 到 OSS,每个都有配套文档
11.4 配套文档
- Demo 文档索引:docs/demos/README.md
- 试卷架构设计:docs/exam_architecture.md
- 每个 Demo 都有六章结构文档:问题背景 / 运行方式 / 核心知识点 / 关键代码剖析 / 踩坑记录 / 扩展思考
结语:Flutter 的学习不是背 API,而是理解「Widget 是配置、Element 是实例化、RenderObject 是渲染」这条主线。所有的高级用法 — 状态管理、路由守卫、主题换肤 — 都是建立在这条主线上的。本项目 28 个 Demo 覆盖了从基础组件到生产级封装的完整路径,建议大家按顺序跑一遍,每个 Demo 都有配套文档。