主要功能: - ✅ 完成Book模型(电子书基本信息管理) - ✅ 完成Highlight模型(文本高亮功能) - ✅ 建立Flutter数据模型设计标准 技术特点: - 采用不可变对象设计模式 - 实现完整的序列化支持(toMap/fromMap) - 提供便利的工厂构造函数 - 正确处理枚举类型序列化 - 完善的空值安全处理 学习文档: - 📚 项目结构与环境配置指南 - 🎯 数据模型设计思路分析 - 💡 Flutter数据模型最佳实践 下一步: - 实现Annotation批注模型 - 设计本地存储方案 - 开始UI组件开发 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
258 lines
6.4 KiB
Markdown
258 lines
6.4 KiB
Markdown
# Flutter 数据模型实践与技巧
|
||
|
||
## 一、已完成的数据模型
|
||
|
||
### 1. Book(书籍模型)✅
|
||
**核心功能:**
|
||
- 电子书基本信息管理(书名、作者、文件格式等)
|
||
- 阅读状态跟踪(阅读中、已完成、待阅读)
|
||
- 文件信息存储(路径、大小、格式)
|
||
- 扩展信息支持(标签、评分、简介)
|
||
|
||
**设计亮点:**
|
||
- 使用`BookFormat`和`ReadingStatus`枚举确保类型安全
|
||
- 提供`Book.create()`工厂方法简化对象创建
|
||
- 完整的序列化支持(toMap/fromMap)
|
||
- 不可变对象设计,所有字段使用final
|
||
|
||
### 2. Highlight(高亮模型)✅
|
||
**核心功能:**
|
||
- 文本高亮记录(选中文本、位置范围)
|
||
- 多颜色分类支持(5种颜色对应不同用途)
|
||
- 章节关联(chapterId可选)
|
||
- 时间戳记录
|
||
|
||
**设计亮点:**
|
||
- `HighlightColor`枚举提供语义化颜色分类
|
||
- `Highlight.create()`工厂方法提供默认颜色
|
||
- `textLength`计算属性获取高亮长度
|
||
- `isInRange()`方法检查高亮范围
|
||
|
||
## 二、Flutter数据模型设计最佳实践
|
||
|
||
### 1. 不可变对象模式
|
||
```dart
|
||
class Book {
|
||
final String title; // 所有字段都是final
|
||
|
||
// 通过copyWith创建新对象,而不是修改原对象
|
||
Book copyWith({String? title}) {
|
||
return Book(title: title ?? this.title);
|
||
}
|
||
}
|
||
```
|
||
|
||
**优势:**
|
||
- 🔒 线程安全
|
||
- 🔄 状态管理友好(Flutter状态管理依赖对象比较)
|
||
- 🐛 减少意外修改bug
|
||
|
||
### 2. 空值安全设计
|
||
```dart
|
||
final String title; // 必需字段
|
||
final String? author; // 可为null的字段
|
||
final List<String> tags = const []; // 默认空列表,避免null
|
||
```
|
||
|
||
**关键符号:**
|
||
- `final`:字段只能在构造函数中赋值
|
||
- `?`:字段可以为null
|
||
- `required`:构造函数必需参数
|
||
- `const []`:不可变空列表
|
||
|
||
### 3. 枚举的正确使用
|
||
```dart
|
||
enum BookFormat { epub, mobi, txt, pdf }
|
||
|
||
// 存储时:枚举 → 字符串
|
||
'format': format.name, // "epub"
|
||
|
||
// 读取时:字符串 → 枚举
|
||
format: BookFormat.values.firstWhere((e) => e.name == "epub")
|
||
```
|
||
|
||
### 4. 工厂构造函数模式
|
||
```dart
|
||
// 便利构造函数
|
||
factory Book.create({
|
||
required String title,
|
||
required String filePath,
|
||
// 自动生成字段
|
||
}) {
|
||
return Book(
|
||
id: _generateId(), // 自动生成ID
|
||
addedDate: DateTime.now(), // 自动设置时间
|
||
status: ReadingStatus.pending, // 默认状态
|
||
title: title,
|
||
filePath: filePath,
|
||
// ...
|
||
);
|
||
}
|
||
```
|
||
|
||
**设计原则:**
|
||
- 自动生成ID和时间戳
|
||
- 为常用参数提供默认值
|
||
- 封装复杂的创建逻辑
|
||
|
||
### 5. 完整的对象生命周期管理
|
||
```dart
|
||
class Book {
|
||
// 构造函数
|
||
const Book({required this.title});
|
||
|
||
// 创建便利方法
|
||
factory Book.create({...}) { ... }
|
||
|
||
// 修改方法
|
||
Book copyWith({...}) { ... }
|
||
|
||
// 序列化方法
|
||
Map<String, dynamic> toMap() { ... }
|
||
factory Book.fromMap(Map<String, dynamic> map) { ... }
|
||
|
||
// 调试方法
|
||
String toString() { ... }
|
||
|
||
// 比较方法
|
||
bool operator ==(Object other) { ... }
|
||
int get hashCode => ...;
|
||
}
|
||
```
|
||
|
||
## 三、常见陷阱与解决方案
|
||
|
||
### 1. 枚举序列化错误
|
||
```dart
|
||
// ❌ 错误:直接存储枚举对象
|
||
'format': format, // 会导致序列化失败
|
||
|
||
// ✅ 正确:转换为字符串
|
||
'format': format.name,
|
||
|
||
// 读取时反向转换
|
||
format: BookFormat.values.firstWhere((e) => e.name == map['format'])
|
||
```
|
||
|
||
### 2. copyWith方法遗漏字段
|
||
```dart
|
||
// ❌ 错误:遗漏了chapterId参数
|
||
Highlight copyWith({String? bookId, ...}) {
|
||
return Highlight(
|
||
bookId: bookId ?? this.bookId,
|
||
// 缺少 chapterId 参数
|
||
);
|
||
}
|
||
|
||
// ✅ 正确:包含所有字段
|
||
Highlight copyWith({String? bookId, String? chapterId, ...}) {
|
||
return Highlight(
|
||
bookId: bookId ?? this.bookId,
|
||
chapterId: chapterId ?? this.chapterId, // 确保完整性
|
||
);
|
||
}
|
||
```
|
||
|
||
### 3. 工厂方法参数设计错误
|
||
```dart
|
||
// ❌ 错误:要求用户传入自动生成的字段
|
||
factory Highlight.create({
|
||
required String id, // 用户不应该手动传入ID
|
||
required DateTime createdTime, // 应该自动生成
|
||
// ...
|
||
});
|
||
|
||
// ✅ 正确:只要求用户必须提供的字段
|
||
factory Highlight.create({
|
||
required String bookId,
|
||
required String selectedText,
|
||
HighlightColor color = HighlightColor.yellow, // 提供默认值
|
||
String? chapterId,
|
||
}) {
|
||
return Highlight(
|
||
id: _generateId(), // 自动生成
|
||
createdTime: DateTime.now(), // 自动设置
|
||
// ...
|
||
);
|
||
}
|
||
```
|
||
|
||
## 四、性能优化技巧
|
||
|
||
### 1. 字符串插值优于字符串拼接
|
||
```dart
|
||
// ❌ 性能较差
|
||
return 'Book(id: ' + id + ', title: ' + title + ')';
|
||
|
||
// ✅ 性能更好
|
||
return 'Book(id: $id, title: $title)';
|
||
```
|
||
|
||
### 2. 避免重复计算
|
||
```dart
|
||
class Highlight {
|
||
// ❌ 每次调用都重新计算
|
||
int getTextLength() => endIndex - startIndex;
|
||
|
||
// ✅ 使用getter缓存计算结果
|
||
int get textLength => endIndex - startIndex;
|
||
}
|
||
```
|
||
|
||
### 3. 合理使用const构造函数
|
||
```dart
|
||
// 对于常量值使用const
|
||
static const List<String> emptyList = [];
|
||
|
||
// 构造函数标记为const
|
||
const Book({required this.title});
|
||
```
|
||
|
||
## 五、调试与测试技巧
|
||
|
||
### 1. 有意义的toString实现
|
||
```dart
|
||
@override
|
||
String toString() {
|
||
return 'Book(id: $id, title: $title, format: $format)';
|
||
}
|
||
```
|
||
|
||
### 2. 完整的对象比较
|
||
```dart
|
||
@override
|
||
bool operator ==(Object other) {
|
||
if (identical(this, other)) return true; // 先检查引用相等
|
||
return other is Book && other.id == id; // 再检查关键字段
|
||
}
|
||
|
||
@override
|
||
int get hashCode => id.hashCode; // 基于相同字段生成
|
||
```
|
||
|
||
## 六、下一步计划
|
||
|
||
### 待完成的模型:
|
||
1. **Annotation(批注模型)** - 处理用户笔记和感想
|
||
2. **Bookmark(书签模型)** - 管理阅读位置标记
|
||
3. **Bookshelf(书架模型)** - 书籍分类管理
|
||
4. **ReadingProgress(阅读进度模型)** - 跟踪阅读进度
|
||
|
||
### 待学习的技术:
|
||
1. **本地存储方案** - Hive数据库集成
|
||
2. **Repository模式** - 数据访问层设计
|
||
3. **状态管理** - Provider/Riverpod集成
|
||
4. **文件处理** - EPUB/MOBI解析
|
||
|
||
## 七、代码质量检查清单
|
||
|
||
### ✅ 数据模型检查清单:
|
||
- [ ] 所有字段使用final
|
||
- [ ] 正确使用空值安全语法(?和required)
|
||
- [ ] 实现copyWith方法并包含所有字段
|
||
- [ ] 正确的序列化(枚举转换)
|
||
- [ ] 提供工厂方法简化创建
|
||
- [ ] 重写toString、equals、hashCode
|
||
- [ ] 添加有意义的文档注释
|
||
- [ ] 提供默认值避免null
|
||
- [ ] 使用计算属性优化性能 |