#请添加图片描述

前言

血压是心血管健康的重要指标,需要同时记录收缩压(高压)和舒张压(低压)两个数值。这个页面的交互设计和体重页面不同,我们用加减按钮来调整数值,更适合整数输入的场景。

除了血压值,很多血压计还会显示脉搏,所以我们也加上脉搏记录功能。


状态变量定义

血压记录需要三个数值:收缩压、舒张压、脉搏。

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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐