
学习统计页面通过图表和数据展示用户的学习情况,帮助用户了解学习效果。本文介绍如何实现一个学习统计页面,包括概览卡片、周学习图表、分类进度和详细数据。
页面基础结构
定义学习统计页面:
dart
class StatisticsScreen extends StatelessWidget {
const StatisticsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('学习统计')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildOverviewCard(context),
SizedBox(height: 16.h),
_buildWeeklyChart(),
SizedBox(height: 16.h),
_buildCategoryProgress(),
SizedBox(height: 16.h),
_buildDetailStats(context),
],
),
),
);
}
使用StatelessWidget构建页面,SingleChildScrollView让内容可以滚动。Column纵向排列四个区域:概览卡片、周学习图表、分类进度和详细数据。crossAxisAlignment.start让内容左对齐,SizedBox控制区域间距。这种分区设计让统计信息层次清晰。
概览卡片
构建顶部的学习概览:
dart
Widget _buildOverviewCard(BuildContext context) {
final userProvider = Provider.of<UserProvider>(context);
final appProvider = Provider.of<AppProvider>(context);
return Card(
child: Padding(
padding: EdgeInsets.all(20.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('学习概览', style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 16.h),
从两个Provider获取统计数据,用Card包裹概览内容。标题用粗体显示,crossAxisAlignment.start让内容左对齐。这个区域展示最关键的三个指标,让用户快速了解学习情况。
概览数据行
显示三个关键指标:
dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildOverviewItem('总学习时长', '${userProvider.totalLearningTime}分钟', Icons.timer),
_buildOverviewItem('已学词汇', '${appProvider.completedLessons.length}个', Icons.school),
_buildOverviewItem('连续天数', '${userProvider.consecutiveDays}天', Icons.local_fire_department),
],
),
],
),
),
);
}
用Row横向排列三个统计项,mainAxisAlignment.spaceAround让它们均匀分布。每个统计项包含标签、数值和图标,调用_buildOverviewItem方法构建。这种数据可视化让关键指标一目了然。
概览项构建
封装概览项的构建逻辑:
dart
Widget _buildOverviewItem(String label, String value, IconData icon) {
return Column(
children: [
Icon(icon, color: const Color(0xFF00897B), size: 28.sp),
SizedBox(height: 8.h),
Text(value, style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold)),
Text(label, style: TextStyle(fontSize: 12.sp, color: Colors.grey)),
],
);
}
用Column纵向排列图标、数值和标签。图标用主题色,数值用大字号粗体,标签用小字号灰色。这种纵向布局让每个统计项独立成块,信息层次清晰。图标语义化,计时器代表时长,书本代表词汇,火焰代表连续。
周学习图表
构建本周学习柱状图:
dart
Widget _buildWeeklyChart() {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('本周学习', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 16.h),
SizedBox(
height: 200.h,
child: BarChart(
用Card包裹图表,标题用粗体显示。SizedBox设置图表高度为200.h。BarChart是fl_chart库提供的柱状图组件,功能强大。这种图表可视化比纯数字更直观。
图表配置
配置柱状图的参数:
dart
BarChartData(
alignment: BarChartAlignment.spaceAround,
maxY: 20,
barTouchData: BarTouchData(enabled: false),
titlesData: FlTitlesData(
show: true,
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
const days = ['一', '二', '三', '四', '五', '六', '日'];
return Text(days[value.toInt()], style: TextStyle(fontSize: 12.sp));
},
),
),
alignment设置柱子对齐方式,maxY设置Y轴最大值,barTouchData禁用触摸交互。titlesData配置坐标轴标题,底部显示星期几。getTitlesWidget自定义标题组件,将索引映射为中文星期。
隐藏其他坐标轴
只显示底部坐标轴:
dart
leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(show: false),
左、上、右三个坐标轴都隐藏,只保留底部的星期标签。borderData隐藏边框。这种简洁设计让图表更清爽,用户关注数据本身而非坐标轴。
柱状图数据
定义每天的学习数据:
dart
barGroups: [
_makeBarGroup(0, 8),
_makeBarGroup(1, 12),
_makeBarGroup(2, 6),
_makeBarGroup(3, 15),
_makeBarGroup(4, 10),
_makeBarGroup(5, 18),
_makeBarGroup(6, 5),
],
),
),
),
],
),
),
);
}
7个柱子代表一周7天,第一个参数是索引(0-6),第二个参数是学习数量。实际项目中应该从服务器或数据库获取真实数据。这种数据展示让用户看到每天的学习波动。
柱子构建
封装柱子的构建逻辑:
dart
BarChartGroupData _makeBarGroup(int x, double y) {
return BarChartGroupData(
x: x,
barRods: [
BarChartRodData(
toY: y,
color: const Color(0xFF00897B),
width: 20.w,
borderRadius: BorderRadius.vertical(top: Radius.circular(4.r)),
),
],
);
}
BarChartGroupData定义一组柱子,x是横坐标,barRods是柱子列表。BarChartRodData定义单个柱子,toY是高度,color是颜色,width是宽度。顶部圆角让柱子更柔和。
分类进度
构建分类学习进度:
dart
Widget _buildCategoryProgress() {
final categories = [
{'name': '基础问候', 'progress': 0.8, 'count': '16/20'},
{'name': '数字手语', 'progress': 0.6, 'count': '12/20'},
{'name': '日常用语', 'progress': 0.4, 'count': '12/30'},
{'name': '情感表达', 'progress': 0.3, 'count': '7/25'},
];
定义4个分类的进度数据,每个包含名称、进度百分比和数量。进度从0到1表示完成度。这种数据驱动的方式便于添加或删除分类。
分类进度卡片
构建分类进度的容器:
dart
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('分类进度', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 16.h),
用Card包裹分类进度,标题用粗体显示。crossAxisAlignment.start让内容左对齐。这个区域展示各个分类的详细进度,帮助用户了解哪些分类需要加强。
分类进度项
使用map生成进度条:
dart
...categories.map((cat) => Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(cat['name'] as String, style: TextStyle(fontSize: 14.sp)),
Text(cat['count'] as String, style: TextStyle(fontSize: 12.sp, color: Colors.grey)),
],
),
SizedBox(height: 4.h),
用...展开运算符将map结果插入列表。顶部显示分类名称和数量,左右两端对齐。数量用灰色小字显示,表示这是辅助信息。
线性进度条
显示分类的进度条:
dart
LinearProgressIndicator(
value: cat['progress'] as double,
backgroundColor: Colors.grey[200],
valueColor: const AlwaysStoppedAnimation(Color(0xFF00897B)),
),
],
),
)).toList(),
],
),
),
);
}
LinearProgressIndicator显示线性进度条,value是进度值(0-1),backgroundColor是背景色,valueColor是进度颜色。AlwaysStoppedAnimation让颜色保持不变。这种进度条比数字更直观。
详细数据
构建详细统计数据:
dart
Widget _buildDetailStats(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('详细数据', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 16.h),
_buildDetailRow('测验次数', '28次'),
_buildDetailRow('平均正确率', '85%'),
_buildDetailRow('最高连续天数', '15天'),
_buildDetailRow('获得积分', '1280分'),
_buildDetailRow('解锁成就', '6个'),
],
),
),
);
}
用Card包裹详细数据,标题用粗体显示。5个数据项调用_buildDetailRow方法构建。这些数据提供更全面的统计信息,满足用户深入了解的需求。
详细数据行
封装数据行的构建逻辑:
dart
Widget _buildDetailRow(String label, String value) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: TextStyle(fontSize: 14.sp, color: Colors.grey[600])),
Text(value, style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold)),
],
),
);
}
}
用Row横向排列标签和数值,mainAxisAlignment.spaceBetween让它们分布在两端。标签用灰色,数值用粗体黑色。这种左右对齐的布局清晰易读,是数据展示的经典设计。
fl_chart库的使用
强大的图表库:
dart
import 'package:fl_chart/fl_chart.dart';
BarChart(
BarChartData(
alignment: BarChartAlignment.spaceAround,
maxY: 20,
barGroups: [...],
),
)
fl_chart是Flutter中功能最强大的图表库之一,支持折线图、柱状图、饼图等多种图表类型。API设计合理,自定义能力强。使用第三方库可以快速实现复杂的图表功能。
BarChartData的配置
丰富的配置选项:
dart
BarChartData(
alignment: BarChartAlignment.spaceAround, // 柱子对齐方式
maxY: 20, // Y轴最大值
barTouchData: BarTouchData(enabled: false), // 触摸交互
titlesData: FlTitlesData(...), // 坐标轴标题
borderData: FlBorderData(show: false), // 边框
barGroups: [...], // 柱子数据
)
每个参数都有明确的作用,通过组合这些参数可以实现各种样式的柱状图。这种声明式配置让代码清晰易懂。
getTitlesWidget自定义标题
自定义坐标轴标题:
dart
getTitlesWidget: (value, meta) {
const days = ['一', '二', '三', '四', '五', '六', '日'];
return Text(days[value.toInt()], style: TextStyle(fontSize: 12.sp));
},
getTitlesWidget回调函数接收值和元数据,返回标题组件。这里将索引(0-6)映射为中文星期。这种自定义能力让图表可以适应各种需求。
LinearProgressIndicator的使用
简单的进度条组件:
dart
LinearProgressIndicator(
value: cat['progress'] as double,
backgroundColor: Colors.grey[200],
valueColor: const AlwaysStoppedAnimation(Color(0xFF00897B)),
),
LinearProgressIndicator是Flutter内置的进度条组件,value是进度值(0-1),backgroundColor是背景色,valueColor是进度颜色。AlwaysStoppedAnimation包裹颜色让它保持不变。这个组件简单易用,适合展示进度。
展开运算符的应用
简化列表插入:
dart
...categories.map((cat) => Padding(...)).toList(),
_buildDetailRow('测验次数', '28次'),
_buildDetailRow('平均正确率', '85%'),
...展开运算符将map返回的列表展开,插入到父列表中。这比先创建列表再用addAll添加更简洁。展开运算符是Dart的语法糖,让代码更优雅。
数据驱动的UI
根据数据动态生成UI:
dart
final categories = [
{'name': '基础问候', 'progress': 0.8, 'count': '16/20'},
// ...
];
...categories.map((cat) {
return LinearProgressIndicator(value: cat['progress'] as double);
}).toList(),
UI完全由数据驱动,修改数据就能改变显示。添加新分类只需在数组中增加一项,不用修改UI代码。这种数据驱动的设计让代码更灵活,易于维护。
响应式布局
使用flutter_screenutil适配屏幕:
dart
fontSize: 18.sp,
padding: EdgeInsets.all(20.w),
height: 200.h,
width: 20.w,
.sp用于字号,.w和.h用于尺寸和间距。这些单位会根据屏幕尺寸自动缩放,确保在不同设备上比例一致。一套代码适配所有屏幕。
小结
学习统计页面通过概览卡片展示关键指标,柱状图可视化每周学习情况。分类进度用进度条展示各分类完成度,详细数据提供全面的统计信息。整体设计注重数据可视化和信息层次,帮助用户全面了解学习效果。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net