运动步数追踪器应用


欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

适配的第三方库地址:

  • location: https://pub.dev/packages/location
  • shared_preferences: https://pub.dev/packages/shared_preferences
  • flutter_local_notifications: https://pub.dev/packages/flutter_local_notifications
  • wakelock: https://pub.dev/packages/wakelock

一、项目概述

运行效果图

image-20260412100759896

image-20260412100808539

image-20260412100814774

image-20260412100818831

image-20260412100830417

1.1 应用简介

运动步数追踪器是一款专注于健康管理的运动记录应用,旨在帮助用户养成每日运动的好习惯。应用通过智能算法记录用户每日步数,精确计算消耗卡路里,实时展示运动轨迹,并提供个性化的目标设定和成就激励系统。

应用以活力绿色为主色调,象征健康与生机。涵盖运动主页、实时追踪、历史记录、成就系统四大核心模块。用户可以设定个人运动目标,追踪每日运动数据,查看历史运动记录,解锁各类成就徽章,在运动中收获健康与快乐。

1.2 核心功能

功能模块 功能描述 实现方式
步数记录 实时记录用户行走步数 传感器/模拟数据
卡路里计算 根据步数计算消耗热量 算法计算
轨迹追踪 展示用户运动路线 GPS定位
目标设定 自定义每日运动目标 本地存储
成就系统 解锁运动成就徽章 成就引擎
历史记录 查看过往运动数据 数据持久化
数据统计 周/月运动数据汇总 图表展示
提醒通知 运动目标提醒推送 本地通知

1.3 运动类型定义

序号 运动类型 Emoji 描述 卡路里消耗/步
1 步行 🚶 日常步行运动 0.04 千卡
2 慢跑 🏃 中等强度跑步 0.08 千卡
3 快走 🚀 高速步行 0.06 千卡
4 徒步 🥾 户外登山徒步 0.07 千卡

1.4 目标等级定义

序号 目标名称 Emoji 步数目标 卡路里目标 距离目标
1 入门级 🌱 3,000步 120千卡 2公里
2 标准级 🌿 6,000步 240千卡 4公里
3 进阶级 🌳 10,000步 400千卡 7公里
4 挑战级 🏔️ 15,000步 600千卡 10公里
5 专家级 🏆 20,000步 800千卡 14公里

1.5 成就类型定义

序号 成就名称 Emoji 描述 积分
1 初出茅庐 🏃 完成第一次运动记录 10
2 目标达成 🎯 单日步数达到目标 50
3 坚持不懈 🔥 连续7天完成目标 100
4 马拉松达人 🏅 累计步行42.195公里 200
5 燃脂先锋 💪 单日消耗500卡路里 150
6 早起鸟儿 🌅 早上6点前开始运动 30
7 夜猫子 🌙 晚上10点后仍在运动 30
8 速度之星 1小时内步行5公里 80

1.6 数据统计周期

序号 统计周期 Emoji 数据范围 展示形式
1 今日 📅 当日数据 实时更新
2 本周 📊 近7天数据 柱状图
3 本月 📈 近30天数据 折线图
4 全部 🏆 累计数据 汇总卡片

1.7 技术栈

技术领域 技术选型 版本要求
开发框架 Flutter >= 3.0.0
编程语言 Dart >= 2.17.0
设计规范 Material Design 3 -
状态管理 StatefulWidget -
数据存储 SharedPreferences >= 2.0.0
定位服务 Location >= 4.0.0
本地通知 FlutterLocalNotifications >= 15.0.0
屏幕唤醒 Wakelock >= 0.6.0
目标平台 鸿蒙OS / Web API 21+

1.8 项目结构

lib/
└── main_fitness_tracker.dart
    ├── FitnessTrackerApp              # 应用入口
    ├── AchievementType                # 成就类型枚举
    ├── DailyGoal                      # 每日目标模型
    ├── WorkoutRecord                  # 运动记录模型
    ├── Achievement                    # 成就模型
    ├── UserStats                      # 用户统计模型
    ├── FitnessTrackerHomePage         # 主页面(底部导航)
    ├── _buildHomePage                 # 主页
    ├── _buildTrackingPage             # 运动追踪页
    ├── _buildHistoryPage              # 历史记录页
    ├── _buildAchievementsPage         # 成就页
    ├── WeeklyChartPainter             # 周统计图表绘制器
    └── RoutePainter                   # 轨迹绘制器

二、系统架构

2.1 整体架构图

Data Layer

Business Layer

Presentation Layer

主页面
FitnessTrackerHomePage

主页

运动追踪页

历史记录页

成就页

目标进度

今日统计

周统计图表

实时追踪

轨迹地图

运动控制

记录列表

记录详情

成就列表

积分统计

运动引擎
FitnessEngine

定位管理器
LocationManager

成就系统
AchievementSystem

通知管理器
NotificationManager

WorkoutRecord
运动记录

DailyGoal
每日目标

Achievement
成就

UserStats
用户统计

2.2 类图设计

unlocks

has

targets

FitnessTrackerApp

+Widget build()

«enumeration»

AchievementType

+String label

+String emoji

+String description

+int points

+firstStep()

+dailyGoal()

+weeklyStreak()

+marathonRunner()

+calorieBurner()

+earlyBird()

+nightOwl()

+speedster()

DailyGoal

+int stepTarget

+double calorieTarget

+double distanceTarget

+DateTime createdAt

+copyWith()

WorkoutRecord

+String id

+DateTime startTime

+DateTime endTime

+int steps

+double distance

+double calories

+List<Offset> route

+double avgSpeed

Achievement

+AchievementType type

+bool isUnlocked

+DateTime unlockedAt

+copyWith()

UserStats

+int totalSteps

+double totalDistance

+double totalCalories

+int totalWorkouts

+int currentStreak

+int longestStreak

+copyWith()

2.3 页面导航流程

主页

运动

记录

成就

应用启动

主页

底部导航

查看今日数据

设置目标

调整目标参数

运动追踪页

开始运动

实时记录数据

结束运动?

保存记录

历史记录页

查看记录列表

查看记录详情

成就页

查看成就列表

查看成就详情

2.4 运动追踪流程

定位管理器 运动引擎 主页 用户 定位管理器 运动引擎 主页 用户 loop [运动过程] 点击开始运动 初始化运动 启动定位服务 定位就绪 更新位置 计算步数/距离/卡路里 更新显示数据 点击结束运动 停止运动 生成运动记录 检查成就解锁 显示运动结果

三、核心模块设计

3.1 数据模型设计

3.1.1 成就类型枚举 (AchievementType)
enum AchievementType {
  firstStep('初出茅庐', '🏃', '完成第一次运动记录', 10),
  dailyGoal('目标达成', '🎯', '单日步数达到目标', 50),
  weeklyStreak('坚持不懈', '🔥', '连续7天完成目标', 100),
  marathonRunner('马拉松达人', '🏅', '累计步行42.195公里', 200),
  calorieBurner('燃脂先锋', '💪', '单日消耗500卡路里', 150),
  earlyBird('早起鸟儿', '🌅', '早上6点前开始运动', 30),
  nightOwl('夜猫子', '🌙', '晚上10点后仍在运动', 30),
  speedster('速度之星', '⚡', '1小时内步行5公里', 80);

  final String label;
  final String emoji;
  final String description;
  final int points;
  const AchievementType(this.label, this.emoji, this.description, this.points);
}
3.1.2 每日目标模型 (DailyGoal)
class DailyGoal {
  final int stepTarget;
  final double calorieTarget;
  final double distanceTarget;
  final DateTime createdAt;

  const DailyGoal({
    required this.stepTarget,
    required this.calorieTarget,
    required this.distanceTarget,
    required this.createdAt,
  });

  DailyGoal copyWith({
    int? stepTarget,
    double? calorieTarget,
    double? distanceTarget,
  }) {
    return DailyGoal(
      stepTarget: stepTarget ?? this.stepTarget,
      calorieTarget: calorieTarget ?? this.calorieTarget,
      distanceTarget: distanceTarget ?? this.distanceTarget,
      createdAt: createdAt,
    );
  }
}
3.1.3 运动记录模型 (WorkoutRecord)
class WorkoutRecord {
  final String id;
  final DateTime startTime;
  final DateTime endTime;
  final int steps;
  final double distance;
  final double calories;
  final List<Offset> route;
  final double avgSpeed;

  const WorkoutRecord({
    required this.id,
    required this.startTime,
    required this.endTime,
    required this.steps,
    required this.distance,
    required this.calories,
    required this.route,
    required this.avgSpeed,
  });
}
3.1.4 成就分布统计
63% 38% 成就解锁分布示例 已解锁 未解锁

3.2 页面结构设计

3.2.1 主页面布局

FitnessTrackerHomePage

IndexedStack

主页

运动追踪页

历史记录页

成就页

NavigationBar

主页 Tab

运动 Tab

记录 Tab

成就 Tab

3.2.2 主页结构

主页

SliverAppBar

目标进度卡片

今日统计卡片

累计统计卡片

周统计图表

步数进度

卡路里进度

距离进度

步数统计

卡路里统计

距离统计

3.2.3 运动追踪页结构

运动追踪页

实时统计

轨迹地图

运动控制

当前步数

当前距离

当前卡路里

运动时长

轨迹绘制

起点标记

终点标记

开始/结束按钮

音乐控制

语音播报

3.2.4 历史记录页结构

历史记录页

本周概览

记录列表

记录详情

运动次数

总步数

总里程

总热量

日期时间

运动时长

运动数据

3.3 运动引擎逻辑

初始化运动

启动定位

开始计时

获取位置更新

计算步数

计算距离

计算卡路里

更新轨迹

运动结束?

停止定位

生成记录

检查成就

保存数据

显示结果

3.4 成就检测逻辑

运动结束

获取运动数据

首次运动?

解锁初出茅庐

达到目标?

解锁目标达成

连续打卡?

解锁坚持不懈

累计达标?

解锁马拉松达人

检查其他成就

更新积分


四、UI设计规范

4.1 配色方案

应用以活力绿色为主色调,象征健康与生机:

颜色类型 色值 用途
主色 #4CAF50 (Green) 导航、主题元素
辅助色 #81C784 卡片背景
第三色 #A5D6A7 进度条背景
强调色 #C8E6C9 成就页面
背景色 #FAFAFA 页面背景
卡片背景 #FFFFFF 信息卡片
步数色 #4CAF50 步数统计
卡路里色 #FF9800 卡路里统计
距离色 #2196F3 距离统计

4.2 目标等级配色

等级 色值 视觉效果
入门级 #4CAF50 绿色
标准级 #8BC34A 浅绿
进阶级 #CDDC39 黄绿
挑战级 #FFC107 黄色
专家级 #FF9800 橙色

4.3 字体规范

元素 字号 字重 颜色
页面标题 24px Bold 主色
步数显示 36px Bold #FFFFFF
统计数值 20px Bold #000000
卡片标题 18px Bold #000000
提示文字 14px Regular #666666
标签文字 12px Regular #999999

4.4 组件规范

4.4.1 目标进度卡片
┌─────────────────────────────────────┐
│  今日目标进度              [设置]   │
│                                     │
│  🚶 步数        8,500 / 10,000 步   │
│  ═════════════●═══════════════     │
│  85%                               │
│                                     │
│  🔥 卡路里      340 / 400 千卡      │
│  ════════════════●═══════════      │
│  85%                               │
│                                     │
│  🗺️ 距离        5.8 / 7.0 公里      │
│  ═══════════════●════════════      │
│  83%                               │
└─────────────────────────────────────┘
4.4.2 今日统计卡片
┌─────────────────────────────────────┐
│  今日运动数据                         │
│                                     │
│    🚶          🔥          🗺️      │
│  8,500        340         5.8      │
│    步         千卡         公里      │
└─────────────────────────────────────┘
4.4.3 运动追踪界面
┌─────────────────────────────────────┐
│  🚶 8,500步  🗺️ 5.8km  🔥 340千卡  │
│                                     │
│  [轨迹地图区域]                       │
│  ╔═══════════════════════════╗     │
│  ║  🔵 ────────●──────── 🔴 ║     │
│  ║                           ║     │
│  ╚═══════════════════════════╝     │
│                                     │
│  ⏱️ 运动时长: 45 分钟               │
│                                     │
│  ┌─────────────────────────────┐   │
│  │     ▶️ 开始运动              │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘
4.4.4 历史记录卡片
┌─────────────────────────────────────┐
│  🚶 4月12日              45分钟     │
│      07:30                          │
│  ─────────────────────────────────  │
│    6,500步       4.5公里    260千卡 │
└─────────────────────────────────────┘
4.4.5 成就卡片
┌─────────────────────────────────────┐
│  成就列表                             │
│                                     │
│  ┌─────────────┐ ┌─────────────┐   │
│  │     🏃      │ │     🎯      │   │
│  │  初出茅庐   │ │  目标达成   │   │
│  │ 完成第一次  │ │ 单日步数达  │   │
│  │ 运动记录    │ │ 到目标      │   │
│  │  ✅ 已解锁  │ │  ✅ 已解锁  │   │
│  │  10 积分    │ │  50 积分    │   │
│  └─────────────┘ └─────────────┘   │
└─────────────────────────────────────┘

五、核心功能实现

5.1 运动引擎实现

class FitnessEngine {
  WorkoutRecord createWorkoutRecord(
    DateTime startTime,
    DateTime endTime,
    int steps,
    List<Offset> route,
  ) {
    final distance = _calculateDistance(route);
    final calories = _calculateCalories(steps);
    final avgSpeed = _calculateAvgSpeed(distance, startTime, endTime);

    return WorkoutRecord(
      id: 'workout_${DateTime.now().millisecondsSinceEpoch}',
      startTime: startTime,
      endTime: endTime,
      steps: steps,
      distance: distance,
      calories: calories,
      route: route,
      avgSpeed: avgSpeed,
    );
  }

  double _calculateDistance(List<Offset> route) {
    double distance = 0;
    for (int i = 1; i < route.length; i++) {
      distance += _calculatePointDistance(route[i - 1], route[i]);
    }
    return distance;
  }

  double _calculateCalories(int steps) {
    return steps * 0.04;
  }

  double _calculateAvgSpeed(double distance, DateTime start, DateTime end) {
    final hours = end.difference(start).inMinutes / 60;
    return hours > 0 ? distance / hours : 0;
  }

  double _calculatePointDistance(Offset a, Offset b) {
    return sqrt(pow(a.dx - b.dx, 2) + pow(a.dy - b.dy, 2));
  }
}

5.2 定位管理器实现

class LocationManager {
  StreamSubscription<LocationData>? _locationSubscription;
  final List<Offset> _route = [];

  Future<void> startTracking(Function(Offset) onLocationUpdate) async {
    bool serviceEnabled = await location.serviceEnabled();
    if (!serviceEnabled) {
      serviceEnabled = await location.requestService();
      if (!serviceEnabled) return;
    }

    PermissionStatus permission = await location.hasPermission();
    if (permission == PermissionStatus.denied) {
      permission = await location.requestPermission();
      if (permission != PermissionStatus.granted) return;
    }

    _locationSubscription = location.onLocationChanged.listen((data) {
      final point = Offset(data.latitude ?? 0, data.longitude ?? 0);
      _route.add(point);
      onLocationUpdate(point);
    });
  }

  Future<void> stopTracking() async {
    await _locationSubscription?.cancel();
    _locationSubscription = null;
  }

  List<Offset> getRoute() => List.unmodifiable(_route);
}

5.3 成就系统实现

class AchievementSystem {
  List<Achievement> checkAchievements(
    WorkoutRecord record,
    UserStats stats,
    List<Achievement> currentAchievements,
  ) {
    final newAchievements = <Achievement>[];

    for (var achievement in currentAchievements) {
      if (!achievement.isUnlocked && _checkCondition(achievement.type, record, stats)) {
        newAchievements.add(achievement.copyWith(
          isUnlocked: true,
          unlockedAt: DateTime.now(),
        ));
      }
    }

    return newAchievements;
  }

  bool _checkCondition(AchievementType type, WorkoutRecord record, UserStats stats) {
    switch (type) {
      case AchievementType.firstStep:
        return stats.totalWorkouts == 1;
      case AchievementType.dailyGoal:
        return record.steps >= 10000;
      case AchievementType.weeklyStreak:
        return stats.currentStreak >= 7;
      case AchievementType.marathonRunner:
        return stats.totalDistance >= 42.195;
      case AchievementType.calorieBurner:
        return record.calories >= 500;
      case AchievementType.earlyBird:
        return record.startTime.hour < 6;
      case AchievementType.nightOwl:
        return record.startTime.hour >= 22;
      case AchievementType.speedster:
        return record.avgSpeed >= 5;
    }
  }
}

5.4 通知管理器实现

class NotificationManager {
  Future<void> initialize() async {
    final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
    
    const initializationSettings = InitializationSettings(
      android: AndroidInitializationSettings('@mipmap/ic_launcher'),
      iOS: DarwinInitializationSettings(),
    );

    await flutterLocalNotificationsPlugin.initialize(initializationSettings);
  }

  Future<void> showGoalReminder(int currentSteps, int targetSteps) async {
    final remaining = targetSteps - currentSteps;
    if (remaining > 0 && remaining <= 1000) {
      await flutterLocalNotificationsPlugin.show(
        0,
        '运动目标提醒',
        '距离今日目标还差 $remaining 步,加油!',
        const NotificationDetails(
          android: AndroidNotificationDetails(
            'fitness_channel',
            '运动提醒',
            importance: Importance.high,
          ),
        ),
      );
    }
  }

  Future<void> showAchievementUnlocked(Achievement achievement) async {
    await flutterLocalNotificationsPlugin.show(
      1,
      '成就解锁!',
      '恭喜解锁「${achievement.type.label}」成就!',
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'achievement_channel',
          '成就通知',
          importance: Importance.high,
        ),
      ),
    );
  }
}

5.5 数据存储实现

class StorageManager {
  Future<void> saveWorkoutRecord(WorkoutRecord record) async {
    final prefs = await SharedPreferences.getInstance();
    final records = prefs.getStringList('workout_records') ?? [];
    records.add(jsonEncode(record.toJson()));
    await prefs.setStringList('workout_records', records);
  }

  Future<List<WorkoutRecord>> loadWorkoutRecords() async {
    final prefs = await SharedPreferences.getInstance();
    final records = prefs.getStringList('workout_records') ?? [];
    return records.map((r) => WorkoutRecord.fromJson(jsonDecode(r))).toList();
  }

  Future<void> saveDailyGoal(DailyGoal goal) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('daily_goal', jsonEncode(goal.toJson()));
  }

  Future<DailyGoal> loadDailyGoal() async {
    final prefs = await SharedPreferences.getInstance();
    final goalStr = prefs.getString('daily_goal');
    if (goalStr == null) {
      return DailyGoal(
        stepTarget: 10000,
        calorieTarget: 400,
        distanceTarget: 7.0,
        createdAt: DateTime.now(),
      );
    }
    return DailyGoal.fromJson(jsonDecode(goalStr));
  }
}

六、交互设计

6.1 运动流程

定位管理器 运动引擎 主页 用户 定位管理器 运动引擎 主页 用户 loop [运动过程] 打开应用 显示今日数据 点击开始运动 初始化运动 启动定位 定位就绪 更新位置 计算数据 更新显示 点击结束运动 停止运动 生成记录 检查成就 显示结果

6.2 目标设定流程

点击设置

显示目标设置面板

调整步数目标

调整卡路里目标

调整距离目标

点击保存?

保存目标设置

取消设置

更新进度显示

关闭面板

6.3 成就解锁流程

完成特定目标

检查条件

弹出成就提示

点击查看

运动进行

达成条件

解锁成就

显示通知

查看成就


七、扩展功能规划

7.1 后续版本规划

2024-01-07 2024-01-14 2024-01-21 2024-01-28 2024-02-04 2024-02-11 2024-02-18 2024-02-25 2024-03-03 2024-03-10 2024-03-17 2024-03-24 2024-03-31 2024-04-07 基础UI框架 运动引擎开发 定位功能实现 成就系统完善 通知功能实现 数据同步功能 社交功能 运动分析报告 健康数据整合 V1.0 基础版本 V1.1 增强版本 V1.2 进阶版本 运动步数追踪器开发计划

7.2 功能扩展建议

7.2.1 社交功能

社交功能:

  • 好友排行榜
  • 运动挑战赛
  • 成就分享
  • 运动动态
7.2.2 健康数据整合

整合功能:

  • 心率监测
  • 睡眠质量
  • 体重管理
  • 饮水记录
7.2.3 智能分析

分析功能:

  • 运动趋势分析
  • 健康建议推送
  • 目标智能调整
  • 运动计划推荐

八、注意事项

8.1 开发注意事项

  1. 定位权限:确保正确处理定位权限申请和拒绝情况

  2. 电池优化:长时间定位会消耗大量电量,需优化定位频率

  3. 数据精度:步数计算需要校准,适应不同用户的步幅

  4. 隐私保护:位置数据属于敏感信息,需妥善存储和处理

  5. 后台运行:运动追踪需要支持后台运行,避免被系统终止

8.2 常见问题

问题 原因 解决方案
步数不准确 传感器精度问题 结合GPS数据校准
轨迹断点 定位信号丢失 增加信号检测和重连
后台停止 系统省电策略 申请后台运行权限
数据丢失 应用异常退出 实时保存运动数据
通知不显示 权限未授予 引导用户开启通知权限

8.3 使用技巧

🏃 运动追踪器使用技巧 🏃

运动准备

  • 确保手机电量充足
  • 开启定位服务
  • 授予应用必要权限
  • 选择合适的运动模式

运动过程

  • 保持手机稳定携带
  • 避免频繁查看手机
  • 注意运动安全
  • 合理安排运动强度

目标达成

  • 设定合理的目标
  • 坚持每日运动
  • 关注成就系统
  • 分享运动成果

九、运行说明

9.1 环境要求

环境 版本要求
Flutter SDK >= 3.0.0
Dart SDK >= 2.17.0
鸿蒙OS API 21+
Android API 21+
iOS 12.0+
Web浏览器 Chrome 90+

9.2 依赖配置

pubspec.yaml 中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.0.0
  location: ^4.0.0
  flutter_local_notifications: ^15.0.0
  wakelock: ^0.6.0

9.3 权限配置

Android (android/app/src/main/AndroidManifest.xml):

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

iOS (ios/Runner/Info.plist):

<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取您的位置以记录运动轨迹</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要获取您的位置以记录运动轨迹</string>
<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>

9.4 运行命令

# 查看可用设备
flutter devices

# 运行到Web服务器
flutter run -d web-server -t lib/main_fitness_tracker.dart --web-port 8145

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 -t lib/main_fitness_tracker.dart

# 运行到Android设备
flutter run -d android -t lib/main_fitness_tracker.dart

# 代码分析
flutter analyze lib/main_fitness_tracker.dart

十、总结

运动步数追踪器应用通过智能算法记录用户每日步数,精确计算消耗卡路里,实时展示运动轨迹,并提供个性化的目标设定和成就激励系统。应用支持步数记录、卡路里计算、轨迹追踪、目标设定、成就系统、历史记录、数据统计、提醒通知八大核心功能。

应用采用 Material Design 3 设计规范,以活力绿色为主色调,象征健康与生机。通过本应用,希望能够帮助用户养成每日运动的好习惯,在运动中收获健康与快乐。

运动步数追踪器——每一步都算数


Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐