Flutter for OpenHarmony 身体健康状况记录App实战 - 添加血压记录实现
本文介绍了一个血压记录页面的Flutter实现方案。页面包含三个核心组件:血压主卡片(显示收缩压和舒张压)、脉搏卡片和时间卡片。血压值通过加减按钮调整,初始设置为标准值120/80 mmHg。页面顶部导航栏提供保存功能,血压状态会根据数值动态显示(正常/偏高/高血压)。整体设计采用卡片式布局,保持界面简洁直观,通过颜色和图标增强用户交互体验。
#
前言
血压是心血管健康的重要指标,需要同时记录收缩压(高压)和舒张压(低压)两个数值。这个页面的交互设计和体重页面不同,我们用加减按钮来调整数值,更适合整数输入的场景。
除了血压值,很多血压计还会显示脉搏,所以我们也加上脉搏记录功能。
状态变量定义
血压记录需要三个数值:收缩压、舒张压、脉搏。
class AddBloodPressurePage extends StatefulWidget {
const AddBloodPressurePage({super.key});
State<AddBloodPressurePage> createState() => _AddBloodPressurePageState();
}
class _AddBloodPressurePageState extends State<AddBloodPressurePage> {
int _systolic = 120;
int _diastolic = 80;
int _pulse = 72;
初始值 120/80 是标准的正常血压,72 是正常的静息心率。这些默认值让用户只需要微调就能完成记录。
用 int 而不是 double,因为血压通常只记录整数。
页面布局结构
页面分为三个卡片:血压主卡片、脉搏卡片、时间卡片。
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFAFAFC),
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: IconButton(
icon: Icon(Icons.close_rounded, size: 24.w),
onPressed: () => Get.back()
),
title: Text('记录血压', style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600
)),
centerTitle: true,
actions: [
TextButton(
onPressed: () {
Get.back();
Get.snackbar('成功', '血压记录已保存', backgroundColor: Colors.white);
},
child: Text('保存', style: TextStyle(
fontSize: 15.sp,
color: const Color(0xFF6C63FF),
fontWeight: FontWeight.w600
)),
),
],
),
body: SingleChildScrollView(
padding: EdgeInsets.all(20.w),
child: Column(
children: [
_buildMainCard(),
SizedBox(height: 16.h),
_buildPulseCard(),
SizedBox(height: 16.h),
_buildTimeCard(),
],
),
),
);
}
导航栏的设计和体重页面一致,保持App内的一致性。用户学会一个页面的操作后,其他页面也能快速上手。
血压主卡片
这是页面的核心部分,左右两列分别显示收缩压和舒张压。
Widget _buildMainCard() {
return Container(
padding: EdgeInsets.all(24.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.r)
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildValueColumn('收缩压', _systolic, 'mmHg',
(v) => setState(() => _systolic = v)),
Container(
margin: EdgeInsets.symmetric(horizontal: 20.w),
child: Text('/', style: TextStyle(
fontSize: 40.sp,
color: Colors.grey[300]
)),
),
_buildValueColumn('舒张压', _diastolic, 'mmHg',
(v) => setState(() => _diastolic = v)),
],
),
中间用一个大号的斜杠分隔两个数值,这是血压的标准写法(如 120/80)。斜杠用浅灰色,不会抢夺数值的注意力。
血压状态判断
根据血压值显示当前状态是否正常。
SizedBox(height: 20.h),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
decoration: BoxDecoration(
color: const Color(0xFF00C9A7).withOpacity(0.12),
borderRadius: BorderRadius.circular(20.r),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check_circle_outline_rounded, size: 16.w,
color: const Color(0xFF00C9A7)),
SizedBox(width: 6.w),
Text('血压正常', style: TextStyle(
fontSize: 13.sp,
color: const Color(0xFF00C9A7),
fontWeight: FontWeight.w500
)),
],
),
),
],
),
);
}
状态标签用图标加文字的组合,比纯文字更醒目。实际项目中应该根据血压值动态判断状态:
- 收缩压 < 120 且 舒张压 < 80:正常
- 收缩压 120-139 或 舒张压 80-89:偏高
- 收缩压 >= 140 或 舒张压 >= 90:高血压
数值调整组件
每个血压值用一个可复用的组件来渲染,包含标签、加减按钮、数值显示、单位。
Widget _buildValueColumn(String label, int value, String unit, Function(int) onChanged) {
return Column(
children: [
Text(label, style: TextStyle(fontSize: 13.sp, color: Colors.grey[500])),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => onChanged(value - 1),
child: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8.r)
),
child: Icon(Icons.remove, size: 18.w, color: Colors.grey[600]),
),
),
SizedBox(width: 16.w),
Text('$value', style: TextStyle(
fontSize: 42.sp,
fontWeight: FontWeight.w700,
color: const Color(0xFF1A1A2E)
)),
SizedBox(width: 16.w),
GestureDetector(
onTap: () => onChanged(value + 1),
child: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFF6C63FF).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r)
),
child: Icon(Icons.add, size: 18.w, color: const Color(0xFF6C63FF)),
),
),
],
),
SizedBox(height: 4.h),
Text(unit, style: TextStyle(fontSize: 12.sp, color: Colors.grey[400])),
],
);
}
减号按钮用灰色背景,加号按钮用主题色背景,视觉上有区分。这种设计暗示"加"是更常用的操作。
onChanged 回调让这个组件可以复用,收缩压和舒张压只是传入不同的状态更新函数。
脉搏卡片
脉搏是可选的附加信息,用一个单独的卡片展示。
Widget _buildPulseCard() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r)
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: const Color(0xFFFF6B6B).withOpacity(0.12),
borderRadius: BorderRadius.circular(12.r)
),
child: Icon(Icons.favorite_border_rounded, size: 22.w,
color: const Color(0xFFFF6B6B)),
),
SizedBox(width: 14.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('脉搏', style: TextStyle(
fontSize: 13.sp,
color: Colors.grey[500]
)),
SizedBox(height: 2.h),
Text('$_pulse bpm', style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF1A1A2E)
)),
],
),
),
左边用心形图标,红色主题,和血压的绿色形成对比。脉搏用 bpm(beats per minute)作为单位。
脉搏调整按钮
脉搏的加减按钮放在卡片右侧,比血压的按钮小一些。
Row(
children: [
GestureDetector(
onTap: () => setState(() => _pulse--),
child: Container(
padding: EdgeInsets.all(6.w),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(6.r)
),
child: Icon(Icons.remove, size: 16.w, color: Colors.grey[600])
),
),
SizedBox(width: 12.w),
GestureDetector(
onTap: () => setState(() => _pulse++),
child: Container(
padding: EdgeInsets.all(6.w),
decoration: BoxDecoration(
color: const Color(0xFF6C63FF).withOpacity(0.1),
borderRadius: BorderRadius.circular(6.r)
),
child: Icon(Icons.add, size: 16.w, color: const Color(0xFF6C63FF))
),
),
],
),
],
),
);
}
按钮尺寸用 6.w 的 padding 和 16.w 的图标,比血压按钮的 8.w 和 18.w 小一号。这种大小差异暗示脉搏是次要信息。
时间卡片
显示记录时间,和体重页面的时间选择器一样。
Widget _buildTimeCard() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r)
),
child: Row(
children: [
Icon(Icons.access_time_rounded, size: 22.w, color: Colors.grey[600]),
SizedBox(width: 12.w),
Expanded(
child: Text('今天 ${TimeOfDay.now().format(context)}', style: TextStyle(
fontSize: 15.sp,
color: const Color(0xFF1A1A2E)
))
),
Icon(Icons.chevron_right_rounded, size: 20.w, color: Colors.grey[400]),
],
),
);
}
}
这个组件在多个添加页面中复用,保持一致的交互模式。用户知道点击这里可以修改时间。
数值范围限制
当前的实现没有限制数值范围,用户可以把血压调成负数或者超大的数。实际项目中应该加上限制:
GestureDetector(
onTap: () {
if (value > 60) { // 收缩压最小值
onChanged(value - 1);
}
},
// ...
),
合理的范围大概是:
- 收缩压:60-250 mmHg
- 舒张压:40-150 mmHg
- 脉搏:40-200 bpm
超出这个范围的数值在医学上基本不可能出现。
长按快速调整
点击一次只能加减 1,如果要从 120 调到 150,需要点 30 次。可以加上长按连续调整的功能:
GestureDetector(
onTap: () => onChanged(value + 1),
onLongPressStart: (_) {
_timer = Timer.periodic(const Duration(milliseconds: 100), (_) {
onChanged(value + 1);
});
},
onLongPressEnd: (_) {
_timer?.cancel();
},
// ...
),
长按时每 100 毫秒增加 1,松开时停止。这个功能可以大大提升输入效率。
小结
血压记录页面的特点:
- 同时记录收缩压和舒张压两个数值
- 用加减按钮调整,适合整数输入
- 附带脉搏记录功能
- 根据数值显示血压状态
加减按钮的交互方式比滑块更精确,适合血压这种需要准确数值的场景。滑块适合体重这种允许一定误差的场景。
下一篇会讲添加血糖记录页面,血糖除了数值还需要选择测量时间(空腹/餐后等)。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)