👉 这个模块的功能:
实现一个“项目 + 计时”的完整业务闭环:
用户可以新建项目,在项目下记录多个计时条目(开始、结束时间),并在界面中随时查看、编辑、删除。
🧩 二、模块结构总览(职责分层)
| 层级 | 文件 | 职责 |
|---|---|---|
| UI 层 | 页面(调用 Bloc) | 用户操作:点按钮、添加项目、删除项目 |
| Bloc 层(业务编排) | projects_bloc.dart | 处理事件 → 调用数据层 → 生成状态 |
| 事件层(输入) | projects_event.dart | 定义用户行为:加载、创建、编辑、删除 |
| 状态层(输出) | projects_state.dart | 定义页面数据:有哪些项目 |
| 数据层(DataProvider) | data_provider.dart | 封装数据库 / API 的 CRUD 操作 |
| 模型层(实体) | project.dart, timer_entry.dart | 定义数据结构与业务含义 |
📘 Bloc = 中间控制层。负责协调 “事件 → 状态”,是整个模块的「大脑」。

🚀 三、核心业务流程(从点击到界面更新)
我们举个例子——用户创建一个项目:
[UI] 用户点击 “+ 新建项目”
↓
Bloc 接收到 CreateProject 事件
↓
调用 DataProvider.createProject() 在数据库中插入
↓
获取新项目对象 Project
↓
拼接到当前项目列表
↓
emit(ProjectsState(projects)) 发射新状态
↓
UI BlocBuilder 监听状态变化 → 自动刷新列表
📍这就是标准的 Flutter BLoC 数据流。
没有直接 setState(),而是状态驱动 UI。
⚙️ 四、各文件详细解析(从下往上)
1️⃣ 模型层:TimerEntry
定义一条“计时记录”,属于某个项目。
class TimerEntry {
final int id;
final String? description;
final int? projectID; // 外键:属于哪个项目
final DateTime startTime;
final DateTime? endTime;
final String? notes;
}
🔸 功能:
-
formatDuration():把时长转为 “HH:mm:ss” 格式 -
formatTime():计算当前条目的工作时长
📊 用途:
每个项目(Project)可以有多个 TimerEntry → 一对多关系。
比如:
| Project | TimerEntry |
|---|---|
| Flutter 开发 | 09:00–11:00、14:00–15:30 |
| 写博客 | 20:00–21:00 |
2️⃣ 数据访问层:DataProvider
定义所有数据库操作接口。
abstract class DataProvider {
Future<Project> createProject(...);
Future<List<Project>> listProjects();
Future<void> editProject(Project project);
Future<void> deleteProject(Project project);
...
}
🔹 特点:
-
不关心底层(可接 SQLite、本地 JSON、云端 API);
-
负责真正的数据读写;
-
还支持 import()(数据迁移):
把别的 DataProvider 的项目与计时数据导入当前系统。
🧠 类比后台开发:
它相当于 DAO 层(Data Access Object)。
3️⃣ 状态层:ProjectsState
表示页面上“当前有哪些项目”的状态。
class ProjectsState extends Equatable {
final List<Project> projects;
}
🔸 特点:
-
只读快照,每次 emit 都生成新对象;
-
用
Equatable保证 Flutter 只在真正变化时刷新; -
initial()定义空状态。
🧠 类比后台开发:
它就像 Controller 最后返回给前端的响应体(Response)。
4️⃣ 事件层:ProjectsEvent
定义了所有可能的用户操作。
abstract class ProjectsEvent {}
class LoadProjects extends ProjectsEvent {}
class CreateProject extends ProjectsEvent { ... }
class EditProject extends ProjectsEvent { ... }
class DeleteProject extends ProjectsEvent { ... }
🧩 每个事件都是“用户意图”的抽象:
-
Load → 打开页面时加载数据;
-
Create → 点击“新建项目”;
-
Edit → 修改项目;
-
Delete → 删除项目。
🧠 类比后台开发:
它就像前端发到 Controller 的不同 API 请求。
5️⃣ Bloc 层:ProjectsBloc
整个模块的业务编排中心。
class ProjectsBloc extends Bloc<ProjectsEvent, ProjectsState> {
final DataProvider data;
ProjectsBloc(this.data) : super(ProjectsState.initial()) {
on<LoadProjects>(...) // 查询
on<CreateProject>(...) // 新增
on<EditProject>(...) // 修改
on<DeleteProject>(...) // 删除
}
}
流程解析(以 Create 为例):
on<CreateProject>((event, emit) async {
// 调用底层创建
Project newProject = await data.createProject(name: event.name, colour: event.colour);
// 克隆现有列表,插入新项目
List<Project> projects = state.projects.map(Project.clone).toList();
projects.add(newProject);
projects.sort((a, b) => a.name.compareTo(b.name));
// 发射新状态
emit(ProjectsState(projects));
});
🧠 类比后台开发:
它相当于 Service 层:负责业务逻辑与数据协调。
6️⃣ 汇总 Barrel:bloc.dart
这是一个 “导出聚合文件”。
export 'projects_bloc.dart';
export 'projects_event.dart';
export 'projects_state.dart';
方便上层页面这样导入:
import 'package:timelens/blocs/projects/bloc.dart';
✅ 避免多文件重复 import。
📲 五、在页面中的实际使用
假设我们有一个 “项目列表页”:
class ProjectsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ProjectsBloc(MySqlDataProvider())..add(LoadProjects()),
child: BlocBuilder<ProjectsBloc, ProjectsState>(
builder: (context, state) {
final projects = state.projects;
return ListView.builder(
itemCount: projects.length,
itemBuilder: (_, i) => ListTile(
title: Text(projects[i].name),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () =>
context.read<ProjectsBloc>().add(DeleteProject(projects[i])),
),
),
);
},
),
);
}
}
🧠 流程:
-
进入页面 → 发出
LoadProjects; -
Bloc 调用
listProjects(); -
生成状态 → UI 自动渲染;
-
用户点击删除 → 触发事件 → Bloc 删除数据 → emit 新状态 → UI 自动更新。
🔄 六、逻辑闭环图
┌────────────┐
│ UI层:按钮 │
└──────┬─────┘
↓ add()
┌────────────┐
│ Bloc:接收事件 │
│ 调用 DataProvider │
└──────┬─────┘
↓ emit()
┌────────────┐
│ State:新状态 │
│ projects 列表变化 │
└──────┬─────┘
↓ rebuild()
┌────────────┐
│ UI层刷新 │
└────────────┘
✅ 七、总结
这套模块就是一个标准的 Flutter BLoC + Repository 架构实现:
Event:描述用户行为(输入)
Bloc:处理逻辑,协调数据与状态(中间层)
State:描述当前页面数据(输出)
DataProvider:封装持久化逻辑(底层)
Model:定义领域对象(实体)
整体实现了一个高内聚、低耦合、易扩展的项目管理 + 计时系统。
未来可以很容易拓展:统计、同步云端、导出报表等功能。
2335

被折叠的 条评论
为什么被折叠?



