本文内容包含:结构说明、使用方法、踩坑点、当前项目采用的方案(GetStorage + Hive)、目录结构、初始化方法、注意事项等。
📘 桑拿控制器项目持久化层(Storage Layer)技术笔记
本笔记用于记录 Sauna Controller 项目中"本地存储(持久化层)"的设计方式、用法、文件结构和常见错误,方便后续开发、维护和排查问题。
⭐ 1. 持久化层作用
项目中所有需要"应用重启后仍然保留"的数据都要放在持久层,例如:
✔ 彩灯状态(开关、颜色、亮度、角度、模式)
✔ 卡路里历史记录(最多 15 条)
✔ 用户设置项(未来扩展)
✔ 运行历史值(温杯、次数等)
为了方便使用和提高扩展性,项目采用了 两种本地存储方式:
⭐ 2. 项目中使用的两种存储方式
A. GetStorage → 用于轻量级 Key--Value 保存
适合存:
- 彩灯开关
- 当前色号 index
- 当前亮度 level
- 色环角度 angle
- 是否渐变模式 gradualMode
- 一些布尔值、整型、小配置项
特点:
| 特点 | 说明 |
|---|---|
| 非常轻量 | 无需模型类,直接 key-value |
| 没有效率问题 | 读写只操作内存,写入自动持久化 |
| 使用简单 | .write()、.read() |
示例:
dart
box.write("color_isOn", true);
bool on = box.read("color_isOn");
B. Hive(Hive + hive_flutter)→ 用于结构化数据持久化
适合存:
- 卡路里历史记录(有日期、有卡路里值)
特点:
| 特点 | 说明 |
|---|---|
| 高性能 NoSQL 数据库 | Flutter 最快的本地库之一 |
| Dart 强类型 | 需要 Model + Adapter |
| 适合复杂对象 | 可存 List、对象、结构体 |
| 本项目用于存 CalorieRecord | 历史卡路里记录 |
结构模型:
dart
@HiveType(typeId: 1)
class CalorieRecord extends HiveObject {
@HiveField(0) int calories;
@HiveField(1) int durationMin;
@HiveField(2) int temperature;
@HiveField(3) DateTime time;
}
编译后会生成:
calorie_record.g.dart
⭐ 3. 持久化目录结构(项目现状)
lib/
├─ models/
│ ├─ calorie_record.dart ← Hive 模型(包含 adapter)
│ └─ calorie_record.g.dart ← 自动生成的适配器文件
│
├─ services/
│ ├─ storage/
│ │ └─ storage_service.dart ← 持久化统一入口(本层核心)
│ └─ ble/...
│
├─ main.dart ← 初始化 Storage
└─ ...
⭐ 4. 全局持久化入口:AppStorage()
整个项目只需要 一个统一的存储服务类,用于管理所有存储的读写。
文件:
lib/services/storage/storage_service.dart
核心职责:
- 初始化 GetStorage(轻量 KV)
- 初始化 Hive(结构化数据)
- 注册 Hive Adapter
- 打开 Hive box(calorie_history)
- 提供所有持久化的 Getter / Setter
⭐ 5. AppStorage 初始化流程
⚠ 必须在
main.dart调用,否则 Hive / GetStorage 都不能使用。
main.dart ⬇(当前项目已更新为正确写法)
dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 全局持久化初始化
await AppStorage().init();
runApp(const SaunaApp());
}
⭐ 6. AppStorage 内部实现重点(总结版)
✔ 初始化 GetStorage
dart
await GetStorage.init();
✔ 初始化 Hive(注意:用 Hive.initFlutter,不用 path_provider)
dart
await Hive.initFlutter();
✔ 注册 Adapter
dart
Hive.registerAdapter(CalorieRecordAdapter());
✔ 打开 box
dart
historyBox = await Hive.openBox<CalorieRecord>("calorie_history");
✔ GetStorage 中的 Key--Value 访问
dart
bool get colorIsOn => box.read("color_isOn") ?? false;
set colorIsOn(bool v) => box.write("color_isOn", v);
✔ Hive 中的历史记录保存(只有 15 条)
dart
void addCalorieRecord(CalorieRecord r) {
if (historyBox.length >= 15) {
historyBox.deleteAt(0);
}
historyBox.add(r);
}
⭐ 7. 常见错误 & 排查指南(最重要)
❌ 1. 忘记在 main.dart 初始化
症状:Hive 报错:box is null or not opened
解决:确认 main.dart 有:
dart
await AppStorage().init();
❌ 2. 使用 Hive.init 而不是 Hive.initFlutter
症状:Web 端无法运行 / Android 运行但警告
解决:
dart
await Hive.initFlutter();
❌ 3. calorie_record.g.dart 未生成
症状:找不到 CalorieRecordAdapter
解决:
终端运行:
flutter packages pub run build_runner build --delete-conflicting-outputs
❌ 4. storage_service.dart 路径不一致
症状:import 报错
正确目录:
lib/services/storage/storage_service.dart
❌ 5. Hive Box 打开失败
原因通常是:
- 适配器未注册
- typeId 冲突
- g.dart 未生成
⭐ 8. 彩灯模块持久化字段(当前项目)
| 字段 | 说明 | 存储方式 |
|---|---|---|
| color_isOn | 灯开关状态 | GetStorage |
| color_index | 颜色序号 1~12 | GetStorage |
| color_brightness | 亮度等级 1~4 | GetStorage |
| color_angle | 色环角度 | GetStorage |
| color_gradual | 是否渐变模式 | GetStorage |
(轻量,适合 GetStorage)
⭐ 9. 卡路里模块持久化字段(当前项目)
| 字段 | 说明 | 存储方式 |
|---|---|---|
| calorie_history | 卡路里历史记录(最多 15 条) | Hive |
(结构化,适合 Hive)
⭐ 10. 什么时候使用哪种存储?
| 数据类型 | 推荐存储 |
|---|---|
| 简单状态(bool、int、String) | GetStorage |
| 结构化对象 | Hive |
| 历史记录、列表 | Hive |
| UI 设置项 | GetStorage |
| 大体积数据 | Hive |
⭐ 总结(一句话)
GetStorage 负责简单状态(KV),Hive 负责结构化历史记录(对象)。
整个项目所有持久化入口都在 AppStorage(),main.dart 启动时必须 init() 一次。