#
前言
运动分析页面帮助用户了解自己的运动习惯和效果。通过统计本周的运动数据,用户可以看到自己的运动时长、消耗热量、运动类型分布等信息,从而更好地规划运动计划。
这篇文章会讲解运动分析页面的实现,包括运动汇总卡片和运动类型分布两个核心组件。
页面整体结构
运动分析页面包含汇总数据卡片和运动类型分布两个主要部分。
dart
class ExerciseAnalysisPage extends StatelessWidget {
const ExerciseAnalysisPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFAFAFC),
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios_rounded, size: 20.w),
onPressed: () => Get.back()
),
title: Text('运动分析', style: TextStyle(fontSize: 17.sp, fontWeight: FontWeight.w600)),
centerTitle: true,
),
body: SingleChildScrollView(
padding: EdgeInsets.all(20.w),
child: Column(
children: [
_buildSummaryCard(),
SizedBox(height: 20.h),
_buildTypeStats(),
],
),
),
);
}
}
页面使用统一的浅灰色背景和透明 AppBar,和其他统计页面保持一致的视觉风格。
运动汇总卡片
汇总卡片使用青绿色渐变背景,展示本周运动时长和相关统计数据。
dart
Widget _buildSummaryCard() {
return Container(
padding: EdgeInsets.all(24.w),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF00C9A7), Color(0xFF4ECDC4)]
),
borderRadius: BorderRadius.circular(24.r),
),
child: Column(
children: [
Text('本周运动', style: TextStyle(fontSize: 13.sp, color: Colors.white70)),
SizedBox(height: 8.h),
Text('156 分钟', style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.w700,
color: Colors.white
)),
SizedBox(height: 4.h),
Text('目标 150 分钟 · 已达标', style: TextStyle(
fontSize: 13.sp,
color: Colors.white70
)),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildMiniStat('运动天数', '5天'),
_buildMiniStat('消耗热量', '1,280kcal'),
_buildMiniStat('总距离', '12.5km'),
],
),
],
),
);
}
Widget _buildMiniStat(String label, String value) {
return Column(
children: [
Text(value, style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w600,
color: Colors.white
)),
SizedBox(height: 2.h),
Text(label, style: TextStyle(fontSize: 11.sp, color: Colors.white60)),
],
);
}
渐变色从 #00C9A7 到 #4ECDC4,是一个清新的青绿色系,和运动主题非常契合。本周运动时长使用 36sp 的大字号居中显示,下方显示目标和达成状态。
底部三个迷你统计项展示运动天数、消耗热量和总距离,用 spaceAround 让三个项目均匀分布。
运动目标达成判断
根据运动时长判断是否达成目标:
dart
Map<String, dynamic> _getGoalStatus(int currentMinutes, int targetMinutes) {
final percentage = currentMinutes / targetMinutes;
if (percentage >= 1.0) {
return {
'text': '已达标',
'emoji': '🎉',
'color': const Color(0xFF00C9A7),
};
} else if (percentage >= 0.8) {
return {
'text': '即将达标',
'emoji': '💪',
'color': const Color(0xFFFFBE0B),
};
} else {
return {
'text': '继续加油',
'emoji': '🏃',
'color': const Color(0xFFFF6B6B),
};
}
}
达成 100% 显示"已达标",达成 80% 以上显示"即将达标",否则显示"继续加油"。不同状态配合不同的 emoji 和颜色,给用户直观的反馈。
运动类型分布
运动类型分布展示用户不同运动类型的时间占比,用进度条可视化。
dart
Widget _buildTypeStats() {
final types = [
{'type': '跑步', 'time': '68min', 'percent': 0.44, 'color': const Color(0xFFFF6B6B)},
{'type': '步行', 'time': '45min', 'percent': 0.29, 'color': const Color(0xFF4D96FF)},
{'type': '游泳', 'time': '28min', 'percent': 0.18, 'color': const Color(0xFF00C9A7)},
{'type': '其他', 'time': '15min', 'percent': 0.09, 'color': const Color(0xFFFFBE0B)},
];
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r)
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('运动类型分布', style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF1A1A2E)
)),
SizedBox(height: 20.h),
...types.map((t) => Padding(
padding: EdgeInsets.only(bottom: 16.h),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(t['type'] as String, style: TextStyle(
fontSize: 14.sp,
color: const Color(0xFF1A1A2E)
)),
Text(t['time'] as String, style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: t['color'] as Color
)),
],
),
SizedBox(height: 8.h),
ClipRRect(
borderRadius: BorderRadius.circular(4.r),
child: LinearProgressIndicator(
value: t['percent'] as double,
minHeight: 8.h,
backgroundColor: Colors.grey[100],
valueColor: AlwaysStoppedAnimation(t['color'] as Color),
),
),
],
),
)),
],
),
);
}
每种运动类型用不同颜色的进度条表示,进度条的长度对应时间占比。运动类型名称在左边,时间在右边,用对应的颜色显示。
ClipRRect 给进度条添加圆角,让视觉效果更柔和。LinearProgressIndicator 的 value 属性接受 0-1 之间的值,正好对应百分比。
运动类型颜色映射
为不同的运动类型分配固定的颜色:
dart
Color _getExerciseColor(String type) {
switch (type) {
case '跑步': return const Color(0xFFFF6B6B);
case '步行': return const Color(0xFF4D96FF);
case '骑行': return const Color(0xFF00C9A7);
case '游泳': return const Color(0xFF845EC2);
case '瑜伽': return const Color(0xFFFFBE0B);
case '健身': return const Color(0xFFFF9F43);
default: return const Color(0xFF6C63FF);
}
}
固定的颜色映射让用户能快速识别不同的运动类型,在整个应用中保持一致。
每日运动统计
可以添加一个每日运动统计的柱状图:
dart
Widget _buildDailyStats() {
final days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
final minutes = [30, 0, 45, 32, 0, 49, 0];
final maxMinutes = minutes.reduce((a, b) => a > b ? a : b);
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('每日运动', style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF1A1A2E),
)),
SizedBox(height: 20.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(7, (i) => Column(
children: [
Container(
width: 32.w,
height: maxMinutes > 0
? (minutes[i] / maxMinutes * 100).h.clamp(4.h, 100.h)
: 4.h,
decoration: BoxDecoration(
color: minutes[i] > 0
? const Color(0xFF00C9A7)
: Colors.grey[200],
borderRadius: BorderRadius.circular(6.r),
),
),
SizedBox(height: 8.h),
Text(days[i], style: TextStyle(
fontSize: 10.sp,
color: Colors.grey[500]
)),
],
)),
),
],
),
);
}
柱状图展示每天的运动时长,有运动的日子用绿色,没运动的日子用灰色。这种可视化让用户一眼就能看出自己的运动规律。
热量消耗分析
可以添加热量消耗的详细分析:
dart
Widget _buildCalorieAnalysis() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('热量消耗', style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
color: const Color(0xFF1A1A2E),
)),
SizedBox(height: 12.h),
Row(
children: [
_buildCalorieItem('本周', '1,280', 'kcal', const Color(0xFFFF6B6B)),
SizedBox(width: 16.w),
_buildCalorieItem('日均', '183', 'kcal', const Color(0xFFFFBE0B)),
],
),
SizedBox(height: 12.h),
Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: const Color(0xFFFF6B6B).withOpacity(0.08),
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
children: [
Icon(Icons.local_fire_department_rounded, size: 16.w, color: const Color(0xFFFF6B6B)),
SizedBox(width: 8.w),
Expanded(
child: Text(
'相当于消耗了约0.18kg脂肪',
style: TextStyle(fontSize: 12.sp, color: const Color(0xFFFF6B6B)),
),
),
],
),
),
],
),
);
}
Widget _buildCalorieItem(String label, String value, String unit, Color color) {
return Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: TextStyle(fontSize: 11.sp, color: Colors.grey[500])),
SizedBox(height: 4.h),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(value, style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w700,
color: color,
)),
Text(' $unit', style: TextStyle(fontSize: 11.sp, color: Colors.grey[400])),
],
),
],
),
);
}
热量分析展示本周总消耗和日均消耗,并用一个有趣的换算(相当于消耗多少脂肪)让数据更直观。
运动建议
根据运动数据生成个性化建议:
dart
List<String> _generateExerciseTips(int weeklyMinutes, int exerciseDays) {
List<String> tips = [];
if (weeklyMinutes >= 150) {
tips.add('本周运动时长已达到推荐标准,继续保持!');
} else {
final remaining = 150 - weeklyMinutes;
tips.add('距离本周目标还差$remaining分钟,加油!');
}
if (exerciseDays >= 5) {
tips.add('运动频率很好,每周运动5天以上对健康很有益');
} else if (exerciseDays >= 3) {
tips.add('运动频率不错,建议增加到每周5天');
} else {
tips.add('建议增加运动频率,每周至少运动3天');
}
tips.add('运动后记得补充水分和适当休息');
return tips;
}
建议内容根据运动时长和频率动态生成,给用户提供有针对性的指导。
与上周对比
展示和上周的运动数据对比:
dart
Widget _buildWeekComparison() {
return Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: const Color(0xFF00C9A7).withOpacity(0.1),
borderRadius: BorderRadius.circular(10.r),
),
child: Row(
children: [
Icon(Icons.trending_up_rounded, size: 20.w, color: const Color(0xFF00C9A7)),
SizedBox(width: 10.w),
Expanded(
child: Text(
'比上周多运动了28分钟,消耗热量增加15%',
style: TextStyle(fontSize: 13.sp, color: const Color(0xFF00C9A7)),
),
),
],
),
);
}
对比数据用箭头图标表示变化方向,增加用绿色向上箭头,减少用红色向下箭头。
小结
运动分析页面通过青绿色渐变卡片展示汇总数据,用进度条可视化运动类型分布,再配合每日统计和热量分析,帮助用户全面了解自己的运动情况。
核心设计要点包括:目标达成状态用文字和 emoji 直观表达,运动类型用不同颜色区分,进度条长度对应时间占比。这些设计让用户能清楚地看到自己的运动习惯,有针对性地调整运动计划。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net