【Flutter for OpenHarmony 跨平台征文】Flutter 血压趋势统计实战:从统计数据计算到简化柱状图的鸿蒙开发指南
【Flutter for OpenHarmony 跨平台征文】Flutter 血压趋势统计实战从统计数据计算到简化柱状图的鸿蒙开发指南欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net 写在前面嗨大家好我是上海某高校大一计算机专业的学生 专注Flutter for OpenHarmony跨平台开发前面三篇我们已经完整实现✅ 血压数据模型 WHO 分类算法✅ 血压录入表单 实时预览✅ 历史记录列表 空状态设计这是血压系列收官篇趋势统计 简化柱状图全程100% Flutter(Dart)代码无 ArkUI、无 ETS、无鸿蒙原生API一套代码直接跑OpenHarmony Android真正跨平台一、趋势统计需求分析1.1 功能需求需求说明统计卡片平均收缩压 / 平均舒张压 / 综合评估趋势图表最近7天双柱状图收缩压舒张压数据列表本周明细 状态颜色标签空数据兼容无记录时不崩溃、显示友好提示跨平台运行Flutter 编译鸿蒙 Hap 直接使用1.2 技术方案Flutter 原生实现不引入任何第三方图表库使用Flutter 原生组件ContainerRowColumn实现轻量柱状图✅ 体积小✅ 性能高✅ 鸿蒙/安卓双端完美兼容二、完整 Flutter 代码实现鸿蒙直接运行2.1 趋势页面主结构importpackage:flutter/material.dart;import../model/blood_pressure_model.dart;classBloodPressureTrendPageextendsStatefulWidget{finalListBloodPressureRecordrecords;constBloodPressureTrendPage({super.key,requiredthis.records,});overrideStateBloodPressureTrendPagecreateState()_BloodPressureTrendPageState();}class_BloodPressureTrendPageStateextendsStateBloodPressureTrendPage{lateMapString,num_avgData;overridevoidinitState(){super.initState();_avgData_calculateAverage();}// 计算一周平均血压MapString,num_calculateAverage(){if(widget.records.isEmpty){return{systolic:0,diastolic:0,pulse:0};}finallistwidget.records.take(7).toList();finalsumSyslist.fold(0,(a,b)ab.systolic);finalsumDialist.fold(0,(a,b)ab.diastolic);finalsumPullist.fold(0,(a,b)ab.pulse);return{systolic:(sumSys/list.length).round(),diastolic:(sumDia/list.length).round(),pulse:(sumPul/list.length).round(),};}overrideWidgetbuild(BuildContextcontext){returnScaffold(backgroundColor:constColor(0xFFF5F7FA),appBar:AppBar(title:constText(血压趋势统计),backgroundColor:constColor(0xFF6366F1),centerTitle:true,elevation:0,),body:widget.records.isEmpty?const_EmptyChartView():SingleChildScrollView(padding:constEdgeInsets.all(16),child:Column(children:[// 平均统计卡片_buildAverageCard(),constSizedBox(height:16),// 趋势柱状图_buildChartCard(),constSizedBox(height:16),// 本周数据列表_buildWeekDataList(),],),),);}}2.2 平均血压统计卡片// 平均血压卡片Widget_buildAverageCard(){returnContainer(width:double.infinity,padding:constEdgeInsets.all(16),decoration:BoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(16),),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[constRow(children:[Text( 本周平均血压,style:TextStyle(fontSize:16,fontWeight:FontWeight.bold)),],),constSizedBox(height:20),Row(mainAxisAlignment:MainAxisAlignment.spaceAround,children:[_buildStatItem(value:_avgData[systolic].toString(),label:平均收缩压,color:constColor(0xFFF44336),),_buildStatItem(value:_avgData[diastolic].toString(),label:平均舒张压,color:constColor(0xFF2196F3),),_buildStatItem(value:${_avgData[systolic]}/${_avgData[diastolic]},label:综合评估,color:Colors.black87,),],),],),);}Widget_buildStatItem({requiredStringvalue,requiredStringlabel,requiredColorcolor,}){returnColumn(children:[Text(value,style:TextStyle(fontSize:28,fontWeight:FontWeight.bold,color:color),),constSizedBox(height:4),Text(label,style:constTextStyle(fontSize:12,color:Colors.grey)),],);}2.3 Flutter 原生简化柱状图核心// 血压趋势柱状图Widget_buildChartCard(){finallistwidget.records.take(7).toList();returnContainer(width:double.infinity,padding:constEdgeInsets.all(16),decoration:BoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(16),),child:Column(children:[constRow(children:[Text( 本周血压趋势,style:TextStyle(fontSize:16,fontWeight:FontWeight.bold)),],),constSizedBox(height:20),// 柱状图Row(crossAxisAlignment:CrossAxisAlignment.end,children:List.generate(list.length,(index){finalitemlist[index];finalsysHeight_normalizeSys(item.systolic);finaldiaHeight_normalizeDia(item.diastolic);finalweek_getWeekLabel(index);returnExpanded(child:Column(children:[Row(mainAxisAlignment:MainAxisAlignment.center,crossAxisAlignment:CrossAxisAlignment.end,children:[// 收缩压红Container(width:12,height:sysHeight,decoration:BoxDecoration(color:constColor(0xFFF44336),borderRadius:BorderRadius.circular(3),),),constSizedBox(width:4),// 舒张压橙Container(width:12,height:diaHeight,decoration:BoxDecoration(color:constColor(0xFFFF9800),borderRadius:BorderRadius.circular(3),),),],),constSizedBox(height:6),Text(week,style:constTextStyle(fontSize:11,color:Colors.grey)),],),);}),),constSizedBox(height:16),// 图例constRow(mainAxisAlignment:MainAxisAlignment.center,children:[_Legend(color:Color(0xFFF44336),text:收缩压),SizedBox(width:24),_Legend(color:Color(0xFFFF9800),text:舒张压),],),],),);}// 归一化收缩压 → 高度double_normalizeSys(int value){constminH20.0,maxH80.0;double norm(value-90)/120;double hminHnorm*(maxH-minH);returnh.clamp(minH,maxH);}// 归一化舒张压 → 高度double_normalizeDia(int value){constminH20.0,maxH80.0;double norm(value-50)/80;double hminHnorm*(maxH-minH);returnh.clamp(minH,maxH);}// 星期文字String_getWeekLabel(int i){constlist[一,二,三,四,五,六,日];returnlist[i];}2.4 本周数据列表// 本周数据列表Widget_buildWeekDataList(){finallistwidget.records.take(7).toList();returnContainer(padding:constEdgeInsets.all(16),decoration:BoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(16),),child:Column(children:[constRow(children:[Text( 本周数据,style:TextStyle(fontSize:16,fontWeight:FontWeight.bold)),],),constSizedBox(height:12),...List.generate(list.length,(index){finalitemlist[index];returnPadding(padding:constEdgeInsets.symmetric(vertical:8),child:Row(children:[Text(item.formattedDate,style:constTextStyle(fontSize:13,color:Colors.grey)),constSizedBox(width:12),Text(${item.systolic}/${item.diastolic},style:TextStyle(fontSize:16,fontWeight:FontWeight.bold,color:HexColor(item.status.color)),),constSpacer(),Text(${item.pulse}bpm,style:constTextStyle(fontSize:12,color:Colors.grey)),constSizedBox(width:8),Container(padding:constEdgeInsets.symmetric(horizontal:8,vertical:4),decoration:BoxDecoration(color:HexColor(item.status.color).withOpacity(0.1),borderRadius:BorderRadius.circular(8),),child:Text(item.status.text,style:TextStyle(color:HexColor(item.status.color),fontSize:12),),),],),);}),],),);}2.5 空状态 / 图例工具类// 空图表状态class_EmptyChartViewextendsStatelessWidget{const_EmptyChartView();overrideWidgetbuild(BuildContextcontext){returnconstCenter(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[Text(,style:TextStyle(fontSize:64)),SizedBox(height:16),Text(暂无统计数据,style:TextStyle(fontSize:16,color:Colors.grey)),],),);}}// 图例组件class_LegendextendsStatelessWidget{finalColorcolor;finalStringtext;const_Legend({super.key,requiredthis.color,requiredthis.text});overrideWidgetbuild(BuildContextcontext){returnRow(children:[Container(width:10,height:10,decoration:BoxDecoration(color:color,shape:BoxShape.circle)),constSizedBox(width:6),Text(text,style:constTextStyle(fontSize:12,color:Colors.grey)),],);}}// 十六进制颜色工具classHexColorextendsColor{staticint_getColorFromHex(StringhexColor){hexColorhexColor.toUpperCase().replaceAll(#,);if(hexColor.length6)hexColorFF$hexColor;returnint.parse(hexColor,radix:16);}HexColor(finalStringhexColor):super(_getColorFromHex(hexColor));}三、Flutter 数据统计核心逻辑3.1 平均值计算finallistwidget.records.take(7).toList();finalsumSyslist.fold(0,(a,b)ab.systolic);finalavg(sumSys/list.length).round();3.2 图表归一化算法数值→高度double_normalizeSys(int value){constminHeight20.0;constmaxHeight80.0;double normalized(value-90)/120;double heightminHeightnormalized*(maxHeight-minHeight);returnheight.clamp(minHeight,maxHeight);}✅ 自动适配视觉高度✅ 防止极端值溢出四、为什么这是Flutter for OpenHarmony不是纯鸿蒙你可以直接放在文章里官方征文专用说明✅ 100% Flutter 技术栈语言Dart框架Flutter 3.x无 ArkUI / 无 ETS / 无鸿蒙原生API无鸿蒙专属装饰器如 Builder、State✅ 真正跨平台编译一套代码直接编译Android APKOpenHarmony HAP鸿蒙安装包✅ 平台体验完全一致列表、图表、统计、颜色、交互鸿蒙设备 安卓手机完全相同五、Flutter 开发踩坑记录坑1柱状图高度不对✅ 解决使用归一化算法 clamp限制范围坑2图表从中间向上长✅ 解决使用CrossAxisAlignment.end底部对齐坑3无数据崩溃✅ 解决优先判断records.isEmpty显示空状态坑4列表不刷新✅ 解决initState中预计算更新数据后setState六、功能验证清单鸿蒙真机测试功能效果状态平均统计正确计算一周平均值✅双柱状图红收缩压 / 橙舒张压✅柱高映射数值越大柱子越高✅本周列表显示日期、血压、脉搏、状态✅空状态无数据显示友好页面✅鸿蒙运行无闪退、无错位✅七、个人总结作为大一学生用Flutter 实现跨平台健康 App真的收获巨大通过这四篇实战我完整掌握了Flutter 表单与状态管理Flutter 列表与空状态设计Flutter 原生图表绘制不用第三方库Flutter for OpenHarmony 跨平台编译图表并没有想象中难用 Flutter 最基础的组件就能做出美观、实用的趋势图八、血压系列完整收官 ✅ 数据模型✅ 录入表单✅ 历史列表✅ 趋势统计本篇全套Flutter 跨平台代码可直接用于课程设计毕业设计健康类跨平台 AppFlutter for OpenHarmony 学习示范再次感谢大家阅读我们下个系列再见创作主题Flutter for OpenHarmony 跨平台开发技术栈Flutter(Dart)适用平台OpenHarmony、Android作者上海大一计算机专业学生版权转载注明出处–