Flutter 框架跨平台鸿蒙开发 - 健身计划应用开发文档
摘要: 健身计划应用是一款基于Flutter开发的跨平台运动管理工具,采用Material Design 3设计语言,支持鸿蒙OS。核心功能包括运动库分类管理(有氧、力量、柔韧等5类)、训练计划定制、实时计时器及数据统计。应用通过SharedPreferences实现本地数据持久化,提供初级到高级三档难度选择,并采用分层架构(表现层-业务逻辑层-数据层)确保模块化开发。项目结构清晰,包含运动模型、
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
一、项目概述
运行效果图






1.1 应用简介
健身计划应用是一款专为健身爱好者设计的综合性运动管理工具。应用整合了运动库管理、训练计划制定、运动计时器、数据统计分析等核心功能,帮助用户科学规划健身方案,追踪运动进度,实现健康生活目标。
应用采用现代化Material Design 3设计语言,界面简洁直观,操作流畅便捷。通过分类管理不同类型的运动项目,用户可以快速找到适合自己的训练内容。内置的计时器功能支持实时记录运动时长和热量消耗,数据持久化存储确保用户的运动记录不会丢失。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 首页仪表盘 | 今日统计、快速开始、最近训练 | 状态管理 + 数据聚合 |
| 运动库管理 | 分类浏览、搜索筛选、详情展示 | 列表渲染 + 过滤算法 |
| 训练计划 | 预设计划、自定义计划、进度追踪 | 数据模型 + 状态管理 |
| 运动计时器 | 倒计时、暂停继续、完成提醒 | Timer + 状态控制 |
| 数据统计 | 总览统计、周报图表、成就系统 | 数据分析 + 可视化 |
| 数据持久化 | 训练记录存储、用户偏好保存 | SharedPreferences |
1.3 运动分类体系
应用采用科学的运动分类体系,涵盖五大运动类型:
| 分类 | 英文标识 | 图标 | 颜色 | 特点 |
|---|---|---|---|---|
| 有氧运动 | cardio | directions_run | 红色 | 提高心肺功能,燃脂效果好 |
| 力量训练 | strength | fitness_center | 橙色 | 增强肌肉力量,塑造体型 |
| 柔韧性 | flexibility | self_improvement | 紫色 | 提高身体柔韧度,预防损伤 |
| 平衡训练 | balance | accessibility_new | 青色 | 增强平衡能力,改善协调性 |
| 核心训练 | core | sports_gymnastics | 蓝色 | 强化核心肌群,稳定身体 |
1.4 难度等级划分
每个运动项目都标注了难度等级,帮助用户选择合适的训练强度:
| 等级 | 英文标识 | 颜色 | 适用人群 |
|---|---|---|---|
| 初级 | easy | 绿色 | 健身新手,运动基础较弱 |
| 中级 | medium | 橙色 | 有一定基础,希望进阶 |
| 高级 | hard | 红色 | 健身达人,追求极限挑战 |
1.5 技术栈
| 技术领域 | 技术选型 | 版本要求 | 说明 |
|---|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 | 跨平台UI框架 |
| 编程语言 | Dart | >= 2.17.0 | 客户端开发语言 |
| 设计规范 | Material Design 3 | - | 现代化设计语言 |
| 状态管理 | setState | - | 简单状态管理 |
| 数据存储 | SharedPreferences | ^2.0.0 | 本地键值存储 |
| 数据序列化 | dart:convert | - | JSON编解码 |
| 目标平台 | 鸿蒙OS | API 21+ | 华为操作系统 |
1.6 项目结构
lib/
└── main_fitness.dart
├── FitnessApp # 应用入口
│
├── 枚举定义
│ ├── ExerciseCategory # 运动分类枚举
│ └── ExerciseDifficulty # 难度等级枚举
│
├── 数据模型
│ ├── Exercise # 运动项目模型
│ ├── WorkoutSession # 训练记录模型
│ └── FitnessPlan # 健身计划模型
│
└── 页面组件
├── MainPage # 主页面(底部导航)
├── HomePage # 首页
├── ExercisesPage # 运动库页面
├── PlansPage # 计划页面
├── StatisticsPage # 统计页面
├── ProfilePage # 个人中心页面
└── WorkoutPage # 训练执行页面
二、系统架构
2.1 整体架构图
2.2 类图设计
2.3 用户操作流程图
2.4 训练执行流程
三、核心模块设计
3.1 数据模型设计
3.1.1 运动项目模型 (Exercise)
class Exercise {
final String id; // 唯一标识
final String name; // 运动名称
final ExerciseCategory category; // 运动分类
final ExerciseDifficulty difficulty;// 难度等级
final int durationMinutes; // 建议时长(分钟)
final int caloriesPerMinute; // 每分钟消耗热量
final String description; // 运动说明
final List<String> steps; // 动作步骤
final String? imageUrl; // 示例图片URL
}
数据序列化方法:
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'category': category.index, // 枚举转索引
'difficulty': difficulty.index,
'durationMinutes': durationMinutes,
'caloriesPerMinute': caloriesPerMinute,
'description': description,
'steps': steps,
'imageUrl': imageUrl,
};
}
factory Exercise.fromMap(Map<String, dynamic> map) {
return Exercise(
id: map['id'],
name: map['name'],
category: ExerciseCategory.values[map['category']], // 索引转枚举
difficulty: ExerciseDifficulty.values[map['difficulty']],
durationMinutes: map['durationMinutes'],
caloriesPerMinute: map['caloriesPerMinute'],
description: map['description'],
steps: List<String>.from(map['steps']),
imageUrl: map['imageUrl'],
);
}
3.1.2 训练记录模型 (WorkoutSession)
class WorkoutSession {
final String id; // 记录ID
final DateTime date; // 训练日期
final List<Exercise> exercises; // 运动列表
final int totalDuration; // 总时长(分钟)
final int totalCalories; // 总消耗热量
final String? notes; // 训练备注
}
热量消耗计算公式:
C t o t a l = ∑ i = 1 n ( T i × R i ) C_{total} = \sum_{i=1}^{n} (T_i \times R_i) Ctotal=i=1∑n(Ti×Ri)
其中:
- C t o t a l C_{total} Ctotal 为总消耗热量
- T i T_i Ti 为第 i i i 项运动的时长
- R i R_i Ri 为第 i i i 项运动每分钟消耗热量
3.1.3 健身计划模型 (FitnessPlan)
class FitnessPlan {
final String id; // 计划ID
final String name; // 计划名称
final String description; // 计划描述
final List<Exercise> exercises; // 包含的运动
final int targetDaysPerWeek; // 每周目标天数
final int durationWeeks; // 持续周数
}
3.2 页面结构设计
3.2.1 主页面导航结构
3.2.2 首页组件结构
3.3 业务逻辑设计
3.3.1 连续打卡计算算法
int _calculateStreak(List<WorkoutSession> sessions) {
if (sessions.isEmpty) return 0;
int streak = 0;
DateTime? lastDate;
// 按日期降序排序
final sortedSessions = sessions.toList()
..sort((a, b) => b.date.compareTo(a.date));
for (final session in sortedSessions) {
// 标准化日期(只保留年月日)
final sessionDate = DateTime(
session.date.year,
session.date.month,
session.date.day
);
if (lastDate == null) {
// 第一条记录
streak = 1;
lastDate = sessionDate;
} else {
// 计算日期差
final diff = lastDate.difference(sessionDate).inDays;
if (diff == 1) {
// 连续一天
streak++;
lastDate = sessionDate;
} else if (diff > 1) {
// 中断,停止计算
break;
}
// diff == 0 表示同一天,跳过
}
}
return streak;
}
3.3.2 热量消耗实时计算
void _updateCalories() {
// 每秒更新一次
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
if (_remainingSeconds > 0) {
_remainingSeconds--;
_totalDuration++;
// 计算热量:总秒数 × 每分钟热量 / 60
final exercise = widget.exercises[_currentExerciseIndex];
_totalCalories = (_totalDuration * exercise.caloriesPerMinute / 60).round();
}
});
});
}
四、UI设计规范
4.1 配色方案
应用采用健康活力的绿色为主色调,辅以各运动类型的专属颜色:
| 颜色类型 | 色值 | 用途 |
|---|---|---|
| 主色调 | #4CAF50 | 导航栏、按钮、强调 |
| 有氧运动 | #F44336 | 红色,代表活力 |
| 力量训练 | #FF9800 | 橙色,代表力量 |
| 柔韧性 | #9C27B0 | 紫色,代表优雅 |
| 平衡训练 | #009688 | 青色,代表平衡 |
| 核心训练 | #2196F3 | 蓝色,代表稳定 |
| 初级难度 | #4CAF50 | 绿色,代表轻松 |
| 中级难度 | #FF9800 | 橙色,代表适中 |
| 高级难度 | #F44336 | 红色,代表挑战 |
4.2 字体规范
| 元素 | 字号 | 字重 | 颜色 |
|---|---|---|---|
| 页面标题 | 20px | Medium | 主色 |
| 区块标题 | 18px | Bold | #212121 |
| 运动名称 | 16px | Bold | #212121 |
| 正文内容 | 14px | Regular | #757575 |
| 辅助文字 | 12px | Regular | #9E9E9E |
| 统计数字 | 24-28px | Bold | 对应颜色 |
4.3 组件规范
4.3.1 首页布局
┌─────────────────────────────────────────────────────────────┐
│ 健身计划 [设置] │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 早上好! │ │
│ │ 今天准备好运动了吗? │ │
│ │ │ │
│ │ [▶ 开始训练] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 今日统计 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 🔥 消耗热量 │ │ ⏱ 运动时长 │ │
│ │ 128 千卡 │ │ 45 分钟 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ 快速开始 │
│ ┌─────────┐ ┌─────────┐ │
│ │🏃有氧 │ │💪力量 │ │
│ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ │
│ │🧘柔韧 │ │⚖️平衡 │ │
│ └─────────┘ └─────────┘ │
│ │
│ 最近训练 [查看全部] │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 💪 3项运动 · 30分钟 · 200千卡 今天 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.3.2 运动详情弹窗
┌─────────────────────────────────────────────────────────────┐
│ ───── │
│ │
│ ┌────────┐ │
│ │ 🏃 │ 跑步 │
│ │ │ 有氧运动 │
│ └────────┘ │
│ │
│ [⏱30分钟] [🔥300千卡] [📶初级] │
│ │
│ 运动说明 │
│ 户外或跑步机跑步,提高心肺功能 │
│ │
│ 动作步骤 │
│ ① 热身5分钟 │
│ ② 慢跑20分钟 │
│ ③ 放松5分钟 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 开始训练 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.3.3 训练执行页面
┌─────────────────────────────────────────────────────────────┐
│ ← 跑步 │
├─────────────────────────────────────────────────────────────┤
│ │
│ │
│ ┌───────────┐ │
│ │ │ │
│ │ 25:30 │ │
│ │ │ │
│ └───────────┘ │
│ │
│ │
│ [▶ 开始] │
│ │
│ 消耗: 128 千卡 │
│ │
└─────────────────────────────────────────────────────────────┘
4.3.4 统计页面布局
┌─────────────────────────────────────────────────────────────┐
│ 运动统计 │
├─────────────────────────────────────────────────────────────┤
│ 总览 │
│ ┌────────────┐ ┌────────────┐ │
│ │ 训练次数 │ │ 消耗热量 │ │
│ │ 15 次 │ │ 2500 千卡 │ │
│ └────────────┘ └────────────┘ │
│ ┌────────────┐ ┌────────────┐ │
│ │ 运动时长 │ │ 连续打卡 │ │
│ │ 450 分钟 │ │ 7 天 │ │
│ └────────────┘ └────────────┘ │
│ │
│ 本周运动 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 一 二 三 四 五 六 日 │ │
│ │ ▁ ▃ ▁ ▅ ▁ ▇ ▁ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 成就徽章 │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ ⭐ │ │ 🔥 │ │ ⚡ │ │
│ │初次训练│ │连续7天 │ │1000千卡│ │
│ └────────┘ └────────┘ └────────┘ │
└─────────────────────────────────────────────────────────────┘
五、核心功能实现
5.1 数据持久化
5.1.1 保存训练记录
Future<void> _saveWorkoutSession() async {
// 创建训练记录对象
final session = WorkoutSession(
id: DateTime.now().millisecondsSinceEpoch.toString(),
date: DateTime.now(),
exercises: widget.exercises,
totalDuration: _totalDuration ~/ 60,
totalCalories: _totalCalories,
);
// 获取SharedPreferences实例
final prefs = await SharedPreferences.getInstance();
// 读取现有记录
final sessionsJson = prefs.getString('workout_sessions') ?? '[]';
final sessions = jsonDecode(sessionsJson) as List;
// 添加新记录
sessions.add(session.toMap());
// 保存到本地
await prefs.setString('workout_sessions', jsonEncode(sessions));
}
5.1.2 加载训练记录
Future<void> _loadData() async {
final prefs = await SharedPreferences.getInstance();
// 读取JSON字符串
final sessionsJson = prefs.getString('workout_sessions') ?? '[]';
// 解析为对象列表
final sessions = (jsonDecode(sessionsJson) as List)
.map((e) => WorkoutSession.fromMap(e))
.toList();
// 计算今日统计
final today = DateTime.now();
final todaySessions = sessions.where((s) =>
s.date.year == today.year &&
s.date.month == today.month &&
s.date.day == today.day).toList();
setState(() {
_recentSessions = sessions.take(5).toList();
_todayCalories = todaySessions.fold(0, (sum, s) => sum + s.totalCalories);
_todayDuration = todaySessions.fold(0, (sum, s) => sum + s.totalDuration);
});
}
5.2 运动计时器
5.2.1 计时器控制
class _WorkoutPageState extends State<WorkoutPage> {
int _remainingSeconds = 0;
bool _isRunning = false;
Timer? _timer;
// 开始计时
void _startTimer() {
setState(() {
_isRunning = true;
});
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
if (_remainingSeconds > 0) {
_remainingSeconds--;
// 更新热量消耗
_updateCalories();
} else {
// 时间结束
_timer?.cancel();
_isRunning = false;
_showExerciseCompleteDialog();
}
});
});
}
// 暂停计时
void _pauseTimer() {
_timer?.cancel();
setState(() {
_isRunning = false;
});
}
}
5.2.2 时间格式化
String _formatTime(int seconds) {
final minutes = seconds ~/ 60;
final secs = seconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}';
}
5.3 运动筛选
5.3.1 分类筛选
List<Exercise> get _filteredExercises {
var exercises = _allExercises;
// 按分类筛选
if (_selectedCategory != null) {
exercises = exercises
.where((e) => e.category == _selectedCategory)
.toList();
}
// 按关键词搜索
if (_searchQuery.isNotEmpty) {
exercises = exercises
.where((e) => e.name.toLowerCase()
.contains(_searchQuery.toLowerCase()))
.toList();
}
return exercises;
}
5.4 统计计算
5.4.1 数据聚合
// 计算总训练次数
_totalWorkouts = sessions.length;
// 计算总消耗热量
_totalCalories = sessions.fold(0, (sum, s) => sum + s.totalCalories);
// 计算总运动时长
_totalDuration = sessions.fold(0, (sum, s) => sum + s.totalDuration);
// 计算连续打卡天数
_currentStreak = _calculateStreak(sessions);
六、预设运动数据
6.1 有氧运动
| 名称 | 难度 | 时长 | 热量/分钟 | 说明 |
|---|---|---|---|---|
| 跑步 | 初级 | 30分钟 | 10千卡 | 户外或跑步机跑步 |
| 跳绳 | 中级 | 15分钟 | 12千卡 | 高效燃脂运动 |
6.2 力量训练
| 名称 | 难度 | 时长 | 热量/分钟 | 说明 |
|---|---|---|---|---|
| 俯卧撑 | 中级 | 10分钟 | 8千卡 | 锻炼胸肌、肩膀和手臂 |
| 深蹲 | 初级 | 10分钟 | 7千卡 | 锻炼腿部和臀部肌肉 |
6.3 柔韧性训练
| 名称 | 难度 | 时长 | 热量/分钟 | 说明 |
|---|---|---|---|---|
| 瑜伽 | 初级 | 30分钟 | 4千卡 | 提高柔韧性,放松身心 |
6.4 核心训练
| 名称 | 难度 | 时长 | 热量/分钟 | 说明 |
|---|---|---|---|---|
| 平板支撑 | 中级 | 5分钟 | 5千卡 | 锻炼核心肌群 |
| 仰卧起坐 | 初级 | 10分钟 | 6千卡 | 锻炼腹部肌肉 |
6.5 平衡训练
| 名称 | 难度 | 时长 | 热量/分钟 | 说明 |
|---|---|---|---|---|
| 单腿站立 | 初级 | 5分钟 | 3千卡 | 提高平衡能力 |
七、预设训练计划
7.1 新手入门计划
| 计划属性 | 值 |
|---|---|
| 名称 | 新手入门计划 |
| 描述 | 适合刚开始健身的人群 |
| 频率 | 每周3天 |
| 周期 | 共4周 |
| 难度 | 初级 |
7.2 燃脂塑形计划
| 计划属性 | 值 |
|---|---|
| 名称 | 燃脂塑形计划 |
| 描述 | 高效燃脂,塑造完美身材 |
| 频率 | 每周4天 |
| 周期 | 共8周 |
| 难度 | 中级 |
7.3 增肌强化计划
| 计划属性 | 值 |
|---|---|
| 名称 | 增肌强化计划 |
| 描述 | 增加肌肉量,提升力量 |
| 频率 | 每周5天 |
| 周期 | 共12周 |
| 难度 | 高级 |
八、成就系统设计
8.1 成就徽章列表
| 徽章名称 | 图标 | 条件 | 颜色 |
|---|---|---|---|
| 初次训练 | ⭐ | 完成第一次训练 | 黄色 |
| 连续7天 | 🔥 | 连续打卡7天 | 橙色 |
| 消耗1000卡 | ⚡ | 累计消耗1000千卡 | 蓝色 |
| 坚持一月 | 🏆 | 连续打卡30天 | 金色 |
| 百次训练 | 💪 | 完成100次训练 | 紫色 |
| 万卡达人 | 🔥 | 累计消耗10000千卡 | 红色 |
8.2 成就解锁逻辑
Widget _buildBadge(String title, IconData icon, Color color, bool achieved) {
return Container(
decoration: BoxDecoration(
// 已解锁:显示彩色
// 未解锁:显示灰色
color: achieved ? color.withValues(alpha: 0.1) : Colors.grey.shade100,
border: Border.all(
color: achieved ? color.withValues(alpha: 0.3) : Colors.grey.shade300,
),
),
child: Icon(
icon,
color: achieved ? color : Colors.grey.shade400,
),
);
}
九、扩展功能规划
9.1 后续版本规划
9.2 功能扩展建议
9.2.1 自定义计划
- 用户自定义训练计划
- 设置训练目标和周期
- 灵活安排运动项目
9.2.2 运动提醒
- 定时提醒功能
- 智能推荐训练时间
- 防止遗忘训练
9.2.3 社交功能
- 添加健身好友
- 查看好友动态
- 排行榜竞争
9.2.4 AI智能推荐
- 根据用户数据推荐运动
- 智能调整训练强度
- 个性化健身方案
十、注意事项
10.1 开发注意事项
-
数据存储:使用SharedPreferences存储JSON字符串,注意数据大小限制
-
计时器管理:页面退出时必须取消Timer,避免内存泄漏
-
状态管理:使用setState进行简单状态管理,复杂场景考虑Provider
-
日期处理:注意时区问题,使用DateTime进行日期计算
-
热量计算:热量消耗为估算值,实际消耗因人而异
10.2 性能优化
| 优化点 | 方法 |
|---|---|
| 列表渲染 | 使用ListView.builder |
| 数据加载 | 使用FutureBuilder |
| 图片加载 | 使用cached_network_image |
| 状态更新 | 避免不必要的setState |
10.3 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 数据丢失 | 未正确保存 | 确保await异步操作 |
| 计时器异常 | 未及时取消 | 在dispose中取消 |
| 日期计算错误 | 时区问题 | 使用UTC时间 |
| 内存泄漏 | Timer未释放 | 页面退出时取消 |
十一、运行说明
11.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
11.2 依赖配置
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.0.0
11.3 运行命令
# 获取依赖
flutter pub get
# 查看可用设备
flutter devices
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_fitness.dart
# 运行到Web服务器
flutter run -d web-server -t lib/main_fitness.dart --web-port 8084
# 运行到Windows
flutter run -d windows -t lib/main_fitness.dart
# 代码分析
flutter analyze lib/main_fitness.dart
十二、总结
健身计划应用通过科学合理的功能设计、直观友好的用户界面、稳定可靠的数据存储,为健身爱好者提供了一个全面的运动管理解决方案。应用涵盖运动库管理、训练计划制定、运动计时器、数据统计分析等核心功能,帮助用户科学规划健身方案,追踪运动进度。
核心功能包括五大运动分类、三级难度体系、实时计时器、数据持久化存储、连续打卡计算、成就徽章系统等。应用采用Material Design 3设计语言,界面简洁美观,操作流畅便捷。通过SharedPreferences实现数据本地存储,确保用户的运动记录安全可靠。
通过本应用,希望能够帮助用户养成良好的运动习惯,科学规划健身方案,实现健康生活目标。
科学健身,健康生活
更多推荐


所有评论(0)