Files
nj/wiki/frontend.md

175 lines
7.9 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.
---
title: Flutter 前端
updated: 2026-06-07
status: active
tags: [flutter, bloc, design-system, responsive]
---
# Flutter 前端
> 从 [[index]] 导航。关联: [[handwriting-engine]] [[data-layer]]
## 1. 设计决策
### Q: 为什么 BLoC 不是 Provider/Riverpod
编辑器strokes + elements + undo/redo + autoSave、同步引擎pending queue + network status、日历date range + mood filter等复杂交互需要 Event/State 显式建模。BLoC 的单向数据流比 Provider 的 notifyListeners() 更可控。
### Q: 为什么 go_router
声明式路由 + 深链接支持 + 路由守卫auth guard 重定向未登录用户)。比 Navigator 2.0 更简洁。
### Q: 设计系统 7 色双模主题?
暖记定位"温暖治愈",浅色模式奶油白(#FFF8F0) + 珊瑚色(#E07A5F) + 鼠尾草绿(#81B29A);深色模式自动映射为暖色调暗色。面向小学生,所有颜色避免冷硬感。
### Q: 为什么手写模型不用 freezed
Stroke/StrokePoint 是热路径高频创建对象freezed 生成的代码有额外开销。手写实现 copyWith + toJson/fromJson 更轻量。
## 2. 关键文件 + 数据流
### 16 个功能模块
| 模块 | BLoC | 职责 |
|------|------|------|
| editor | EditorBloc | 手写 + 元素 + 撤销重做 + 自动保存 |
| auth | AuthBloc | 登录/注册/角色选择/班级码加入 |
| home | HomeBloc | 首页日记列表 + 搜索 |
| calendar | CalendarBloc | 日历视图 + 日期过滤 |
| mood | MoodBloc | 心情统计 + 趋势图 |
| class_ | ClassBloc | 班级管理 + 成员列表 |
| achievement | AchievementBloc | 成就徽章系统 |
| stickers | StickerBloc | 贴纸库浏览 + 选择 |
| templates | TemplateBloc | 模板画廊 |
| profile | SettingsBloc | 主题切换 + 个人设置 |
| search | SearchBloc | 日记搜索(按心情/标签/关键词) |
| teacher | — | 老师主题发布 + 批改 |
| parent | ParentBloc | 家长监护 + 数据管理 |
| discover | DiscoverBloc | 发现页(每日推荐/热门话题/精选模板/达人日记) |
| settings | — | 设置页面 UI |
### 注入链 (app.dart)
```
MultiRepositoryProvider
├─ ApiClient
├─ AuthRepository
├─ JournalRepository (= IsarJournalRepository, 离线优先)
├─ RemoteJournalRepository (供 SyncEngine)
├─ SyncEngine
├─ ClassRepository
└─ SettingsBloc (ChangeNotifier)
└─ BlocProvider<AuthBloc>
└─ MaterialApp.router (ListenableBuilder 监听主题)
```
### 路由表
`app_router.dart` (269 行) 定义完整路由:
- `/` → 首页auth guard 重定向)
- `/login`, `/role-selection`, `/class-code-join`
- `/editor/:id?` → 编辑器
- `/calendar`, `/mood`, `/class`, `/achievements`
- `/stickers`, `/templates`, `/search`
- `/teacher/*`, `/parent/*`
- `/settings`, `/profile`
## 3. 代码逻辑
### 不变量
**响应式断点** — 手机 <600px 底部 TabBar 单列 / 平板 600-1024px 侧边双栏 / 桌面 >1024px 三栏
**触摸目标 ≥ 44px** — 面向小学生,所有可交互元素不小于 44px
**设计 Token 统一管理**`core/constants/design_tokens.dart` 定义间距/圆角/阴影
**SettingsBloc 用 ChangeNotifier** — 不用 BLoC 因为设置是全局状态ChangeNotifier + ListenableBuilder 更轻量
### 主题系统
```
AppTheme.light() / AppTheme.dark()
├─ 7 色 × 2 模式 (bg/accent/secondary/tertiary/fg/surface/rose)
├─ 3 套字体 (Noto Sans SC / Caveat / JetBrains Mono)
├─ 4 级圆角 (10/16/22/28/pill)
└─ 动画曲线 cubic-bezier(0.34, 1.56, 0.64, 1) — 弹性过冲
```
## 4. 活跃问题 + 陷阱
| 问题 | 级别 | 状态 | 说明 |
|------|------|------|------|
| ~~编辑器不加载已有数据~~ | ~~HIGH~~ | ✅ 已修复 | _loadExistingJournal 从 Isar 读取日记 + 元素 + 笔画 |
| ~~SSE 端口不一致~~ | ~~HIGH~~ | ✅ 已修复 | AppConfig 统一管理 apiBaseUrl/sseBaseUrl均指向 3000 |
| ~~编辑器打开即弹出画笔~~ | ~~HIGH~~ | ✅ 已修复 | 打开已有日记默认查看模式,点"编辑"才进入编辑模式 |
| API base URL 硬编码 | HIGH | 待修 | localhost:3000 硬编码,生产环境需配置化 |
| 多处硬编码数据未对接 API | HIGH | 部分修复 | 见下方「硬编码数据清单」 |
| 前端测试覆盖不足 | MEDIUM | 持续 | 15 个测试文件 203 个用例auth_bloc 有 1 个失败待修 |
| 状态管理不统一 | MEDIUM | 待规划 | 5 模块用 BLoC5 模块用 ChangeNotifier |
| freezed 声明未使用 | MEDIUM | 待清理 | pubspec 声明了但全部手写不可变类 |
| SyncEngine 缺少网络监听 | MEDIUM | 待做 | 只有 trySync() 方法,无自动触发 |
| 搜索功能空壳 | MEDIUM | 待做 | Isar FTS 未实现 |
| 家长/教师页面占位 | LOW | 持续 | 多处 onTap 为 SnackBar 提示 |
| core/utils/ 空目录 | LOW | 待填充 | 缺少日期格式化、颜色解析等通用工具 |
| 深色模式细节 | LOW | 持续 | 部分组件深色适配需检查 |
### 硬编码数据清单
> 2026-06-07 全面排查结果。标记 ✅ 已修复 / ❌ 待修复。
**HIGH — 已有 API应直接替换**
| 页面 | 文件 | 硬编码内容 | 后端 API | 状态 |
|------|------|-----------|----------|------|
| 首页 | home_page.dart | 用户名 `'小暖'` | AuthBloc.user | ❌ |
| 个人 | profile_page.dart | 头像 emoji `'😊'` | AuthBloc.user | ❌ |
| 个人 | profile_page.dart | 6 个固定成就徽章 | AchievementBloc | ❌ |
| 搜索 | search_page.dart | 4 个假模板名称 | TemplateBloc | ❌ |
| 贴纸 | sticker_library_page.dart | 精选贴纸包"治愈小动物" | StickerBloc | ❌ |
| 教师 | teacher_page.dart | 班级码 `'a1b2c3'` | ClassBloc | ❌ |
| 班级 | class_page.dart | 头像首字固定 `'同'` | JournalEntry.authorId | ❌ |
| 发现 | discover_page.dart | 全部 4 板块假数据 | GET /diary/discover | ✅ |
**MEDIUM — 需要 API 或可延后:**
| 页面 | 文件 | 硬编码内容 | 备注 |
|------|------|-----------|------|
| 搜索 | search_page.dart | 热门搜索 8 个关键词 | 需新增后端 API |
| 编辑器 | sticker_picker_sheet.dart | 60 个内置 emoji 贴纸 | Phase 1 占位,贴纸包 API 已有 |
| 编辑器 | tag_panel.dart | 10 个推荐标签 | 可从用户历史标签推导 |
| 贴纸 | sticker_library_page.dart | 8 个固定分类名 | StickerBloc 已有分类数据 |
| 模板 | template_gallery_page.dart | 每张卡片固定"学生专属"/"简约"标签 | 模板 API 已有 |
| 个人 | profile_page.dart | 贴纸数 `'--'` | 需新增统计 API |
**LOW — 可接受的 UI 设计常量:**
| 页面 | 内容 | 说明 |
|------|------|------|
| 编辑器 | 画笔/文本颜色面板 | 设计工具调色板 |
| 编辑器 | 字号选项 [小/中/大] | 编辑器选项 |
| 首页/日历 | 心情/天气 emoji 映射 | enum → UI 展示映射 |
| 引导页 | 3 步引导内容 | 静态引导文案 |
| 班级 | 快捷评语模板 7 条 | 内置教学工具 |
### 历史教训
- F11 深色模式修复需要 bloat bloc 测试套件同步更新 (05317d5)
- NuanjiApp 是 StatelessWidgetbuild() 可被调用多次 → 全局依赖应在 build() 中创建单例
- 25 处通用 catch(e) 静默吞异常,排查问题时需注意
- 班级码在 teacher 模块硬编码为 'a1b2c3',需接入后端 API
## 5. 变更记录
| 日期 | 变更 |
|------|------|
| 2026-06-07 | 发现页全链路打通DiscoverBloc + GET /diary/discover替换全部硬编码 |
| 2026-06-07 | 全面排查硬编码数据,新增「硬编码数据清单」章节 |
| 2026-06-07 | 编辑器新增查看模式(打开已有日记默认只读)、元素图层调整(置顶/置底)|
| 2026-06-07 | 日历页面修复:初始加载自动填充 selectedDayJournals |
| 2026-06-01 | 补充状态管理不统一、SSE 端口问题、测试缺失等新发现 |
| 2026-06-01 | IsarJournalRepository 注入为主 JournalRepository (2481c8f) |
| 2026-06-01 | 设置页 UI + Mood/成就/贴纸 BLoC (8331db6) |
| 2026-06-01 | 初始创建 — 16 模块地图、注入链、设计系统 |