flutter_for_openharmony家庭药箱管理app实战+药品详情实现

药品详情页面是用户查看药品完整信息的重要入口。在这里,用户可以查看药品的所有属性,包括基本信息、有效期、用法用量等,还可以进行编辑、删除和库存调整操作。

页面功能设计

详情页面需要实现:药品信息展示状态提醒库存调整编辑删除操作。信息按照逻辑分组展示,状态卡片醒目提示需要关注的问题,库存调整提供快捷的增减操作。

页面结构实现

详情页面使用StatelessWidget,因为数据通过参数传入:

dart 复制代码
class MedicineDetailScreen extends StatelessWidget {
  final Medicine medicine;

  const MedicineDetailScreen({super.key, required this.medicine});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('药品详情'),
        actions: [
          IconButton(
            icon: const Icon(Icons.edit),
            onPressed: () => Get.to(() => EditMedicineScreen(medicine: medicine)),
          ),
          IconButton(
            icon: const Icon(Icons.delete),
            onPressed: () => _showDeleteDialog(context),
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildHeader(),
            _buildStatusCard(),
            _buildInfoSection('基本信息', [...]),
            _buildQuantityControl(context),
          ],
        ),
      ),
    );
  }
}

AppBar右侧提供编辑和删除按钮,让用户可以快速进行操作。页面主体使用SingleChildScrollView支持滚动,内容通过Column垂直排列。各个信息模块之间使用SizedBox添加间距,保持视觉上的分隔。

头部信息展示

头部展示药品名称和分类:

dart 复制代码
Widget _buildHeader() {
  return Row(
    children: [
      Container(
        width: 80.w,
        height: 80.w,
        decoration: BoxDecoration(
          color: const Color(0xFF00897B).withOpacity(0.1),
          borderRadius: BorderRadius.circular(16.r),
        ),
        child: Icon(
          Icons.medication,
          color: const Color(0xFF00897B),
          size: 40.sp,
        ),
      ),
      SizedBox(width: 16.w),
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              medicine.name,
              style: TextStyle(
                fontSize: 20.sp,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 4.h),
            Container(
              padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
              decoration: BoxDecoration(
                color: Colors.teal.withOpacity(0.1),
                borderRadius: BorderRadius.circular(10.r),
              ),
              child: Text(
                medicine.category,
                style: TextStyle(
                  fontSize: 12.sp,
                  color: Colors.teal,
                ),
              ),
            ),
          ],
        ),
      ),
    ],
  );
}

左侧是药品图标,使用80x80的正方形容器,主题色的半透明背景。右侧是药品名称和分类标签,名称使用20.sp的加粗字体,是页面中最醒目的文字。分类标签使用圆角矩形,背景色和文字颜色都使用青绿色,与应用主题保持一致。

状态提醒卡片

状态卡片根据药品状态显示不同的提示:

dart 复制代码
Widget _buildStatusCard() {
  Color statusColor;
  String statusText;
  IconData statusIcon;

  if (medicine.isExpired) {
    statusColor = Colors.red;
    statusText = '已过期,请及时处理';
    statusIcon = Icons.warning;
  } else if (medicine.isExpiringSoon) {
    statusColor = Colors.orange;
    statusText = '即将在${medicine.daysUntilExpiry}天后过期';
    statusIcon = Icons.access_time;
  } else if (medicine.quantity <= medicine.lowStockThreshold) {
    statusColor = Colors.amber;
    statusText = '库存不足,请及时补充';
    statusIcon = Icons.inventory_2;
  } else {
    statusColor = Colors.green;
    statusText = '状态正常';
    statusIcon = Icons.check_circle;
  }

  return Container(
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: statusColor.withOpacity(0.1),
      borderRadius: BorderRadius.circular(12.r),
      border: Border.all(color: statusColor.withOpacity(0.3)),
    ),
    child: Row(
      children: [
        Icon(statusIcon, color: statusColor, size: 24.sp),
        SizedBox(width: 12.w),
        Expanded(
          child: Text(
            statusText,
            style: TextStyle(
              fontSize: 14.sp,
              color: statusColor,
              fontWeight: FontWeight.w500,
            ),
          ),
        ),
      ],
    ),
  );
}

状态判断按照优先级进行:已过期最严重,其次是即将过期,再次是库存不足,最后是正常状态。每种状态使用不同的颜色、图标和文字,让用户能够快速识别。卡片使用半透明背景和边框,既醒目又不会过于突兀。

信息分组展示

信息按照逻辑分组,每组使用独立的卡片:

dart 复制代码
Widget _buildInfoSection(String title, List<Widget> children) {
  return Container(
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12.r),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.05),
          blurRadius: 10,
          offset: const Offset(0, 2),
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: TextStyle(
            fontSize: 16.sp,
            fontWeight: FontWeight.bold,
          ),
        ),
        SizedBox(height: 12.h),
        ...children,
      ],
    ),
  );
}

每个信息组都是一个白色卡片,带有轻微的阴影效果。标题使用16.sp的加粗字体,与内容形成层次对比。使用展开运算符...children将子组件列表展开,这种方式让代码更加简洁。

信息行组件

单行信息展示采用标签-值的形式:

dart 复制代码
Widget _buildInfoRow(String label, String value) {
  return Padding(
    padding: EdgeInsets.only(bottom: 8.h),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (label.isNotEmpty) ...[
          SizedBox(
            width: 80.w,
            child: Text(
              label,
              style: TextStyle(
                fontSize: 14.sp,
                color: Colors.grey[600],
              ),
            ),
          ),
        ],
        Expanded(
          child: Text(
            value,
            style: TextStyle(
              fontSize: 14.sp,
            ),
          ),
        ),
      ],
    ),
  );
}

标签固定宽度80.w,使用灰色字体,作为辅助说明。值使用Expanded占据剩余空间,使用默认黑色字体,是主要内容。当标签为空时不显示,这样可以用于展示长文本内容如副作用、禁忌等。

库存调整功能

库存调整提供加减按钮:

dart 复制代码
Widget _buildQuantityControl(BuildContext context) {
  return Container(
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12.r),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.05),
          blurRadius: 10,
          offset: const Offset(0, 2),
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          '库存管理',
          style: TextStyle(
            fontSize: 16.sp,
            fontWeight: FontWeight.bold,
          ),
        ),
        SizedBox(height: 12.h),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            IconButton(
              onPressed: () {
                if (medicine.quantity > 0) {
                  context.read<MedicineProvider>().updateQuantity(
                        medicine.id,
                        medicine.quantity - 1,
                      );
                }
              },
              icon: Container(
                padding: EdgeInsets.all(8.w),
                decoration: BoxDecoration(
                  color: Colors.grey[200],
                  shape: BoxShape.circle,
                ),
                child: const Icon(Icons.remove),
              ),
            ),
            SizedBox(width: 20.w),
            Consumer<MedicineProvider>(
              builder: (context, provider, child) {
                final currentMedicine = provider.getMedicineById(medicine.id);
                return Text(
                  '${currentMedicine?.quantity ?? medicine.quantity}${medicine.unit}',
                  style: TextStyle(
                    fontSize: 24.sp,
                    fontWeight: FontWeight.bold,
                  ),
                );
              },
            ),
            SizedBox(width: 20.w),
            IconButton(
              onPressed: () {
                context.read<MedicineProvider>().updateQuantity(
                      medicine.id,
                      medicine.quantity + 1,
                    );
              },
              icon: Container(
                padding: EdgeInsets.all(8.w),
                decoration: BoxDecoration(
                  color: const Color(0xFF00897B),
                  shape: BoxShape.circle,
                ),
                child: const Icon(Icons.add, color: Colors.white),
              ),
            ),
          ],
        ),
      ],
    ),
  );
}

减号按钮使用灰色背景,加号按钮使用主题色背景,形成视觉对比。中间的数量使用Consumer监听Provider,当数量变化时自动更新显示。减号按钮添加了判断,当数量为0时不能继续减少。按钮使用圆形容器包裹图标,提供更大的点击区域。

删除确认对话框

删除操作需要用户确认:

dart 复制代码
void _showDeleteDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('删除药品'),
      content: Text('确定要删除"${medicine.name}"吗?'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('取消'),
        ),
        TextButton(
          onPressed: () {
            context.read<MedicineProvider>().deleteMedicine(medicine.id);
            Navigator.pop(context);
            Get.back();
          },
          child: const Text('删除', style: TextStyle(color: Colors.red)),
        ),
      ],
    ),
  );
}

使用AlertDialog显示确认对话框,标题和内容清晰说明操作。提供取消和删除两个按钮,删除按钮使用红色文字,警示用户这是危险操作。确认删除后,调用Provider的deleteMedicine方法,关闭对话框,返回上一页。

响应式数据更新

库存调整使用Consumer实现响应式更新。当用户点击加减按钮时,Provider中的数据立即更新,Consumer监听到变化后重建Widget,显示最新的数量。这种机制让UI始终与数据保持同步,用户体验流畅。

总结

药品详情页面通过分组展示、状态提醒和快捷操作,为用户提供了完整的药品信息查看和管理功能。使用Provider实现响应式更新,确保数据的实时性。通过颜色编码和图标,让状态信息一目了然。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
木井巳2 小时前
【递归算法】求根节点到叶节点数字之和
java·算法·leetcode·深度优先
没有bug.的程序员2 小时前
Spring Boot 事务管理:@Transactional 失效场景、底层内幕与分布式补偿实战终极指南
java·spring boot·分布式·后端·transactional·失效场景·底层内幕
华农第一蒟蒻2 小时前
一次服务器CPU飙升的排查与解决
java·运维·服务器·spring boot·arthas
m0_748229992 小时前
帝国CMS后台搭建全攻略
java·c语言·开发语言·学习
We་ct2 小时前
LeetCode 15. 三数之和:排序+双指针解法全解析
前端·算法·leetcode·typescript
美狐美颜SDK开放平台2 小时前
直播场景下抖动特效的实现方案:美颜sdk开发经验分享
前端·人工智能·美颜sdk·直播美颜sdk·视频美颜sdk
xingfanjiuge2 小时前
Flutter框架跨平台鸿蒙开发——ListView.builder深度解析
flutter·华为·harmonyos
码农娟2 小时前
Hutool XML工具-XmlUtil的使用
xml·java
草青工作室2 小时前
java-FreeMarker3.4自定义异常处理
java·前端·python