鸿蒙结合Flutter 实战:XMB Tracker

目录

    • 前言
    • 一、项目概览与功能总览
      • [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 设计理念

应用的核心设计理念围绕四个关键词展开:

  1. 离线优先 :所有数据存储在本地 SQLite 数据库,无需服务器,保护用户隐私
  2. 中文友好:完整的中文界面和文档,降低使用门槛
  3. 轻量聚焦:只做目标管理和专注计时,不堆砌多余功能
  4. 高定制性:番茄钟参数、目标排序方式均可自定义

🎯 项目定位:面向注重隐私和效率的个人用户,提供一个无广告、无网络依赖的纯净生产力工具。


二、技术选型与依赖分析

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 生态中,状态管理方案众多(ProviderRiverpodBloc 等),选择 GetX 基于以下考量:

yaml 复制代码
# pubspec.yaml 核心依赖
dependencies:
  get: ^4.6.6
  sqflite:  # 鸿蒙定制版本
  flutter_svg: ^2.0.9
  simple_animations: ^5.0.2

GetX 的三大优势让它非常适合中小型项目:

  1. 极简 API.obs 声明响应式变量,Obx() 包裹 UI 即可自动刷新,无需样板代码
  2. 全家桶方案:内置状态管理、路由管理、依赖注入,一个库解决三个问题
  3. 高性能 :只刷新绑定的 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 虽然更快,但存在以下问题:

  1. 代码复用困难:多个 Controller 需要相同查询时,SQL 会重复
  2. 单元测试困难:Controller 与数据库耦合,无法独立测试
  3. 维护成本高:表结构变更时需要修改所有引用处

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 实现。核心绘制流程:

  1. 绘制灰色背景圆环(底层)
  2. 根据进度绘制紫色渐变弧线(顶层)
  3. 圆环端点添加圆角处理
  4. 中心显示倒计时文字
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 隐私保护设计

项目的隐私保护体现在以下方面:

  1. 无网络请求:应用不包含任何 HTTP 请求代码,数据不会离开设备
  2. 无第三方 SDK:不集成广告 SDK、统计 SDK 等可能收集隐私的组件
  3. 本地存储:所有数据存储在 SQLite 数据库中,用户可随时清除
  4. 协议透明:隐私政策明确说明数据收集范围(实际上为零)

🔒 隐私承诺:XMB Tracker 不收集、不传输、不分享任何用户数据,这是一个真正的"设备端应用"。


十一、快速上手与运行指南

11.1 环境要求

运行 XMB Tracker 需要以下环境:

  1. 安装 Flutter SDK(Dart >=2.19.6, ❤️.0.0)
  2. 安装 VS Code + Flutter 插件
  3. 准备 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 目前已经具备了完善的核心功能,但仍有一些值得探索的方向:

  1. 云同步功能:在保持离线优先的基础上,增加可选的云端备份与同步能力
  2. 智能提醒通知:目标到期提醒、定时专注提醒,支持本地推送
  3. 数据分析看板:更丰富的统计图表,如专注趋势曲线、目标完成率饼图
  4. 数据导入导出:支持导出 CSV/JSON 格式的使用数据,方便备份迁移
  5. 桌面小组件:Android/iOS 桌面小组件(Widget)快速查看今日目标和专注数据
  6. 协作功能:支持目标分享和团队协作(需要后端服务)

总结

XMB Tracker 是一个设计精良的 Flutter 实战项目,它展示了如何用现代 Flutter 技术栈构建一个功能完整、架构清晰的移动应用。项目的核心价值在于:

  • 实用性强:目标管理 + 番茄钟的组合,覆盖了日常效率工具的核心需求
  • 架构规范:GetX + Repository Pattern 的分层设计,代码可读性和可维护性优秀
  • 技术全面:涵盖了状态管理、本地数据库、自定义绘制、动画、隐私合规等常见开发场景
  • 跨平台 :一套代码运行在 Android、iOS 和 HarmonyOS

无论你是 Flutter 初学者想要学习项目架构,还是有经验的开发者想要参考最佳实践,XMB Tracker 都是一个值得深入研究的项目。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

相关推荐
前端不太难2 小时前
鸿蒙 PC:从“用户点击”到“AI 调度”
人工智能·华为·harmonyos
大雷神3 小时前
HarmonyOS APP<<古今职鉴定>>开源教程第26篇:【完整案例】职业性格测试开发
harmonyos·arkts·鸿蒙·古今职鉴
ai安歌3 小时前
鸿蒙PC:鸿蒙electron跨端框架PC链接雷达实战:把本地收藏夹升级成可巡检的链接管理面板
华为·electron·harmonyos
ai安歌3 小时前
鸿蒙PC:Qt适配OpenHarmony实战【昼刻】:用 Qt Quick 做一个可运行的鸿蒙时钟应用
qt·华为·harmonyos
想你依然心痛3 小时前
HarmonyOS 6 悬浮导航 + 沉浸光感:打造鸿蒙智能体驱动的沉浸式音乐创作协作工坊
华为·ar·harmonyos·智能体
lqj_本人4 小时前
鸿蒙electron跨端框架PC复盘手账实战:把一天的判断、评分和明日计划收成结构化记录
华为·harmonyos
ai安歌4 小时前
鸿蒙PC:鸿蒙 electron :模板装配台,把可复用内容拆成模块、变量和发布检查
华为·electron·harmonyos
lqj_本人13 小时前
鸿蒙electron跨端框架PC今日打卡实战:频率、连续天数和今日进度怎么放进桌面工具
华为·harmonyos