目录
-
- 前言
- 一、项目概览与功能总览
-
- [1.1 一句话介绍](#1.1 一句话介绍)
- [1.2 核心功能模块](#1.2 核心功能模块)
- [1.3 设计理念](#1.3 设计理念)
- 二、技术选型与依赖分析
-
- [2.1 整体技术栈](#2.1 整体技术栈)
- [2.2 为什么选择 GetX?](#2.2 为什么选择 GetX?)
- [2.3 为什么选择 SQLite?](#2.3 为什么选择 SQLite?)
- 三、项目架构设计
-
- [3.1 目录结构](#3.1 目录结构)
- [3.2 分层架构图](#3.2 分层架构图)
- [3.3 GetX 依赖注入绑定](#3.3 GetX 依赖注入绑定)
- 四、数据模型与数据库设计
-
- [4.1 核心数据模型](#4.1 核心数据模型)
- [4.2 数据库表结构](#4.2 数据库表结构)
- [4.3 数据库初始化与版本管理](#4.3 数据库初始化与版本管理)
- [4.4 多版本升级策略](#4.4 多版本升级策略)
- [五、Repository 数据访问层](#五、Repository 数据访问层)
-
- [5.1 GoalRepository 实现](#5.1 GoalRepository 实现)
- [5.2 为什么要用 Repository 模式?](#5.2 为什么要用 Repository 模式?)
- [六、GetX 响应式状态管理](#六、GetX 响应式状态管理)
-
- [6.1 Controller 中的响应式变量](#6.1 Controller 中的响应式变量)
- [6.2 View 中的响应式绑定](#6.2 View 中的响应式绑定)
- 七、番茄钟功能实现
-
- [7.1 番茄钟计时器核心逻辑](#7.1 番茄钟计时器核心逻辑)
- [7.2 番茄钟数据持久化](#7.2 番茄钟数据持久化)
- [八、CustomPainter 环形进度条](#八、CustomPainter 环形进度条)
-
- [8.1 绘制原理](#8.1 绘制原理)
- [8.2 在 Widget 中使用](#8.2 在 Widget 中使用)
- 九、归档系统实现
-
- [9.1 归档与恢复逻辑](#9.1 归档与恢复逻辑)
- [9.2 按日期分组展示](#9.2 按日期分组展示)
- 十、隐私合规与用户协议
-
- [10.1 首次启动协议展示](#10.1 首次启动协议展示)
- [10.2 隐私保护设计](#10.2 隐私保护设计)
- 十一、快速上手与运行指南
-
- [11.1 环境要求](#11.1 环境要求)
- [11.2 运行项目](#11.2 运行项目)
- [11.3 构建发布包](#11.3 构建发布包)
- 十二、未来规划与拓展方向
- 总结
前言
在快节奏的工作和生活中,高效的目标管理和专注力训练变得越来越重要。市面上虽然有很多类似的应用,但大多数需要联网、存在隐私顾虑,或者功能臃肿。XMB Tracker 正是为了解决这些痛点而诞生的------一款基于 Flutter 开发的离线优先、轻量纯粹的目标管理与番茄钟专注应用。
本文将从项目架构、技术选型、核心功能实现、数据库设计、UI 绘制等多个维度,带你深入剖析 XMB Tracker 的每一个技术细节。无论你是 Flutter 初学者想要学习完整的项目架构,还是有经验的开发者想要参考 GetX + Repository Pattern 的最佳实践,这篇文章都能让你有所收获。
💡 本文所有代码均来自 XMB Tracker 开源项目,基于 Flutter + GetX + SQLite 技术栈,支持 Android / iOS / HarmonyOS(鸿蒙)三端运行。
一、项目概览与功能总览
1.1 一句话介绍
XMB Tracker 是一款跨平台的离线目标管理与专注计时应用,所有数据本地存储,无需联网即可使用,支持 Android、iOS 和 HarmonyOS(鸿蒙) 三大平台。
1.2 核心功能模块
| 功能模块 | 功能说明 | 对应 Tab |
|---|---|---|
| 目标管理 | 创建、编辑、完成、归档目标,支持多种排序方式 | Tab 1 |
| 番茄钟专注 | 自定义专注/休息时长,实时统计专注数据 | Tab 2 |
| 归档系统 | 按日期分组管理已完成目标,支持批量恢复 | Tab 3 |
| 功能中心 | 统计总览、个性化设置、隐私政策 | Tab 4 |
1.3 设计理念
应用的核心设计理念围绕四个关键词展开:
- 离线优先 :所有数据存储在本地 SQLite 数据库,无需服务器,保护用户隐私
- 中文友好:完整的中文界面和文档,降低使用门槛
- 轻量聚焦:只做目标管理和专注计时,不堆砌多余功能
- 高定制性:番茄钟参数、目标排序方式均可自定义
🎯 项目定位:面向注重隐私和效率的个人用户,提供一个无广告、无网络依赖的纯净生产力工具。
二、技术选型与依赖分析
2.1 整体技术栈
XMB Tracker 采用了成熟且高效的 Flutter 技术方案,以下是完整的技术栈概览:
| 技术层级 | 选型 | 版本 | 用途 |
|---|---|---|---|
| UI 框架 | Flutter | Dart >=2.19.6 | 跨平台 UI 开发 |
| 状态管理 | GetX | 4.6.6 | 响应式状态管理 + 依赖注入 + 路由 |
| 数据存储 | sqflite | 鸿蒙定制版 | 本地 SQLite 数据库 |
| 图标渲染 | flutter_svg | 2.0.9 | SVG 图标加载 |
| 动画引擎 | simple_animations | 5.0.2 | 复杂动画效果 |
| 文件操作 | file_selector | 鸿蒙定制版 | 文件选择器 |
| 分享功能 | share_plus | 鸿蒙定制版 | 系统分享 |
| 路径管理 | path_provider | 鸿蒙定制版 | 获取本地路径 |
2.2 为什么选择 GetX?
在 Flutter 生态中,状态管理方案众多(Provider、Riverpod、Bloc 等),选择 GetX 基于以下考量:
yaml
# pubspec.yaml 核心依赖
dependencies:
get: ^4.6.6
sqflite: # 鸿蒙定制版本
flutter_svg: ^2.0.9
simple_animations: ^5.0.2
GetX 的三大优势让它非常适合中小型项目:
- 极简 API :
.obs声明响应式变量,Obx()包裹 UI 即可自动刷新,无需样板代码 - 全家桶方案:内置状态管理、路由管理、依赖注入,一个库解决三个问题
- 高性能 :只刷新绑定的
Obx组件,不会触发整个页面重建
⚠️ 注意:GetX 的灵活性也意味着需要开发者自律地组织代码结构,否则大型项目中容易出现 Controller 臃肿的问题。本项目通过 Repository 模式有效规避了这一点。
2.3 为什么选择 SQLite?
对于离线优先的应用,本地数据库是刚需。选择 SQLite 的理由:
- 零依赖:嵌入式数据库,无需额外安装数据库服务
- 高性能:读写速度远超 SharedPreferences / JSON 文件
- 结构化查询:支持 SQL 语句,方便复杂查询和排序
- 成熟稳定 :SQLite 官方文档 完善,社区支持广泛
三、项目架构设计
3.1 目录结构
项目采用了模块化分层架构,严格遵循单一职责原则:
lib/
├── main.dart # 应用入口
└── app/
├── core/ # 核心配置层
│ ├── theme/ # 主题(颜色、字体、样式)
│ └── translations/ # 国际化翻译
├── data/ # 数据层
│ ├── models/ # 数据模型定义
│ ├── repositories/ # 数据仓库(CRUD 操作)
│ └── services/ # 业务服务(数据库初始化等)
├── modules/ # 功能模块层
│ └── home/ # 主页模块
│ ├── bindings/ # 依赖注入绑定
│ ├── controllers/ # 业务控制器
│ └── views/ # 页面视图
├── pages/ # 独立页面组件
├── policy/ # 用户协议与隐私政策
├── routes/ # 路由配置
└── widgets/ # 可复用 UI 组件
3.2 分层架构图
整体架构采用 MVC + Repository 模式,数据流向清晰:
┌─────────────────────────────────────────────────┐
│ View 层 │
│ (TabOneView / TabTwoView / TabArchiveView) │
│ 负责 UI 渲染,通过 Obx 响应式绑定数据 │
├─────────────────────────────────────────────────┤
│ Controller 层 │
│ (HomeController / TabController) │
│ 负责业务逻辑,调用 Repository 读写数据 │
├─────────────────────────────────────────────────┤
│ Repository 层 │
│ (GoalRepository / PomodoroRepository) │
│ 负责数据访问,封装 SQL 操作 │
├─────────────────────────────────────────────────┤
│ Database 层 │
│ (SQLite 本地数据库) │
│ 持久化存储所有数据 │
└─────────────────────────────────────────────────┘
3.3 GetX 依赖注入绑定
模块间的依赖关系通过 GetX Binding 统一管理,确保 Controller 和 Repository 在正确的时机初始化:
dart
class HomeBinding extends Bindings {
@override
void dependencies() {
// 懒加载注入 Controller
Get.lazyPut<HomeController>(() => HomeController());
// 注入 Repository
Get.lazyPut<GoalRepository>(() => GoalRepository());
Get.lazyPut<PomodoroRepository>(() => PomodoroRepository());
}
}
这种依赖注入方式的优势是:按需加载、自动销毁,避免内存泄漏。
四、数据模型与数据库设计
4.1 核心数据模型
项目的 Goal 模型是最核心的数据结构,定义了目标的所有属性:
dart
class Goal {
int? id; // 主键,自增
String name; // 目标名称
DateTime dueDate; // 截止日期
int status; // 状态:0-进行中,1-已完成
int priority; // 优先级:0-低,1-中,2-高
bool archived; // 是否已归档
int sortIndex; // 手动排序索引
Goal({
this.id,
required this.name,
required this.dueDate,
this.status = 0,
this.priority = 1,
this.archived = false,
this.sortIndex = 0,
});
}
4.2 数据库表结构
数据库包含四张核心表,覆盖了应用的所有数据需求:
| 表名 | 用途 | 关键字段 |
|---|---|---|
| goals | 目标表 | id, name, dueDate, status, priority, archived, sortIndex |
| checkins | 签到记录表 | id, goalId, checkinDate |
| pomodoro_sessions | 番茄钟会话表 | id, startTime, endTime, duration, isFocus |
| settings | 设置表 | key, value |
4.3 数据库初始化与版本管理
数据库初始化在 DatabaseService 中完成,支持从 v1 到 v5 的多版本平滑升级:
dart
class DatabaseService {
static const int _databaseVersion = 5;
Future<Database> initDatabase() async {
final dbPath = await getDatabasesPath();
return openDatabase(
join(dbPath, 'xmb_tracker.db'),
version: _databaseVersion,
onCreate: _onCreate,
onUpgrade: _onUpgrade,
);
}
// 首次安装时创建所有表
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE goals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dueDate TEXT NOT NULL,
status INTEGER DEFAULT 0,
priority INTEGER DEFAULT 1,
archived INTEGER DEFAULT 0,
sortIndex INTEGER DEFAULT 0
)
''');
await db.execute('''
CREATE TABLE pomodoro_sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
startTime TEXT NOT NULL,
endTime TEXT,
duration INTEGER DEFAULT 0,
isFocus INTEGER DEFAULT 1
)
''');
// ... 其他表创建
}
}
4.4 多版本升级策略
随着功能迭代,数据库结构也在不断演进。通过 onUpgrade 回调逐步执行 ALTER TABLE 语句,确保老用户升级时数据不丢失:
dart
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
// v2: 新增签到表
await db.execute('CREATE TABLE checkins (...)');
}
if (oldVersion < 3) {
// v3: 新增番茄钟会话表
await db.execute('CREATE TABLE pomodoro_sessions (...)');
}
if (oldVersion < 4) {
// v4: 新增设置表
await db.execute('CREATE TABLE settings (...)');
}
if (oldVersion < 5) {
// v5: goals 表增加新字段
await db.execute('ALTER TABLE goals ADD COLUMN priority INTEGER DEFAULT 1');
await db.execute('ALTER TABLE goals ADD COLUMN archived INTEGER DEFAULT 0');
await db.execute('ALTER TABLE goals ADD COLUMN sortIndex INTEGER DEFAULT 0');
}
}
💡 这种渐进式升级策略是移动端数据库设计的最佳实践 ,避免了全量重建表带来的数据丢失风险。详见 SQLite ALTER TABLE 文档。
五、Repository 数据访问层
5.1 GoalRepository 实现
Repository Pattern 是项目数据访问的核心模式,将所有 SQL 操作封装在 Repository 类中:
dart
class GoalRepository {
final Database _db;
GoalRepository(this._db);
// 创建目标
Future<int> insertGoal(Goal goal) async {
return await _db.insert('goals', goal.toMap());
}
// 查询所有未归档目标
Future<List<Goal>> getActiveGoals() async {
final maps = await _db.query(
'goals',
where: 'archived = ?',
whereArgs: [0],
orderBy: 'sortIndex ASC',
);
return maps.map((map) => Goal.fromMap(map)).toList();
}
// 更新目标状态
Future<void> updateGoalStatus(int id, int status) async {
await _db.update(
'goals',
{'status': status},
where: 'id = ?',
whereArgs: [id],
);
}
// 归档已完成目标
Future<void> archiveCompletedGoals() async {
await _db.update(
'goals',
{'archived': 1},
where: 'status = ? AND archived = ?',
whereArgs: [1, 0],
);
}
}
5.2 为什么要用 Repository 模式?
直接在 Controller 中写 SQL 虽然更快,但存在以下问题:
- 代码复用困难:多个 Controller 需要相同查询时,SQL 会重复
- 单元测试困难:Controller 与数据库耦合,无法独立测试
- 维护成本高:表结构变更时需要修改所有引用处
Repository 模式的优势:
- Controller 只调用
repository.getGoals()方法,不关心 SQL 细节 - 表结构变更只需修改 Repository 内部实现
- 可以轻松替换数据源(如从 SQLite 切换到云端 API)
六、GetX 响应式状态管理
6.1 Controller 中的响应式变量
HomeController 是应用最核心的控制器,管理目标列表和排序逻辑:
dart
class HomeController extends GetxController {
final GoalRepository _repository = Get.find<GoalRepository>();
// 响应式变量声明
var goals = <Goal>[].obs;
var completedGoals = <Goal>[].obs;
var inProgressCount = 0.obs;
var completedCount = 0.obs;
var currentSortMode = SortMode.time.obs;
@override
void onInit() {
super.onInit();
loadGoals();
}
// 加载目标数据
Future<void> loadGoals() async {
final allGoals = await _repository.getActiveGoals();
goals.value = allGoals.where((g) => g.status == 0).toList();
completedGoals.value = allGoals.where((g) => g.status == 1).toList();
inProgressCount.value = goals.length;
completedCount.value = completedGoals.length;
}
// 切换排序模式
void changeSortMode(SortMode mode) {
currentSortMode.value = mode;
_sortGoals();
}
}
6.2 View 中的响应式绑定
在 View 层,使用 Obx 组件包裹需要动态更新的 UI 部分:
dart
class TabOneView extends GetView<HomeController> {
@override
Widget build(BuildContext context) {
return Column(
children: [
// 统计面板 - 响应式更新
Obx(() => Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatCard('进行中', controller.inProgressCount.value),
_buildStatCard('已完成', controller.completedCount.value),
],
)),
// 目标列表 - 响应式更新
Obx(() => ListView.builder(
itemCount: controller.goals.length,
itemBuilder: (context, index) {
return _buildGoalCard(controller.goals[index]);
},
)),
],
);
}
}
🔑 关键点 :
Obx只会重建其内部的 Widget,不影响外部组件。这种精细化的响应式更新机制,是 GetX 性能优于setState()的核心原因。
七、番茄钟功能实现
7.1 番茄钟计时器核心逻辑

番茄钟采用经典的 25 分钟专注 + 5 分钟休息 模式,支持自定义参数:
dart
class PomodoroController extends GetxController {
// 计时器状态
var isRunning = false.obs;
var isFocusMode = true.obs;
var remainingSeconds = (25 * 60).obs;
var totalFocusSeconds = 0.obs;
var completedRounds = 0.obs;
// 用户配置
var focusDuration = 25.obs; // 专注时长(分钟)
var restDuration = 5.obs; // 休息时长(分钟)
var dailyTarget = 4.obs; // 每日目标轮次
Timer? _timer;
void startTimer() {
isRunning.value = true;
_timer = Timer.periodic(Duration(seconds: 1), (_) {
if (remainingSeconds.value > 0) {
remainingSeconds.value--;
if (isFocusMode.value) {
totalFocusSeconds.value++;
}
} else {
_onTimerComplete();
}
});
}
void pauseTimer() {
isRunning.value = false;
_timer?.cancel();
}
void resetTimer() {
pauseTimer();
remainingSeconds.value = isFocusMode.value
? focusDuration.value * 60
: restDuration.value * 60;
}
void _onTimerComplete() {
pauseTimer();
if (isFocusMode.value) {
// 专注结束 → 切换到休息模式
completedRounds.value++;
isFocusMode.value = false;
remainingSeconds.value = restDuration.value * 60;
} else {
// 休息结束 → 切换到专注模式
isFocusMode.value = true;
remainingSeconds.value = focusDuration.value * 60;
}
}
}
7.2 番茄钟数据持久化
每次专注结束后,会话数据通过 PomodoroRepository 保存到数据库:
dart
class PomodoroRepository {
final Database _db;
PomodoroRepository(this._db);
// 保存番茄钟会话
Future<int> insertSession(PomodoroSession session) async {
return await _db.insert('pomodoro_sessions', session.toMap());
}
// 查询今日专注总时长(秒)
Future<int> getTodayFocusSeconds() async {
final today = DateTime.now().toIso8601String().substring(0, 10);
final result = await _db.rawQuery('''
SELECT SUM(duration) as total
FROM pomodoro_sessions
WHERE isFocus = 1 AND startTime LIKE ?
''', ['$today%']);
return result.first['total'] as int? ?? 0;
}
// 查询今日完成轮次
Future<int> getTodayCompletedRounds() async {
final today = DateTime.now().toIso8601String().substring(0, 10);
final result = await _db.rawQuery('''
SELECT COUNT(*) as count
FROM pomodoro_sessions
WHERE isFocus = 1 AND startTime LIKE ?
''', ['$today%']);
return result.first['count'] as int? ?? 0;
}
}
八、CustomPainter 环形进度条
8.1 绘制原理

番茄钟的圆环进度条是整个应用最亮眼的 UI 元素,使用 Flutter 的 CustomPainter 实现。核心绘制流程:
- 绘制灰色背景圆环(底层)
- 根据进度绘制紫色渐变弧线(顶层)
- 圆环端点添加圆角处理
- 中心显示倒计时文字
dart
class PomodoroRingPainter extends CustomPainter {
final double progress; // 进度 0.0 ~ 1.0
final Color backgroundColor; // 背景环颜色
final Color progressColor; // 进度环颜色
final double strokeWidth; // 环宽
PomodoroRingPainter({
required this.progress,
this.backgroundColor = const Color(0xFFE0E0E0),
this.progressColor = const Color(0xFF5866F2),
this.strokeWidth = 12.0,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = (size.width - strokeWidth) / 2;
// 1. 绘制灰色背景圆环
final bgPaint = Paint()
..color = backgroundColor
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round;
canvas.drawCircle(center, radius, bgPaint);
// 2. 绘制紫色渐变进度弧
final progressPaint = Paint()
..shader = SweepGradient(
startAngle: -math.pi / 2,
endAngle: math.pi * 1.5,
colors: [Color(0xFF5866F2), Color(0xFF7C4DFF)],
).createShader(Rect.fromCircle(center: center, radius: radius))
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round;
final sweepAngle = 2 * math.pi * progress;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
-math.pi / 2, // 从12点方向开始
sweepAngle,
false,
progressPaint,
);
}
@override
bool shouldRepaint(covariant PomodoroRingPainter oldDelegate) {
return oldDelegate.progress != progress;
}
}
8.2 在 Widget 中使用
将 CustomPainter 嵌入到计时器 UI 中,实时驱动进度变化:
dart
Obx(() => CustomPaint(
size: Size(200, 200),
painter: PomodoroRingPainter(
progress: controller.remainingSeconds.value /
(controller.isFocusMode.value
? controller.focusDuration.value * 60
: controller.restDuration.value * 60),
progressColor: controller.isFocusMode.value
? Color(0xFF5866F2) // 专注模式:紫色
: Color(0xFF4CAF50), // 休息模式:绿色
),
))
🎨 设计细节 :紫色渐变配色方案(
#5866F2 → #7C4DFF)营造出专注、沉静的视觉氛围,配合SweepGradient实现顺时针渐变效果。
九、归档系统实现
9.1 归档与恢复逻辑

归档系统让用户可以将已完成的目标从主列表移除,但保留历史记录:
dart
// 归档所有已完成目标
Future<void> archiveCompletedGoals() async {
await _repository.archiveCompletedGoals();
// 刷新列表
await loadGoals();
Get.snackbar('归档成功', '已归档 ${completedGoals.length} 个目标');
}
// 从归档恢复单个目标
Future<void> restoreGoal(int id) async {
await _repository.updateGoalArchived(id, false);
await loadArchivedGoals();
Get.snackbar('恢复成功', '目标已恢复到进行中列表');
}
9.2 按日期分组展示
归档页面按日期分组展示已归档目标,使用 groupby 进行数据分组:
dart
// 按归档日期分组
final groupedGoals = groupBy(
archivedGoals,
(Goal goal) => goal.archivedDate.substring(0, 10), // 取日期部分
);
// 分组结果示例:
// {
// "2024-01-15": [Goal A, Goal B],
// "2024-01-14": [Goal C],
// }
每个分组头部显示日期,下方展示该日期归档的所有目标,用户可以批量选择并恢复。
十、隐私合规与用户协议
10.1 首次启动协议展示
应用在首次启动时强制展示用户协议 和隐私政策,符合国内应用上架规范:
dart
class PolicyService {
static const String _acceptedKey = 'policy_accepted';
// 检查是否已接受协议
bool isPolicyAccepted() {
final settings = Get.find<SettingsRepository>();
return settings.getBool(_acceptedKey) ?? false;
}
// 标记已接受协议
Future<void> acceptPolicy() async {
final settings = Get.find<SettingsRepository>();
await settings.setBool(_acceptedKey, true);
}
}
10.2 隐私保护设计
项目的隐私保护体现在以下方面:
- 无网络请求:应用不包含任何 HTTP 请求代码,数据不会离开设备
- 无第三方 SDK:不集成广告 SDK、统计 SDK 等可能收集隐私的组件
- 本地存储:所有数据存储在 SQLite 数据库中,用户可随时清除
- 协议透明:隐私政策明确说明数据收集范围(实际上为零)
🔒 隐私承诺:XMB Tracker 不收集、不传输、不分享任何用户数据,这是一个真正的"设备端应用"。
十一、快速上手与运行指南
11.1 环境要求
运行 XMB Tracker 需要以下环境:
- 安装 Flutter SDK(Dart >=2.19.6, ❤️.0.0)
- 安装 VS Code + Flutter 插件
- 准备 HarmonyOS 模拟器/真机
11.2 运行项目
bash
# 克隆项目
git clone <repo-url>
cd xmbtracker
# 安装依赖
flutter pub get
# 运行应用(连接设备或模拟器后)
flutter run
11.3 构建发布包
bash
# 构建 Android APK
flutter build apk --release
# 构建 iOS(需要 macOS + Xcode)
flutter build ios --release
# 构建鸿蒙 HAP(需要 DevEco Studio)
flutter build hap --release
📱 提示 :鸿蒙平台需要使用定制版的依赖包(sqflite、path_provider 等),这些已在
pubspec.yaml中配置好,flutter pub get会自动拉取。
十二、未来规划与拓展方向
XMB Tracker 目前已经具备了完善的核心功能,但仍有一些值得探索的方向:
- 云同步功能:在保持离线优先的基础上,增加可选的云端备份与同步能力
- 智能提醒通知:目标到期提醒、定时专注提醒,支持本地推送
- 数据分析看板:更丰富的统计图表,如专注趋势曲线、目标完成率饼图
- 数据导入导出:支持导出 CSV/JSON 格式的使用数据,方便备份迁移
- 桌面小组件:Android/iOS 桌面小组件(Widget)快速查看今日目标和专注数据
- 协作功能:支持目标分享和团队协作(需要后端服务)
总结
XMB Tracker 是一个设计精良的 Flutter 实战项目,它展示了如何用现代 Flutter 技术栈构建一个功能完整、架构清晰的移动应用。项目的核心价值在于:
- 实用性强:目标管理 + 番茄钟的组合,覆盖了日常效率工具的核心需求
- 架构规范:GetX + Repository Pattern 的分层设计,代码可读性和可维护性优秀
- 技术全面:涵盖了状态管理、本地数据库、自定义绘制、动画、隐私合规等常见开发场景
- 跨平台 :一套代码运行在 Android、iOS 和 HarmonyOS 上
无论你是 Flutter 初学者想要学习项目架构,还是有经验的开发者想要参考最佳实践,XMB Tracker 都是一个值得深入研究的项目。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源: