Files
2026-09-04 15:22:20 +08:00

713 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](file:///d:/flutter_learn/new_learn/android/local.properties))
### 1.2 创建项目
```bash
flutter create --org com.example --platforms=android,ios,web,windows new_learn
```
### 1.3 项目核心依赖
完整依赖见 [pubspec.yaml](file:///d:/flutter_learn/new_learn/pubspec.yaml),关键项:
```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)
```dart
String name = 'Flutter'; // 不可为 null
String? nickname; // 可为 null,使用前必须判空
// 安全调用
int len = nickname?.length ?? 0; // ?. + ?? 兜底
```
> **核心规则**:所有变量默认非空,`?` 才允许 null。编译期就帮你拦住空指针。
### 2.3 异步编程
`fromJson` 是构造函数,把字典转换成我们定义的 User 实体类对象,获得类型安全、点访问、编译检查。
```dart
Future<User> fetchUser() async {
final response = await dio.get('/user');
return User.fromJson(response.data);
}
```
### 2.4 模式匹配(Dart 3 特性)
```dart
// switch 表达式
String describe(Object obj) => switch (obj) {
int() => '整数',
String() => '字符串',
List<int>() => '整数列表',
_ => '未知',
};
```
### 2.5 类与混入
```dart
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`、`Icon`
- `StatefulWidget`:有内部状态,`setState` 触发重建 → 如 `Checkbox`、`TextField`
### 3.2 核心布局原理
> **Flutter 布局第一法则**:父组件向子组件传递**约束(Constraints)**,子组件根据约束**决定自身大小(Size)**,然后向上返回。
```
父 Widget ──Constraints──> 子 Widget
↓ 决定自身大小
父 Widget <──Size────────── 子 Widget
```
- **主轴**(MainAxis):Row 的水平方向 / Column 的垂直方向
- **交叉轴**(CrossAxis):与主轴垂直的方向
### 3.3 启动流程链路
入口文件 [main.dart](file:///d:/flutter_learn/new_learn/lib/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](file:///d:/flutter_learn/new_learn/lib/core/app.dart):
```
TabApp (StatelessWidget)
└─ ScreenUtilInit // 屏幕适配
└─ MaterialApp.router // Material3 + 品牌种子色
├─ builder: PrivacyStartupGate // 启动层隐私拦截
└─ routerConfig: appRouter // 全局路由表
```
### 3.4 路由体系
路由表 [app_router.dart](file:///d:/flutter_learn/new_learn/lib/core/router/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](file:///d:/flutter_learn/new_learn/lib/core/providers/privacy_provider.dart):
```dart
final ValueNotifier<bool> privacyAcceptedNotifier = ValueNotifier<bool>(false);
```
> **为什么不用 Riverpod?** go_router 的 `refreshListenable` 需要 `Listenable`,ValueNotifier 足够轻量,且 `main()` 中 `runApp` 前就能直接恢复状态。
#### 方案 B:Riverpod AsyncNotifier — 全局用户状态
用户状态 [user_provider.dart](file:///d:/flutter_learn/new_learn/lib/core/providers/user_provider.dart):
```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<int>)是登录态变化的信号源,Riverpod 写状态、ValueNotifier 通知路由,两者协作实现「响应式登录守卫」。
---
## 五、Dio 网络请求生产级封装(~4 min)
### 5.1 封装架构
核心文件 [dio_client.dart](file:///d:/flutter_learn/new_learn/lib/core/network/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 统一异常翻译
```dart
// 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](file:///d:/flutter_learn/new_learn/lib/pages/demo/dio_pro_demo.dart) |
| 取消请求 | `CancelToken` + dispose 自动取消 | 同上 |
| 失败重试 | 指数退避 `_canRetry`(仅网络层错误)| 同上 |
| Token 自动注入 | 拦截器 onRequest | dio_client.dart |
| 401 自动登出 | 拦截器 onError | dio_client.dart |
| OSS 直传 | 已有 authorization 头时跳过注入 | [oss_uploader.dart](file:///d:/flutter_learn/new_learn/lib/core/network/oss_uploader.dart) |
### 5.4 数据模型 fromJson
```dart
// 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 层永远只 catch `ApiException` 一个类型。
---
## 六、屏幕适配:ScreenUtil 方案(~3 min)
### 6.1 核心文件
适配模板 [screen_adapt_template.dart](file:///d:/flutter_learn/new_learn/lib/core/utils/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 使用规则
```dart
// 字号:用 .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](file:///d:/flutter_learn/new_learn/lib/core/theme/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](file:///d:/flutter_learn/new_learn/lib/core/theme/brands/sky_blue_theme.dart) — seedColor `0xFF4FC3F7`
- [red_theme.dart](file:///d:/flutter_learn/new_learn/lib/core/theme/brands/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](file:///d:/flutter_learn/new_learn/lib/core/config/app_env.dart):
```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 运行命令
```bash
# 开发环境 + 爱学蝶变品牌
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](file:///d:/flutter_learn/new_learn/lib/core/config/app_config.dart) — 全项目唯一取配置入口:
```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](file:///d:/flutter_learn/new_learn/lib/core/storage/user_storage.dart) |
| `shared_preferences` | 隐私协议同意状态 | [privacy_storage.dart](file:///d:/flutter_learn/new_learn/lib/core/storage/privacy_storage.dart) |
| `isar_community` | 题库/错题/播放记录 | [app_database.dart](file:///d:/flutter_learn/new_learn/lib/core/storage/database/app_database.dart) |
### 9.2 Isar 数据模型
```dart
@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](file:///d:/flutter_learn/new_learn/lib/pages/demo/isar_watch_demo.dart):
```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](file:///d:/flutter_learn/new_learn/lib/pages/demo/text_demo.dart) |
| Image | 四种来源 + BoxFit + 错误兜底 | [image_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/image_demo.dart) |
| Input | controller + dispose + 密码显隐 | [input_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/input_demo.dart) |
| Align | 对齐 + Riverpod 全局信息 | [align_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/align_demo.dart) |
| Stack | 层叠 + 全 Positioned 尺寸坑 | [stack_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/stack_demo.dart) |
| Wrap | 自动换行 + Chip + 自绘 CustomTag | [wrap_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/wrap_demo.dart) |
#### 第二阶段:布局进阶
| Demo | 核心知识点 | 代码 |
|------|-----------|------|
| List | 懒加载/下拉刷新/触底加载 | [list_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/list_demo.dart) |
| Grid | count/builder/extent 三种构造 | [grid_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/grid_demo.dart) |
| Sliver | CustomScrollView + 折叠 AppBar | [sliver_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/sliver_demo.dart) |
| Responsive | 手机单列 vs Pad 多列 | [responsive_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/responsive_demo.dart) |
#### 第三阶段:路由与表单
| Demo | 核心知识点 | 代码 |
|------|-----------|------|
| Router | 命名路由表 + 三种跳转姿势 | [router_home_page.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/router_home_page.dart) |
| Detail | 构造传参 vs 命名路由 arguments | [detail_page.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/detail_page.dart) |
| Form | 7 个校验知识点/两段式提交 | [form_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/form_demo.dart) |
| Picker | pop 带值返回 | [picker_page.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/picker_page.dart) |
#### 第四阶段:网络与数据
| Demo | 核心知识点 | 代码 |
|------|-----------|------|
| Dio | GET/POST/错误兜底三态 UI | [dio_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/dio_demo.dart) |
| Dio Pro | 下载进度/CancelToken/重试 | [dio_pro_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/dio_pro_demo.dart) |
| Isar Watch | 错题本自动刷新 | [isar_watch_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/isar_watch_demo.dart) |
| Wrong Flow | 接口+Isar+watch 完整链路 | [wrong_flow_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/wrong_flow_demo.dart) |
| OSS Upload | 客户端签名+PUT直传 | [oss_upload_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/oss_upload_demo.dart) |
#### 第五阶段:主题与媒体
| Demo | 核心知识点 | 代码 |
|------|-----------|------|
| Theme | 两级主题模型 + 品牌换肤 | [theme_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/theme_demo.dart) |
| Seed Palette | colorSchemeSeed 自动生成配色 | [seed_palette_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/seed_palette_demo.dart) |
| Animation | 隐式/显式/Hero/CustomPaint | [animation_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/animation_demo.dart) |
| Rich Text | HTML 题目渲染 + audio 替换 | [rich_text_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/rich_text_demo.dart) |
| Audio | just_audio 控制条 | [audio_player_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/audio_player_demo.dart) |
| Share | 文本/图片/截图海报/存相册 | [share_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/share_demo.dart) |
| Smooth Sheet | 常驻多停靠底部面板 | [smooth_sheet_demo.dart](file:///d:/flutter_learn/new_learn/lib/pages/demo/smooth_sheet_demo.dart) |
### 10.2 核心组件封装思路(9个可复用组件)
| 组件 | 封装思路 | 代码 |
|------|----------|------|
| **PrivacyStartupGate** | 启动层隐私 Gate — 冷启动不闪登录页,视觉第一拦 | [privacy_startup_gate.dart](file:///d:/flutter_learn/new_learn/lib/core/components/privacy_startup_gate.dart) |
| **CommonPagedList** | 通用分页列表 — 基于 infinite_scroll_pagination,对标 z-paging | [common_paged_list.dart](file:///d:/flutter_learn/new_learn/lib/core/components/common_paged_list.dart) |
| **AudioPlayerCard** | 通用音频控制条 — 播放/进度/倍速/循环,切后台自动暂停 | [audio_player_card.dart](file:///d:/flutter_learn/new_learn/lib/core/components/audio_player_card.dart) |
| **VideoPlayerView** | 通用视频播放器内嵌版 — 无 Scaffold,可嵌入任意位置 | [video_player_view.dart](file:///d:/flutter_learn/new_learn/lib/core/components/video_player_view.dart) |
| **ImagePreviewViewer** | 通用图片预览 — photo_view 双指缩放 + Hero 动画 | [image_preview_viewer.dart](file:///d:/flutter_learn/new_learn/lib/core/components/image_preview_viewer.dart) |
| **RichTextContent** | 富文本渲染 — HTML + audio/video 自动替换为播放器 | [rich_text_content.dart](file:///d:/flutter_learn/new_learn/lib/core/components/rich_text_content.dart) |
| **PersistentSnapSheet** | 常驻底部面板 — smooth_sheets 封装 | [persistent_snap_sheet.dart](file:///d:/flutter_learn/new_learn/lib/core/components/persistent_snap_sheet.dart) |
| **AppUpdateDialog** | 版本更新弹窗 — 强制/可选 + 平台分流跳转 | [app_update_dialog.dart](file:///d:/flutter_learn/new_learn/lib/core/components/app_update_dialog.dart) |
### 10.3 服务层封装思路(3个核心服务)
| 服务 | 封装思路 | 代码 |
|------|----------|------|
| **PickerService** | 图片选择统一服务 — Web用image_picker / 原生用微信选择器 + 权限守卫 | [picker_service.dart](file:///d:/flutter_learn/new_learn/lib/core/services/picker_service.dart) |
| **ImageProcessor** | 图片压缩 — 原生走flutter_image_compress / Web走纯Dart image包(Isolate隔离)| [image_processor.dart](file:///d:/flutter_learn/new_learn/lib/core/services/image_processor.dart) |
| **ShareService** | 分享服务 — 截图Widget渲染树→PNG→分享/存相册 | [share_service.dart](file:///d:/flutter_learn/new_learn/lib/core/services/share_service.dart) |
### 10.4 封装设计原则
1. **平台分流**:同一接口,io/web 双实现,条件 import自动选择
2. **资源释放**:所有 `TextEditingController` / `StreamSubscription` / `CancelToken` / `AnimationController` 在 `dispose` 中销毁
3. **防御性解析**:`fromJson` 兜底后端类型不稳定(`_toInt` / `_toDouble`)
4. **单一职责**:组件只管渲染,数据逻辑下沉到 Provider/Service
5. **配置驱动**:环境/品牌/主题互不依赖,全项目统一走 `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 本项目的技术亮点
1. **一套代码出 7 个品牌 App** — 编译期 `--dart-define` 注入品牌+环境
2. **响应式登录守卫** — Riverpod 写状态 + ValueNotifier 通知路由
3. **双方案屏幕适配** — 手机用 375 设计稿 + 平板回退原始尺寸 + 字体封顶
4. **两级主题模型** — 种子色自动生成色板 + 页面细节配置
5. **生产级 Dio 封装** — 拦截器/统一异常/重试/取消/进度
6. **OSS 客户端直传** — V4 签名 + PUT 直传 + 进度
7. **Isar 响应式数据库** — watch 驱动 UI 自动刷新
8. **28 个 Demo 全覆盖** — 从 Text 到 OSS,每个都有配套文档
### 11.4 配套文档
- Demo 文档索引:[docs/demos/README.md](file:///d:/flutter_learn/new_learn/docs/demos/README.md)
- 试卷架构设计:[docs/exam_architecture.md](file:///d:/flutter_learn/new_learn/docs/exam_architecture.md)
- 每个 Demo 都有六章结构文档:问题背景 / 运行方式 / 核心知识点 / 关键代码剖析 / 踩坑记录 / 扩展思考
---
> **结语**:Flutter 的学习不是背 API,而是理解「Widget 是配置、Element 是实例化、RenderObject 是渲染」这条主线。所有的高级用法 — 状态管理、路由守卫、主题换肤 — 都是建立在这条主线上的。本项目 28 个 Demo 覆盖了从基础组件到生产级封装的完整路径,建议大家按顺序跑一遍,每个 Demo 都有配套文档。