- Complete MVP with Repository Pattern, SQLite storage - Provider + ChangeNotifier state management - Navigation 2.0 with deep link support - Habit CRUD with twoDayRule, notifications, categories - Backup/Restore via JSON - Statistics with streak tracking - Material You theme support - Biometric lock support - Desktop widget support - 27 languages i18n structure - Comprehensive test suite (87/89 passing)
25 KiB
25 KiB
Habo 开发文档
版本: 3.1.2+5115 | Flutter 3.41.1+ | Dart 3.11.0+
Habo 是一款极简风格的习惯追踪应用,支持 Android、iOS、Linux、macOS 多平台。所有数据存储在本地 SQLite 数据库中,无需服务端。
目录
- 项目概览
- 技术栈与依赖
- 目录结构
- 架构设计
- 数据模型
- 数据库 Schema
- 核心模块详解
- 导航系统
- 状态管理
- 国际化 (i18n)
- 主题系统
- 通知系统
- 备份与恢复
- 桌面端小组件
- 生物识别认证
- CI/CD 与构建
- 开发指南
1. 项目概览
Habo 是一个功能完整的习惯追踪应用,核心功能包括:
- 习惯管理 — 创建、编辑、归档、删除习惯,支持拖拽排序
- 两种习惯类型 — 布尔型(打卡/未打卡)和数值型(进度追踪,如跑步 5km)
- 日历视图 — 基于
table_calendar的月度视图,标记每日状态 - 连续天数 (Streak) — 支持普通模式和"两天法则"(允许间隔一天)
- 分类系统 — 习惯可归属多个分类,支持按分类筛选
- 统计分析 — 饼图总览、月度柱状图、个人习惯统计卡片
- 通知提醒 — 每日提醒和成就/惩罚通知
- 备份恢复 — JSON 文件导入/导出,支持跨设备迁移
- 桌面小组件 — iOS/Android 主屏幕小组件显示今日进度
- 生物识别锁 — 支持指纹/面容锁定应用
- Material You — 支持动态取色主题
2. 技术栈与依赖
| 类别 | 技术 | 说明 |
|---|---|---|
| 框架 | Flutter 3.41.1+ | 跨平台 UI 框架 |
| 语言 | Dart 3.11.0+ | 支持 null safety |
| 数据库 | sqflite / sqflite_common_ffi | SQLite(移动端/桌面端) |
| 状态管理 | provider + ChangeNotifier | 响应式状态管理 |
| 图表 | fl_chart | 统计图表渲染 |
| 日历 | table_calendar | 日历视图组件 |
| 通知 | awesome_notifications | 本地通知调度 |
| 国际化 | flutter_localizations + intl | ARB 文件管理多语言 |
| 字体 | google_fonts + 动态取色 | dynamic_color (Material You) |
| 音效 | flutter_soloud + audio_session | 习惯完成音效反馈 |
| 认证 | local_auth | 指纹/面容生物识别 |
| 小组件 | home_widget | iOS/Android 桌面小组件 |
| 桌面窗口 | window_manager | Linux/macOS 窗口管理 |
| 测试 | flutter_test + mocktail | 单元测试与 mock |
| CI | GitHub Actions | 自动测试与 APK 构建 |
| 发布 | fastlane | 多平台商店发布自动化 |
3. 目录结构
Habo-master/
├── lib/ # 应用主源码
│ ├── main.dart # 应用入口,初始化流程
│ ├── constants.dart # 枚举类型和颜色常量
│ ├── themes.dart # 主题定义(亮色/暗色/OLED)
│ ├── helpers.dart # 工具函数(日期解析等)
│ │
│ ├── model/ # 数据模型层
│ │ ├── habit_data.dart # HabitData 习惯数据模型
│ │ ├── habo_model.dart # HaboModel 数据库操作层
│ │ ├── category.dart # Category 分类模型
│ │ ├── settings_data.dart # 设置数据模型
│ │ └── backup.dart # 备份数据模型
│ │
│ ├── habits/ # 习惯管理模块
│ │ ├── habit.dart # Habit StatefulWidget(日历卡片)
│ │ ├── habits_manager.dart # HabitsManager 业务逻辑中心
│ │ ├── habits_screen.dart # 习惯列表主屏幕
│ │ ├── create_habit.dart # 创建习惯页面
│ │ └── edit_habit.dart # 编辑习惯页面
│ │
│ ├── statistics/ # 统计分析模块
│ │ ├── statistics.dart # 统计数据计算逻辑
│ │ ├── statistics_screen.dart # 统计主屏幕
│ │ ├── statistics_card.dart # 单个习惯统计卡片
│ │ ├── overall_statistics_card.dart # 总览饼图卡片
│ │ └── monthly_graph.dart # 月度柱状图
│ │
│ ├── settings/ # 设置模块
│ │ ├── settings_manager.dart # 设置管理器(持久化)
│ │ ├── settings_screen.dart # 设置页面 UI
│ │ └── color_icon.dart # 颜色选择器组件
│ │
│ ├── navigation/ # 导航系统
│ │ ├── routes.dart # 路由常量
│ │ ├── app_router.dart # RouterDelegate 实现
│ │ ├── app_state_manager.dart # 导航状态管理
│ │ ├── route_information_parser.dart # 深度链接解析
│ │ └── navigation.dart # 导出文件
│ │
│ ├── repositories/ # 数据仓库层(Repository Pattern)
│ │ ├── habit_repository.dart # 习惯仓库接口
│ │ ├── sqlite_habit_repository.dart # SQLite 实现
│ │ ├── event_repository.dart # 事件仓库接口
│ │ ├── category_repository.dart # 分类仓库接口
│ │ └── repository_factory.dart # 仓库工厂(DI)
│ │
│ ├── services/ # 服务层
│ │ ├── service_locator.dart # 服务定位器(DI 容器)
│ │ ├── notification_service.dart # 通知服务
│ │ ├── backup_service.dart # 备份/恢复服务
│ │ ├── ui_feedback_service.dart # UI 反馈服务(Snackbar)
│ │ ├── biometric_auth_service.dart # 生物识别服务
│ │ └── home_widget_service.dart # 桌面小组件服务
│ │
│ ├── widgets/ # 可复用 UI 组件
│ │ ├── habit_progress_indicator.dart # 进度指示器
│ │ ├── biometric_auth_wrapper.dart # 生物识别包裹组件
│ │ ├── category_filter_row.dart # 分类筛选行
│ │ ├── progress_input_modal.dart # 数值进度输入弹窗
│ │ ├── home_widget_data.dart # 小组件数据模型
│ │ ├── habo_home_widget.dart # 小组件渲染
│ │ └── text_container.dart # 文本输入组件
│ │
│ ├── onboarding/ # 引导页
│ │ ├── onboarding_screen.dart
│ │ └── onboarding.dart
│ │
│ ├── l10n/ # 国际化 ARB 文件(27 种语言)
│ └── generated/ # 自动生成的代码(intl 等)
│
├── test/ # 测试目录
├── assets/ # 静态资源
│ ├── images/ # 图片(含 onboard/ 引导图)
│ ├── sounds/ # 音效文件
│ └── google_fonts/ # 本地字体文件
│
├── android/ # Android 平台代码
├── ios/ # iOS 平台代码
├── linux/ # Linux 平台代码
├── macos/ # macOS 平台代码
├── fastlane/ # 发布自动化配置
├── .github/workflows/ci.yml # CI/CD 流水线
└── pubspec.yaml # 项目配置和依赖声明
4. 架构设计
4.1 整体架构
┌─────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (Screens, Widgets) │
│ habits_screen statistics_screen settings_screen │
├─────────────────────────────────────────────────────┤
│ Business Logic Layer │
│ HabitsManager (ChangeNotifier) │
│ SettingsManager │
├─────────────────────────────────────────────────────┤
│ Service Layer │
│ NotificationService BackupService UIFeedbackService│
│ BiometricAuthService HomeWidgetService │
├─────────────────────────────────────────────────────┤
│ Repository Layer │
│ HabitRepository EventRepository CategoryRepository│
│ RepositoryFactory │
├─────────────────────────────────────────────────────┤
│ Data Layer │
│ HaboModel → SQLite (sqflite) │
└─────────────────────────────────────────────────────┘
4.2 设计模式
| 模式 | 应用场景 |
|---|---|
| Repository Pattern | HabitRepository、EventRepository、CategoryRepository 抽象数据访问 |
| Service Locator | ServiceLocator 单例管理全局服务实例 |
| Factory Pattern | RepositoryFactory 创建各仓库实例 |
| Observer Pattern | ChangeNotifier + Provider 实现响应式 UI 更新 |
| Singleton Pattern | HaboModel、ServiceLocator 确保单实例 |
| Strategy Pattern | HabitType 枚举区分布尔/数值习惯的不同处理逻辑 |
4.3 数据流
用户操作 → Widget
→ HabitsManager (ChangeNotifier)
→ Repository (数据访问抽象)
→ HaboModel (SQLite 操作)
→ Database
HabitsManager.notifyListeners()
→ Provider 更新
→ Widget 重建
4.4 初始化流程 (main.dart)
1. SettingsManager 初始化(读取持久化设置)
2. 创建 HaboModel 实例(共享数据库连接)
3. 调用 HaboModel.initDatabase() 初始化 SQLite
4. 初始化 ServiceLocator(注册所有服务)
5. 创建 HabitsManager(注入仓库和服务依赖)
6. 调用 HabitsManager.loadHabits() 加载数据
7. 初始化通知服务
8. 创建 AppRouter(注入状态管理器)
9. 启动日变化定时器(检测跨日自动刷新)
10. 渲染 MaterialApp.router
5. 数据模型
5.1 核心枚举 (constants.dart)
// 习惯类型
enum HabitType { boolean, numeric }
// 日状态类型
enum DayType { clear, check, fail, skip, progress }
// 主题模式
enum Themes { device, light, dark, oled, materialYou }
5.2 HabitData (model/habit_data.dart)
习惯的完整数据模型:
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int? |
数据库自增主键 |
position |
int |
排序位置 |
title |
String |
习惯标题 |
twoDayRule |
bool |
是否启用两天法则 |
cue |
String |
提示(触发器) |
routine |
String |
例行动作描述 |
reward |
String |
奖励描述 |
showReward |
bool |
是否显示奖励通知 |
sanction |
String |
惩罚描述 |
showSanction |
bool |
是否显示惩罚通知 |
accountant |
String |
问责伙伴 |
advanced |
bool |
是否显示高级选项(cue/routine/reward) |
notification |
bool |
是否启用通知提醒 |
notTime |
TimeOfDay |
通知时间 |
events |
SplayTreeMap<DateTime, List> |
事件记录(日期→事件列表) |
habitType |
HabitType |
布尔型或数值型 |
targetValue |
double |
数值型目标值(默认 100) |
partialValue |
double |
数值型部分进度增量(默认 10) |
unit |
String |
数值型单位 |
categories |
List<Category> |
所属分类列表 |
archived |
bool |
是否已归档 |
streak |
int |
当前连续天数(运行时计算) |
事件列表结构 (events[date]):
布尔型: [DayType, comment]
数值型: [DayType, comment, progressValue, targetValue]
关键方法:
isCompletedForDate(date)— 判断某日是否完成getProgressForDate(date)— 获取某日进度值getProgressPercentage(date)— 获取进度百分比
5.3 Category (model/category.dart)
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int? |
自增主键 |
title |
String |
分类名称 |
iconCodePoint |
int |
图标 Unicode 码点 |
fontFamily |
String? |
图标字体族(如 FontAwesome) |
6. 数据库 Schema
数据库版本: 9,文件: habo_db0.db
6.1 habits 表
CREATE TABLE habits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
position INTEGER, -- 排序位置
title TEXT, -- 习惯标题
twoDayRule INTEGER, -- 两天法则开关 (0/1)
cue TEXT DEFAULT '', -- 提示触发器
routine TEXT DEFAULT '', -- 例行动作
reward TEXT DEFAULT '', -- 奖励
showReward INTEGER, -- 显示奖励 (0/1)
advanced INTEGER, -- 高级模式 (0/1)
notification INTEGER, -- 通知开关 (0/1)
notTime TEXT, -- 通知时间 (HH:MM)
sanction TEXT DEFAULT '', -- 惩罚
showSanction INTEGER DEFAULT 0, -- 显示惩罚 (0/1)
accountant TEXT DEFAULT '', -- 问责伙伴
habitType INTEGER DEFAULT 0, -- 习惯类型 (0=布尔, 1=数值)
targetValue REAL DEFAULT 1.0, -- 目标值
partialValue REAL DEFAULT 1.0, -- 部分增量
unit TEXT DEFAULT '', -- 单位
archived INTEGER DEFAULT 0 -- 归档状态 (0/1)
);
6.2 events 表
CREATE TABLE events (
id INTEGER, -- 外键 → habits.id
dateTime TEXT, -- 日期时间字符串
dayType INTEGER, -- DayType 枚举索引
comment TEXT DEFAULT '', -- 备注
progressValue REAL DEFAULT 0.0, -- 进度值(数值习惯)
targetValue REAL DEFAULT 0.0, -- 目标值快照
PRIMARY KEY(id, dateTime),
FOREIGN KEY (id) REFERENCES habits(id) ON DELETE CASCADE
);
DayType 枚举值:
| 值 | 含义 |
|---|---|
| 0 | clear — 清除 |
| 1 | check — 完成 |
| 2 | fail — 失败 |
| 3 | skip — 跳过 |
| 4 | progress — 进行中(数值型部分完成) |
6.3 categories 表
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL, -- 分类名称
iconCodePoint INTEGER NOT NULL, -- 图标码点
fontFamily TEXT -- 图标字体族
);
6.4 habit_categories 关联表
CREATE TABLE habit_categories (
habit_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
PRIMARY KEY (habit_id, category_id),
FOREIGN KEY (habit_id) REFERENCES habits(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
);
6.5 数据库迁移历史
| 版本 | 变更 |
|---|---|
| V1→V2 | events 表增加 comment 字段 |
| V2→V3 | habits 表增加 sanction、showSanction、accountant 字段 |
| V3→V4 | habits 表增加 habitType、targetValue、partialValue、unit;events 增加 progressValue |
| V4→V5 | events 表增加 targetValue;新建 categories 和 habit_categories 表 |
| V5→V6 | habits 表增加 archived 字段 |
| V6→V7 | events 表确保有 targetValue;categories 增加 fontFamily |
| V7→V8 | events 表确保有 targetValue |
| V8→V9 | events 表确保有 targetValue |
7. 核心模块详解
7.1 HabitsManager (habits/habits_manager.dart)
中心业务逻辑管理器,继承 ChangeNotifier,是整个应用的核心。
职责:
- 管理 habits 和 categories 的内存状态
- 协调 Repository 层的数据操作
- 集成通知、备份、UI 反馈等服务
- 处理拖拽排序、归档、undo 等交互逻辑
核心 API:
// 习惯 CRUD
Future<void> loadHabits()
Future<void> addHabit(Habit habit)
Future<void> editHabit(Habit habit)
Future<void> deleteHabit(int id)
Future<void> archiveHabit(int id)
void reorderList(int oldIndex, int newIndex)
// 事件操作
Future<void> addEvent(int id, DateTime date, List event)
Future<void> deleteEvent(int id, DateTime date)
// 分类操作
Future<void> loadCategories()
Future<void> addCategory(Category category)
Future<void> updateCategory(Category category)
Future<void> deleteCategory(int id)
Future<void> updateHabitCategories(int habitId, List<Category> categories)
// 服务调用
Future<void> createBackup()
Future<void> loadBackup(String path)
Future<void> resetNotifications()
void updateHomeWidget()
// 数据访问
List<Habit> get activeHabits // 未归档习惯
List<Habit> get archivedHabits // 已归档习惯
Habit? findHabitById(int id)
7.2 HaboModel (model/habo_model.dart)
直接操作 SQLite 数据库的底层类。
- 使用
sqflite(移动端)或sqflite_common_ffi(Linux/macOS) - 管理数据库创建、迁移、CRUD
- 处理
PRAGMA foreign_keys = ON级联删除
7.3 Repository 层 (repositories/)
为 HaboModel 提供抽象接口,实现关注点分离:
HabitRepository— 习惯 CRUD、排序、批量操作EventRepository— 事件增删查CategoryRepository— 分类 CRUD 及关联管理RepositoryFactory— 创建各 Repository 实例,注入HaboModel
7.4 Service 层 (services/)
| 服务 | 职责 |
|---|---|
ServiceLocator |
单例 DI 容器,初始化并持有所有服务实例 |
NotificationService |
管理本地通知调度(每日提醒、奖励/惩罚) |
BackupService |
数据库备份为 JSON 文件、从 JSON 恢复 |
UIFeedbackService |
统一的 Snackbar 消息展示(成功/失败/警告) |
BiometricAuthService |
封装 local_auth 生物识别认证 |
HomeWidgetService |
更新 iOS/Android 主屏幕小组件数据 |
8. 导航系统
采用 Flutter Navigation 2.0,基于 RouterDelegate。
路由定义
| 常量 | 路径 | 页面 |
|---|---|---|
splashPath |
/ |
启动页 |
habitsPath |
/habits |
习惯列表主页 |
statisticsPath |
/statistics |
统计页 |
settingsPath |
/settings |
设置页 |
onboardingPath |
/onboarding |
引导页 |
createHabitPath |
/create |
创建习惯 |
editHabitPath |
/edit |
编辑习惯 |
whatsNewPath |
/whatsnew |
更新日志 |
深度链接
支持 habo:// scheme,例如 habo://settings 直接跳转设置页。
关键类
AppRouter—RouterDelegate实现,管理页面栈AppStateManager— 管理各页面的显示状态(bool 标志位)HaboRouteInformationParser— 解析 URL 到HaboRouteConfiguration
9. 状态管理
使用 Provider + ChangeNotifier 模式:
MultiProvider(
providers:
- ChangeNotifierProvider<SettingsManager>
- ChangeNotifierProvider<HabitsManager>
- ChangeNotifierProvider<AppStateManager>
)
HabitsManager— 习惯数据变化时调用notifyListeners(),驱动 UI 重建SettingsManager— 设置变更时通知(主题、音效等)AppStateManager— 导航状态变更通知- 注意:
AppRouter不监听HabitsManager,避免数据变化导致非预期的导航跳转
10. 国际化 (i18n)
- 使用
flutter_intl+ ARB 文件管理 - 文件位于
lib/l10n/intl_*.arb - 支持 27 种语言:
- 中文(简体/繁体)、英语、西班牙语、法语、德语、意大利语、葡萄牙语、俄语、日语(未列出但可能有)、阿拉伯语、希伯来语、波兰语、荷兰语、瑞典语、捷克语、越南语、印尼语、土耳其语、乌克兰语、加泰罗尼亚语、斯洛伐克语、巴斯克语、世界语、挪威语等
- 生成代码在
lib/generated/目录
添加新语言:
- 在
lib/l10n/下创建intl_<locale>.arb - 运行
flutter gen-l10n生成代码
11. 主题系统
HaboTheme 类提供三种主题:
| 主题 | 说明 |
|---|---|
lightTheme |
浅色主题,浅灰背景 (#FAFAFA) |
darkTheme |
深色主题,纯黑背景 (#000000) |
oledTheme |
OLED 深色主题,纯黑背景 |
主题模式 (Themes 枚举):
device— 跟随系统light— 强制浅色dark— 强制深色oled— OLED 黑色materialYou— Material You 动态取色
特性:
- 使用 Google Fonts 自定义字体
- 主色调:
#09BF30(绿色) - 支持平台差异(iOS/Android 不同组件样式)
12. 通知系统
使用 awesome_notifications 实现本地通知:
- 每日提醒 — 用户设定时间推送提醒
- 奖励通知 — 完成习惯时触发(可配置音效)
- 惩罚通知 — 习惯失败时触发
NotificationService 通过 HabitsManager 调用:
resetNotifications()— 重置所有习惯通知removeNotifications(id)— 删除指定习惯的通知handleHabitEventAdded()— 事件添加后触发通知
13. 备份与恢复
BackupService 提供完整的数据导入/导出:
- 导出: 将所有 habits、events、categories 序列化为 JSON 文件
- 导入: 从 JSON 文件解析并恢复到数据库
- 兼容性: 支持旧版格式迁移
- 文件选择: 使用
flutter_file_dialog(移动端)或file_picker(桌面端)
14. 桌面端小组件
使用 home_widget 包实现 iOS/Android 主屏幕小组件:
- 小组件类型: 170x170 圆形进度指示器
- 数据传递: 通过
HomeWidgetService更新数据 - 显示内容: 今日习惯完成数量 / 总数量
- 渲染:
CircularProgressPainter自定义绘制多段圆弧
15. 生物识别认证
BiometricAuthService封装local_authBiometricAuthWrapperWidget 包裹主内容- 支持指纹和面容识别
- 应用从后台恢复时重新验证
- 认证失败提供重试对话框
16. CI/CD 与构建
GitHub Actions (ci.yml)
触发条件: push/PR 到 main 或 develop 分支
流水线:
- test —
flutter analyze+flutter test - build-android(依赖 test 通过):
flutter build apk --release --split-per-abi --no-tree-shake-icons- 按 CPU 架构分拆 APK(arm64-v8a, armeabi-v7a, x86_64)
- 上传构建产物
本地构建
# 安装依赖
flutter pub get
# 生成图标包
dart run flutter_iconpicker:generate_packs --packs fontAwesomeIcons
# 生成国际化代码
flutter gen-l10n
# 运行测试
flutter test
# 构建 APK
flutter build apk --release
# 构建 iOS
flutter build ios --release
# 桌面端
flutter build linux --release
flutter build macos --release
Fastlane
fastlane/ 目录包含多平台商店发布的自动化配置。
17. 开发指南
环境要求
- Flutter SDK >= 3.41.1
- Dart SDK >= 3.11.0
- Android: Java 17, minSdk 21
- iOS: Xcode (最新版)
- Linux: 额外依赖
sqflite_common_ffi
项目约定
- 状态管理 — 使用
ChangeNotifier+Provider,新功能应创建 Manager 类 - 数据访问 — 通过 Repository 接口,不直接使用
HaboModel - 服务依赖 — 通过
ServiceLocator获取,不手动创建实例 - 国际化 — 所有用户可见文本必须使用
AppLocalizations,不硬编码字符串 - 主题 — 使用
HaboTheme定义的颜色和样式,不直接写色值 - 数据库迁移 — 修改 Schema 必须新增迁移方法并更新
_dbVersion - 测试 — 使用
mocktailmock Repository 层,测试业务逻辑而非 UI 渲染
添加新功能的典型流程
- 在
model/中定义或修改数据模型 - 在
repositories/中添加/更新 Repository 接口和实现 - 在
services/中添加服务(如需要) - 在
HabitsManager中添加业务逻辑方法 - 在对应的 screen/widget 中实现 UI
- 在
lib/l10n/intl_en.arb中添加国际化文本 - 编写单元测试
关键文件速查
| 需求 | 文件 |
|---|---|
| 添加新习惯字段 | model/habit_data.dart + model/habo_model.dart + 数据库迁移 |
| 修改日历行为 | habits/habit.dart |
| 添加新统计图表 | statistics/ 目录 |
| 修改通知逻辑 | services/notification_service.dart |
| 添加新设置项 | settings/settings_manager.dart + settings_screen.dart |
| 修改导航流程 | navigation/app_router.dart + navigation/app_state_manager.dart |
| 添加新语言 | lib/l10n/intl_<locale>.arb |
| 修改主题 | themes.dart |