Flutter for OpenHarmony 视力保护提醒App实战 - 数据统计与图表展示
本文介绍了视力保护应用的数据统计功能实现,重点讲解了使用fl_chart库构建统计图表的方法。统计页面包含四个主要组件:概览卡片展示本周关键数据(提醒次数、休息时间和眼睛评分);选项卡支持周、月、年统计切换;柱状图直观呈现数据趋势;详细统计部分提供更多分析维度。实现上采用了Flutter的响应式设计,通过StatefulWidget管理状态,使用Container创建卡片式UI,并借助GetX框架
数据统计是应用的重要功能,它可以帮助用户了解自己的使用情况和眼睛健康状况。本文将详细讲解如何使用fl_chart库来实现数据统计和图表展示功能。
统计页面的整体结构
统计页面包含统计概览、图表选项卡、柱状图和详细统计等多个组件。这样的设计可以让用户从多个角度了解自己的数据。
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fl_chart/fl_chart.dart';
import '../controllers/app_controller.dart';
import 'detail_pages/weekly_stats_detail.dart';
import 'detail_pages/monthly_stats_detail.dart';
import 'detail_pages/health_report_detail.dart';
class StatisticsPage extends StatefulWidget {
const StatisticsPage({super.key});
State<StatisticsPage> createState() => _StatisticsPageState();
}
class _StatisticsPageState extends State<StatisticsPage> {
final AppController appController = Get.find<AppController>();
int _selectedTab = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('数据统计'),
elevation: 0,
backgroundColor: const Color(0xFF2196F3),
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Column(
children: [
_buildStatisticsOverview(),
SizedBox(height: 16.h),
_buildChartTabs(),
SizedBox(height: 16.h),
_buildChart(),
SizedBox(height: 16.h),
_buildDetailedStats(),
SizedBox(height: 20.h),
],
),
),
);
}
统计页面使用StatefulWidget来管理选中的图表类型。我们通过_selectedTab变量来跟踪用户选择的是周统计、月统计还是年统计。
统计页面的设计理念
数据统计功能是视力保护应用的重要组成部分,它帮助用户了解自己的使用习惯和眼睛健康状况。统计页面的设计遵循以下原则:首先,提供清晰的数据概览,让用户一目了然地看到关键指标;其次,支持多种统计维度(周、月、年),满足不同用户的需求;再次,使用图表可视化数据,使数据更容易理解;最后,提供详细的统计报告,让用户深入了解自己的数据。
在实现统计功能时,我们使用了fl_chart库来绘制柱状图。这个库提供了丰富的图表类型和自定义选项,使我们能够创建美观且功能完整的图表。同时,我们使用GetX框架来管理应用的全局状态,确保统计数据能够实时更新。
统计概览卡片的实现
统计概览卡片显示本周的关键数据,包括提醒次数、休息时间和眼睛评分。
Widget _buildStatisticsOverview() {
return Container(
margin: EdgeInsets.all(16.w),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'本周统计',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
统计概览卡片使用Container创建白色卡片,带有圆角和阴影效果。EdgeInsets.all(16.w)设置外边距,EdgeInsets.all(16.w)设置内边距。BoxDecoration配置卡片的样式,包括白色背景、12像素的圆角和阴影。BoxShadow使用Colors.black.withOpacity(0.1)创建半透明黑色阴影,blurRadius: 8使阴影更加柔和。Column包含标题和统计数据行。标题"本周统计"使用16sp的粗体字体,这样可以让标题更加突出。
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('提醒次数', '42', const Color(0xFF2196F3)),
_buildStatItem('休息时间', '210分', const Color(0xFF4CAF50)),
_buildStatItem('眼睛评分', '8.5', const Color(0xFFFFC107)),
],
),
],
),
);
}
Widget _buildStatItem(String label, String value, Color color) {
return Column(
children: [
Text(
value,
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: color,
),
),
SizedBox(height: 4.h),
Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
),
),
],
);
}
Row使用mainAxisAlignment: MainAxisAlignment.spaceAround使三个统计项均匀分布。每个统计项通过_buildStatItem方法创建,传入标签、数值和颜色三个参数。_buildStatItem构建单个统计项,使用Column包含两个Text组件:第一个显示数值,使用20sp的粗体字体和指定的颜色,这样可以让数值更加突出;第二个显示标签,使用12sp的灰色字体。每个统计项都有自己的颜色:蓝色代表提醒次数,绿色代表休息时间,黄色代表眼睛评分。这样的设计让用户能够快速识别不同的数据类型。
图表选项卡的实现
图表选项卡允许用户在周统计、月统计和年统计之间切换。
Widget _buildChartTabs() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
_buildTabButton('周统计', 0),
SizedBox(width: 12.w),
_buildTabButton('月统计', 1),
SizedBox(width: 12.w),
_buildTabButton('年统计', 2),
],
),
);
}
Widget _buildTabButton(String label, int index) {
final isSelected = _selectedTab == index;
return Expanded(
child: GestureDetector(
onTap: () {
setState(() => _selectedTab = index);
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.h),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF2196F3) : Colors.grey[200],
borderRadius: BorderRadius.circular(8.r),
),
child: Center(
child: Text(
label,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: isSelected ? Colors.white : Colors.grey,
),
),
),
),
),
);
}
_buildChartTabs方法使用Padding添加水平边距,Row包含三个_buildTabButton调用,分别为"周统计"、“月统计"和"年统计”。SizedBox(width: 12.w)在按钮之间添加间距。_buildTabButton构建单个选项卡按钮,接收标签和索引两个参数。isSelected变量通过比较_selectedTab和index来判断该选项卡是否被选中。GestureDetector检测点击事件,点击时通过setState更新_selectedTab变量。Expanded使按钮占据相等的宽度。Container的背景色根据isSelected改变:选中时使用蓝色,未选中时使用灰色。Text的颜色也根据isSelected改变:选中时使用白色,未选中时使用灰色。这样的设计让用户能够清楚地看到当前选中的选项卡。
柱状图的实现
柱状图使用fl_chart库来显示数据。
Widget _buildChart() {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_selectedTab == 0 ? '周提醒统计' : _selectedTab == 1 ? '月提醒统计' : '年提醒统计',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
柱状图使用Container创建白色卡片,带有圆角和阴影效果。EdgeInsets.symmetric(horizontal: 16.w)设置水平边距,EdgeInsets.all(16.w)设置内边距。BoxDecoration配置卡片的样式,包括白色背景、12像素的圆角和阴影。Column包含标题和图表两个元素。标题使用三元运算符根据_selectedTab的值显示不同的文本:周统计显示"周提醒统计",月统计显示"月提醒统计",年统计显示"年提醒统计"。标题使用14sp的粗体字体,这样可以让标题更加突出。
SizedBox(
height: 200.h,
child: BarChart(
BarChartData(
barGroups: _getBarGroups(),
borderData: FlBorderData(show: false),
gridData: const FlGridData(show: false),
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
const titles = ['一', '二', '三', '四', '五', '六', '日'];
return Text(
titles[value.toInt()],
style: TextStyle(fontSize: 10.sp),
);
},
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
return Text(
'${value.toInt()}',
style: TextStyle(fontSize: 10.sp),
);
},
),
),
),
),
),
),
],
),
);
}
List<BarChartGroupData> _getBarGroups() {
return [
BarChartGroupData(x: 0, barRods: [BarChartRodData(toY: 5, color: const Color(0xFF2196F3))]),
BarChartGroupData(x: 1, barRods: [BarChartRodData(toY: 6, color: const Color(0xFF2196F3))]),
BarChartGroupData(x: 2, barRods: [BarChartRodData(toY: 7, color: const Color(0xFF2196F3))]),
BarChartGroupData(x: 3, barRods: [BarChartRodData(toY: 8, color: const Color(0xFF2196F3))]),
BarChartGroupData(x: 4, barRods: [BarChartRodData(toY: 6, color: const Color(0xFF2196F3))]),
BarChartGroupData(x: 5, barRods: [BarChartRodData(toY: 9, color: const Color(0xFF2196F3))]),
BarChartGroupData(x: 6, barRods: [BarChartRodData(toY: 7, color: const Color(0xFF2196F3))]),
];
}
SizedBox设置图表的高度为200.h。BarChart组件来自fl_chart库,用于显示柱状图。BarChartData配置图表的数据和样式。barGroups通过_getBarGroups()方法获取,定义了七个柱子,分别代表一周的七天。borderData: FlBorderData(show: false)隐藏图表的边框,gridData: const FlGridData(show: false)隐藏网格线,这样可以简化图表的外观。titlesData配置图表的标题。bottomTitles显示底部标题(星期几),使用getTitlesWidget回调根据value返回对应的星期文字。leftTitles显示左边标题(数值),使用getTitlesWidget回调根据value返回对应的数值。_getBarGroups方法定义了七个BarChartGroupData,每个代表一天的数据。x表示柱子的位置,barRods包含一个BarChartRodData,toY表示柱子的高度,color设置为蓝色。
详细统计按钮的实现
详细统计按钮提供了访问更详细统计信息的方式。
Widget _buildDetailedStats() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Column(
children: [
_buildDetailButton(
'周详统计',
Icons.calendar_today,
() => Get.to(() => const WeeklyStatsDetail()),
),
SizedBox(height: 12.h),
_buildDetailButton(
'月详统计',
Icons.date_range,
() => Get.to(() => const MonthlyStatsDetail()),
),
SizedBox(height: 12.h),
_buildDetailButton(
'健康报告',
Icons.description,
() => Get.to(() => const HealthReportDetail()),
),
],
),
);
}
Widget _buildDetailButton(String label, IconData icon, VoidCallback onTap) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.grey[300]!, width: 1),
),
child: Row(
children: [
Icon(icon, color: const Color(0xFF2196F3), size: 20.sp),
SizedBox(width: 12.w),
Expanded(
child: Text(
label,
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500),
),
),
Icon(Icons.arrow_forward_ios, size: 16.sp, color: Colors.grey),
],
),
),
);
}
}
_buildDetailedStats方法使用Padding添加水平边距,Column包含三个_buildDetailButton调用,分别为"周详统计"、“月详统计"和"健康报告”。SizedBox(height: 12.h)在按钮之间添加间距。_buildDetailButton构建单个详情按钮,接收标签、图标和点击回调三个参数。GestureDetector检测点击事件,点击时通过Get.to()导航到相应的详情页面。Container使用白色背景和灰色边框,EdgeInsets.all(12.w)设置内边距。Row包含四个元素:左边的图标(蓝色,20sp)、标签文本(14sp粗体)、右边的箭头图标(灰色,16sp)。Expanded使标签文本占据中间的空间。这样的设计让用户能够快速访问更详细的统计信息。
导航设计的考虑
详细统计按钮提供了访问更深层次统计信息的入口。通过提供三个不同的详情页面(周详统计、月详统计和健康报告),我们让用户能够根据自己的需求选择查看不同的统计信息。
按钮的设计也很重要。我们使用了白色背景和灰色边框,使按钮与卡片的背景形成对比。按钮中的图标和文字清晰易读,用户能够快速理解按钮的功能。右边的箭头图标表示这是一个可点击的元素,会导航到另一个页面。
在实现导航时,我们使用了GetX框架提供的Get.to()方法。这个方法提供了平滑的页面转换动画,使用户体验更加流畅。同时,GetX框架还提供了返回上一页的功能,用户可以通过返回按钮或手势返回到统计页面。
统计功能的扩展可能性
在实际应用中,我们可以进一步扩展统计功能:
- 自定义时间范围:允许用户选择任意时间范围来查看统计数据
- 数据导出:允许用户将统计数据导出为CSV或PDF格式
- 对比分析:允许用户对比不同时间段的数据
- 趋势预测:使用机器学习算法预测未来的趋势
- 数据分享:允许用户分享统计数据到社交媒体
总结
通过以上的设计,我们实现了一个完整的数据统计系统。用户可以查看本周的统计数据、切换不同的统计类型、查看详细的统计信息。这样的设计使得数据统计功能易于使用和理解。
在实际应用中,我们可以进一步扩展这个系统,添加更多的图表类型、更详细的数据分析等功能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)