
成绩对比是艺考学习应用中的核心功能模块,其核心价值在于帮助艺考生直观感知自身在不同参照群体中的能力定位,为学习规划提供数据支撑。本次将从架构设计、视觉呈现、交互逻辑等维度,拆解一个覆盖多维度对比、数据可视化的成绩对比页面实现方案。
成绩对比架构设计
成绩对比页面采用StatefulWidget作为核心载体,核心设计考量如下:
- 状态管理:通过局部状态维护当前选中的对比维度(班级/年级/全校/历史)
- 布局结构:采用垂直滚动布局承载多模块对比数据,适配艺考场景下多科目、多维度的数据展示需求
- 交互逻辑:通过弹窗菜单实现对比维度切换,保证操作便捷性
dart
class ComparisonPage extends StatefulWidget {
const ComparisonPage({Key? key}) : super(key: key);
@override
State<ComparisonPage> createState() => _ComparisonPageState();
}
延续状态类的设计,重点关注两个核心状态变量:
selectedComparison:存储当前激活的对比维度,默认值为"班级",符合用户最常用的对比场景comparisons:定义所有可选对比维度,为后续弹窗菜单提供数据源
dart
class _ComparisonPageState extends State<ComparisonPage> {
String selectedComparison = '班级';
final List<String> comparisons = ['班级', '年级', '全校', '历史'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('成绩对比'),
backgroundColor: Colors.red,
AppBar的交互设计细节补充:
- 右侧弹窗菜单:通过
PopupMenuButton实现维度切换,点击后触发状态更新 - 主题色选择:采用红色系作为主色调,贴合艺考应用的视觉风格
- 状态更新:通过
setState刷新页面,保证维度切换后数据实时更新
dart
actions: [
PopupMenuButton<String>(
onSelected: (value) {
setState(() {
selectedComparison = value;
});
},
弹窗菜单的Item构建逻辑:
- 动态生成:基于
comparisons列表映射生成菜单选项 - 文案设计:采用"与XX对比"的句式,符合用户认知习惯
- 布局容器:使用
SingleChildScrollView包裹主体内容,避免多模块数据溢出
dart
itemBuilder: (context) => comparisons.map((comparison) {
return PopupMenuItem(
value: comparison,
child: Text('与$comparison对比'),
);
}).toList(),
),
],
),
主体内容布局的核心设计点:
- 内边距控制:统一设置16.w的内边距,保证各模块间距一致性
- 模块组合:按"概览-分数-排名-进步"的逻辑顺序排列子组件
- 交叉对齐:采用左对齐方式,符合数据类页面的阅读习惯
dart
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildComparisonOverview(),
_buildScoreComparison(),
_buildRankingComparison(),
_buildProgressComparison(),
],
),
),
);
}
}
对比概览卡片实现
概览卡片作为页面核心视觉模块,设计要点如下:
- 视觉层级:采用渐变背景突出核心数据,提升视觉冲击力
- 数据维度:聚焦"我的平均分""群体平均分""分差"三个核心指标
- 响应式适配:使用
w/h/sp单位,适配不同屏幕尺寸的鸿蒙设备
dart
Widget _buildComparisonOverview() {
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red[400]!, Colors.red[600]!],
),
borderRadius: BorderRadius.circular(16.r),
),
概览卡片的内容布局逻辑:
- 标题区域:动态展示当前选中的对比维度,强化用户认知
- 间距控制:设置20.h的垂直间距,区分标题与数据区域
- 排版方式:采用横向均分布局,保证三个核心指标视觉均衡
dart
child: Column(
children: [
Text(
'与$selectedComparison对比',
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 20.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
核心数据项的封装设计:
- 组件复用:通过
_buildOverviewItem统一封装数据展示样式 - 视觉区分:用户数据用纯白展示,群体数据用半透明白色,分差用绿色突出正向优势
- 字体层级:数值采用粗体大号字体,标签采用小号浅色调字体
dart
children: [
_buildOverviewItem('我的平均分', '85.6', Colors.white),
_buildOverviewItem('${selectedComparison}平均', '78.2', Colors.white70),
_buildOverviewItem('差距', '+7.4', Colors.green),
],
),
],
),
);
}
数据项组件的精细化设计:
- 结构分层:数值在上、标签在下,符合数据展示的视觉习惯
- 颜色透传:支持自定义文字颜色,适配不同数据类型的视觉区分
- 间距控制:4.h的垂直间距,保证数值与标签的呼吸感
dart
Widget _buildOverviewItem(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),
标签文字的视觉优化:
- 字号控制:12.sp的小号字体,作为数值的辅助说明
- 透明度处理:80%的不透明度,降低视觉权重,突出核心数值
- 通用性设计:适配不同标签文本长度,保证布局稳定性
dart
Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: color.withOpacity(0.8),
),
),
],
);
}
分数对比图表实现
分数对比模块采用柱状图可视化设计,核心设计思路:
- 容器样式:白色背景+轻微阴影,营造卡片式视觉效果
- 圆角设计:12.r的圆角半径,符合鸿蒙设计规范的视觉风格
- 阴影参数:低透明度灰色阴影,提升层次感但不抢焦点
dart
Widget _buildScoreComparison() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
图表模块的标题与布局设计:
- 标题样式:18.sp粗体,明确模块功能定位
- 间距控制:16.h的垂直间距,区分标题与图表区域
- 图表尺寸:固定200.h的高度,保证多设备下的展示一致性
dart
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'分数对比',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
SizedBox(
height: 200.h,
child: BarChart(
柱状图的基础配置项设计:
- 网格线:隐藏网格线,简化视觉层级
- 坐标轴:仅保留左侧数值轴和底部科目轴,减少视觉干扰
- 右侧/顶部轴:隐藏冗余坐标轴,聚焦核心数据展示
dart
BarChartData(
gridData: FlGridData(show: false),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
return Text(
value.toInt().toString(),
style: TextStyle(fontSize: 12.sp),
);
},
),
),
坐标轴的精细化配置:
- 右侧轴:完全隐藏,避免重复信息
- 顶部轴:隐藏,简化图表顶部视觉
- 底部轴:展示艺考核心科目(美术/音乐/舞蹈/播音),贴合业务场景
dart
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
final subjects = ['美术', '音乐', '舞蹈', '播音'];
if (value.toInt() < subjects.length) {
return Text(
subjects[value.toInt()],
style: TextStyle(fontSize: 12.sp),
);
}
return const Text('');
},
),
),
),
图表边框与数据组设计:
- 边框隐藏:移除图表边框,提升视觉简洁度
- 柱状组设计:每个科目包含两个柱子(用户分数/群体平均分)
- 颜色区分:蓝色代表用户分数,灰色代表群体平均分,符合视觉认知习惯
dart
borderData: FlBorderData(show: false),
barGroups: [
BarChartGroupData(x: 0, barRods: [
BarChartRodData(toY: 88, color: Colors.blue, width: 15.w),
BarChartRodData(toY: 75, color: Colors.grey, width: 15.w),
]),
BarChartGroupData(x: 1, barRods: [
BarChartRodData(toY: 92, color: Colors.blue, width: 15.w),
BarChartRodData(toY: 78, color: Colors.grey, width: 15.w),
]),
延续科目数据配置:
- 舞蹈科目:用户76分 vs 群体72分,体现小幅优势
- 播音科目:用户85分 vs 群体80分,中等优势
- 宽度统一:所有柱子保持15.w的宽度,保证视觉一致性
dart
BarChartGroupData(x: 2, barRods: [
BarChartRodData(toY: 76, color: Colors.blue, width: 15.w),
BarChartRodData(toY: 72, color: Colors.grey, width: 15.w),
]),
BarChartGroupData(x: 3, barRods: [
BarChartRodData(toY: 85, color: Colors.blue, width: 15.w),
BarChartRodData(toY: 80, color: Colors.grey, width: 15.w),
]),
],
),
),
),
图表图例设计要点:
- 视觉对应:色块颜色与图表柱子完全一致
- 布局方式:横向排列,左侧色块+右侧文字
- 字号控制:12.sp浅灰色文字,作为辅助说明
dart
SizedBox(height: 16.h),
Row(
children: [
Container(
width: 20.w,
height: 12.h,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(2.r),
),
),
SizedBox(width: 8.w),
Text(
'我的分数',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
图例的动态适配设计:
- 间距控制:24.w的横向间距,区分两个图例项
- 动态文本:根据选中的对比维度更新图例文字
- 圆角设计:2.r的圆角,让色块更柔和
dart
SizedBox(width: 24.w),
Container(
width: 20.w,
height: 12.h,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(2.r),
),
),
SizedBox(width: 8.w),
Text(
'$selectedComparison平均',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
],
),
],
),
);
}
排名对比分析实现
排名对比模块的核心设计思路:
- 数据建模:采用列表存储各科目排名数据,包含4个核心字段
- 科目覆盖:覆盖艺考核心科目(美术基础/音乐理论/舞蹈基础/播音主持)
- 数据维度:排名/总人数/百分位,全方位展示用户位次
dart
Widget _buildRankingComparison() {
final rankings = [
{'subject': '美术基础', 'myRank': 5, 'total': 30, 'percentile': 83},
{'subject': '音乐理论', 'myRank': 3, 'total': 28, 'percentile': 89},
{'subject': '舞蹈基础', 'myRank': 8, 'total': 25, 'percentile': 68},
{'subject': '播音主持', 'myRank': 6, 'total': 32, 'percentile': 81},
];
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
排名模块的容器样式设计:
- 阴影效果:与分数对比模块保持一致,保证视觉统一性
- 标题样式:18.sp粗体,明确模块定位
- 间距控制:16.h的垂直间距,区分标题与数据列表
dart
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'排名对比',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
排名列表的动态渲染逻辑:
- 映射遍历:通过
map方法遍历排名数据列表 - 间距控制:16.h的底部间距,区分不同科目项
- 布局结构:左侧科目名称+中间排名信息+右侧百分位
dart
...rankings.map((ranking) {
return Padding(
padding: EdgeInsets.only(bottom: 16.h),
child: Row(
children: [
SizedBox(
width: 80.w,
child: Text(
ranking['subject'] as String,
style: TextStyle(fontSize: 14.sp),
),
),
排名信息的层级设计:
- 排名文本:粗体展示"第X名/共X人",突出核心信息
- 进度条:通过
LinearProgressIndicator可视化百分位 - 颜色映射:根据百分位值动态设置进度条颜色
dart
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'第${ranking['myRank']}名 / 共${ranking['total']}人',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4.h),
LinearProgressIndicator(
value: (ranking['percentile'] as double) / 100,
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(
_getRankColor(ranking['percentile'] as int),
),
),
],
),
),
百分位文本展示设计:
- 间距控制:8.w的横向间距,区分进度条与文本
- 样式设计:粗体+颜色映射,突出百分位等级
- 文案格式:"前X%"的表述方式,符合用户认知习惯
dart
SizedBox(width: 8.w),
Text(
'前${ranking['percentile']}%',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: _getRankColor(ranking['percentile'] as int),
),
),
],
),
);
}).toList(),
],
),
);
}
排名颜色映射逻辑
颜色映射的核心设计原则:
- 等级区分:通过颜色直观展示排名优劣
- 阈值设定:90%(绿)、80%(蓝)、70%(橙)、其他(红)
- 业务适配:符合艺考场景下用户对排名等级的认知习惯
dart
Color _getRankColor(int percentile) {
switch (percentile) {
case 90:
return Colors.green;
case 80:
return Colors.blue;
case 70:
return Colors.orange;
default:
return Colors.red;
}
}
进步对比分析实现
进步对比模块的容器设计:
- 视觉统一:与分数/排名模块保持相同的卡片样式
- 圆角阴影:延续12.r圆角+轻量阴影的设计语言
- 内边距:16.w的统一内边距,保证布局一致性
dart
Widget _buildProgressComparison() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
进步对比模块的标题与布局:
- 标题样式:18.sp粗体,与其他模块保持视觉统一
- 图表尺寸:200.h的固定高度,保证折线图展示空间
- 网格配置:仅展示水平网格线,间隔10分,辅助分数读取
dart
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'进步对比',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
SizedBox(
height: 200.h,
child: LineChart(
LineChartData(
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 10,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey[300],
strokeWidth: 1,
);
},
),
折线图坐标轴配置:
- 左侧轴:显示分数值,间隔10分,12.sp字号
- 右侧/顶部轴:隐藏,简化视觉
- 底部轴:展示6个月的时间维度,贴合艺考长期学习的场景
dart
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 10,
getTitlesWidget: (value, meta) {
return Text(
value.toInt().toString(),
style: TextStyle(fontSize: 12.sp),
);
},
),
),
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
final months = ['1月', '2月', '3月', '4月', '5月', '6月'];
if (value.toInt() < months.length) {
return Text(
months[value.toInt()],
style: TextStyle(fontSize: 12.sp),
);
}
return const Text('');
},
),
),
),
折线图的基础配置:
- 边框隐藏:移除图表边框,提升简洁度
- 线条配置:用户进步线(蓝色)+群体平均线(灰色)
- 曲线设计:启用曲线效果,让趋势更平滑
dart
borderData: FlBorderData(show: false),
lineBarsData: [
LineChartBarData(
spots: [
const FlSpot(0, 65),
const FlSpot(1, 72),
const FlSpot(2, 78),
const FlSpot(3, 82),
const FlSpot(4, 85),
const FlSpot(5, 88),
],
isCurved: true,
color: Colors.blue,
barWidth: 3.w,
用户进步折线的细节设计:
- 数据点:显示圆形数据点,4.r半径
- 描边设计:白色描边+蓝色填充,提升视觉辨识度
- 线条宽度:3.w的宽度,保证线条清晰可见
dart
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 4.r,
color: Colors.blue,
strokeWidth: 2.w,
strokeColor: Colors.white,
);
},
),
),
群体平均进步线设计:
- 数据趋势:从60分缓慢增长到75分,体现平稳进步
- 颜色区分:灰色系,与用户的蓝色形成视觉对比
- 数据点样式:与用户线保持一致,保证视觉统一
dart
LineChartBarData(
spots: [
const FlSpot(0, 60),
const FlSpot(1, 65),
const FlSpot(2, 68),
const FlSpot(3, 70),
const FlSpot(4, 73),
const FlSpot(5, 75),
],
isCurved: true,
color: Colors.grey,
barWidth: 3.w,
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 4.r,
color: Colors.grey,
strokeWidth: 2.w,
strokeColor: Colors.white,
);
},
),
),
],
),
),
),
进步对比图例设计:
- 样式统一:与分数对比图例保持相同的设计语言
- 动态文本:根据选中的对比维度更新图例描述
- 间距控制:16.h的垂直间距,区分图例与提示框
dart
SizedBox(height: 16.h),
Row(
children: [
Container(
width: 20.w,
height: 12.h,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(2.r),
),
),
SizedBox(width: 8.w),
Text(
'我的进步',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
SizedBox(width: 24.w),
Container(
width: 20.w,
height: 12.h,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(2.r),
),
),
SizedBox(width: 8.w),
Text(
'$selectedComparison平均进步',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
],
),
进步提示框设计:
- 视觉风格:浅绿色背景+绿色边框,传递正向反馈
- 图标搭配:上升趋势图标,强化进步感知
- 文案设计:动态适配对比维度,个性化反馈用户进步情况
dart
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.green[50],
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.green[200]!),
),
child: Row(
children: [
Icon(Icons.trending_up, color: Colors.green),
SizedBox(width: 8.w),
Text(
'你的进步速度超过了$selectedComparison平均水平!',
style: TextStyle(
fontSize: 14.sp,
color: Colors.green[700],
),
),
],
),
),
],
),
);
}
对比维度切换设计要点
- 交互入口:AppBar右侧的弹窗菜单,符合移动端操作习惯
- 状态同步:切换后实时更新所有模块的对比数据
- 用户体验:切换过程中保留页面滚动位置,提升连贯性
数据可视化优化策略
- 动画效果:为图表添加渐入动画,提升数据加载的视觉体验
- 交互提示:点击数据点展示详细分数信息,增强交互性
- 颜色体系:建立统一的颜色规范,用户数据用蓝色系,群体数据用灰色系
- 字体层级:核心数据用粗体大号字体,辅助信息用小号浅色调字体
隐私保护设计
- 数据脱敏:展示群体数据时仅显示统计值,不暴露个人信息
- 权限控制:用户可自主选择是否参与群体对比
- 本地存储:敏感数据仅存储在本地,不进行云端上传
- 匿名化:群体对比数据采用匿名化处理,保护用户隐私
通过以上实现,我们创建了一个功能完善、视觉丰富的成绩对比页面。这个页面不仅能够帮助用户了解自己在群体中的相对水平,还提供了直观的数据可视化和详细的分析功能,为用户的学习提供了有力的对比分析支持。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net