flutter_for_openharmony家庭药箱管理app实战+过期提醒实现
药箱管理应用通过过期提醒功能帮助用户识别过期药品,分为"已过期"和"即将过期(30天内)"两个标签页。界面采用DefaultTabController实现标签切换,药品卡片通过颜色区分状态(红色表示已过期,橙色表示即将过期),并显示药品名称、有效期及剩余天数。空状态时显示相应提示图标和文字。底层通过Provider筛选药品数据,Medicine模型自动计算过
过期提醒是药箱管理应用的核心功能之一。药品过期后不仅失去疗效,还可能产生有害物质。通过过期提醒功能,用户可以及时发现并处理过期或即将过期的药品。
功能设计思路
过期提醒页面分为两个Tab:已过期和即将过期。已过期Tab显示所有过期药品,即将过期Tab显示30天内将要过期的药品。使用不同颜色区分两种状态,让用户能够快速识别紧急程度。
Tab页面结构
使用DefaultTabController实现Tab切换:
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Text('过期提醒'),
bottom: const TabBar(
tabs: [
Tab(text: '已过期'),
Tab(text: '即将过期'),
],
),
),
body: Consumer<MedicineProvider>(
builder: (context, provider, child) {
return TabBarView(
children: [
_buildMedicineList(provider.expiredMedicines, true),
_buildMedicineList(provider.expiringSoonMedicines, false),
],
);
},
),
),
);
}
DefaultTabController管理Tab状态,length: 2表示有两个Tab。AppBar的bottom属性放置TabBar,定义两个Tab标签。TabBarView包含两个子页面,分别显示已过期和即将过期的药品。使用Consumer监听Provider,当药品数据变化时自动更新。
药品列表构建
根据是否过期显示不同的空状态:
Widget _buildMedicineList(List<Medicine> medicines, bool isExpired) {
if (medicines.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
isExpired ? Icons.check_circle : Icons.access_time,
size: 64.sp,
color: Colors.grey[300],
),
SizedBox(height: 16.h),
Text(
isExpired ? '没有过期药品' : '没有即将过期的药品',
style: TextStyle(color: Colors.grey[500], fontSize: 16.sp),
),
],
),
);
}
return ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: medicines.length,
itemBuilder: (context, index) {
final medicine = medicines[index];
return _buildMedicineCard(medicine, isExpired);
},
);
}
列表为空时显示友好的空状态提示。已过期Tab显示对勾图标和"没有过期药品",即将过期Tab显示时钟图标和"没有即将过期的药品"。有数据时使用ListView.builder构建列表。
药品卡片设计
每个药品卡片显示完整的过期信息:
Widget _buildMedicineCard(Medicine medicine, bool isExpired) {
final color = isExpired ? Colors.red : Colors.orange;
final statusText = isExpired
? '已过期 ${-medicine.daysUntilExpiry} 天'
: '${medicine.daysUntilExpiry} 天后过期';
return GestureDetector(
onTap: () => Get.to(() => MedicineDetailScreen(medicine: medicine)),
child: Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
border: Border(left: BorderSide(color: color, width: 4.w)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Container(
width: 48.w,
height: 48.w,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
isExpired ? Icons.warning : Icons.access_time,
color: color,
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
medicine.name,
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
SizedBox(height: 4.h),
Text(
'有效期至: ${DateFormat('yyyy-MM-dd').format(medicine.expiryDate)}',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.r),
),
child: Text(
statusText,
style: TextStyle(fontSize: 10.sp, color: color),
),
),
],
),
),
);
}
卡片左侧有一条彩色边框,已过期使用红色,即将过期使用橙色。这种设计让用户能够快速识别药品状态。左侧图标也使用对应颜色,已过期显示警告图标,即将过期显示时钟图标。
状态文字计算:已过期药品显示"已过期X天",使用负数的剩余天数。即将过期药品显示"X天后过期",使用正数的剩余天数。这种表述方式直观明了,用户一眼就能看出紧急程度。
信息展示:中间区域显示药品名称和有效期日期。名称使用加粗字体,是卡片中最重要的信息。有效期使用灰色小字体,作为补充说明。右侧的状态标签使用圆角矩形,背景色和文字颜色都使用状态颜色。
Provider过期逻辑
Provider中实现过期判断:
List<Medicine> get expiredMedicines =>
_medicines.where((m) => m.isExpired).toList();
List<Medicine> get expiringSoonMedicines =>
_medicines.where((m) => m.isExpiringSoon).toList();
expiredMedicines筛选所有已过期的药品,expiringSoonMedicines筛选即将过期的药品。这两个计算属性每次访问时都会重新筛选,确保数据始终是最新的。
Medicine模型判断
Medicine模型中定义过期判断逻辑:
bool get isExpired => DateTime.now().isAfter(expiryDate);
bool get isExpiringSoon {
final daysUntilExpiry = expiryDate.difference(DateTime.now()).inDays;
return daysUntilExpiry <= 30 && daysUntilExpiry > 0;
}
int get daysUntilExpiry => expiryDate.difference(DateTime.now()).inDays;
isExpired判断当前时间是否晚于有效期。isExpiringSoon判断剩余天数是否在0到30天之间。daysUntilExpiry计算剩余天数,正数表示未过期,负数表示已过期。
颜色语义化
使用颜色传达状态信息是一种有效的设计手法。红色代表危险和警告,用于已过期药品。橙色代表注意和提醒,用于即将过期药品。这种颜色编码符合用户的认知习惯,无需文字说明就能理解状态的严重程度。
左侧边框设计
卡片左侧的彩色边框是一个巧妙的设计。它不会占用太多空间,但能够有效地区分不同状态。用户浏览列表时,通过边框颜色就能快速识别哪些药品需要优先处理。这种设计在很多应用中都有使用,是一种成熟的UI模式。
日期格式化
使用intl包的DateFormat格式化日期:
'有效期至: ${DateFormat('yyyy-MM-dd').format(medicine.expiryDate)}'
DateFormat提供了灵活的日期格式化功能。'yyyy-MM-dd’格式输出"年-月-日",这是中国用户习惯的日期格式。格式化后的日期清晰易读,避免了时间戳等不友好的显示方式。
过期天数计算
计算药品距离过期的天数:
int get daysUntilExpiry {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final expiry = DateTime(expiryDate.year, expiryDate.month, expiryDate.day);
return expiry.difference(today).inDays;
}
String get expiryStatusText {
final days = daysUntilExpiry;
if (days < 0) {
return '已过期 ${-days} 天';
} else if (days == 0) {
return '今天过期';
} else if (days <= 7) {
return '$days 天后过期';
} else if (days <= 30) {
return '$days 天后过期';
} else {
return '${(days / 30).floor()} 个月后过期';
}
}
计算时先将日期标准化到当天0点,避免时间部分影响计算结果。根据剩余天数返回不同的文字描述,让用户更直观地了解过期状态。超过30天的显示月数,避免数字过大。
批量操作功能
支持批量删除过期药品:
Widget _buildBatchActions(List<Medicine> medicines) {
return Container(
padding: EdgeInsets.all(16.w),
child: Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () => _showBatchDeleteDialog(medicines),
icon: const Icon(Icons.delete_sweep),
label: Text('批量删除 (${medicines.length})'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 12.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
),
),
],
),
);
}
void _showBatchDeleteDialog(List<Medicine> medicines) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('批量删除'),
content: Text('确定要删除这 ${medicines.length} 种过期药品吗?此操作不可撤销。'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
for (var medicine in medicines) {
context.read<MedicineProvider>().deleteMedicine(medicine.id);
}
Navigator.pop(context);
Get.snackbar('成功', '已删除 ${medicines.length} 种过期药品');
},
child: const Text('删除', style: TextStyle(color: Colors.red)),
),
],
),
);
}
批量删除功能让用户可以一次性清理所有过期药品,避免逐个删除的繁琐操作。删除前显示确认对话框,防止误操作。删除后显示成功提示,让用户知道操作已完成。
过期提醒通知
设置过期提醒通知:
void _scheduleExpiryNotifications() {
final medicines = context.read<MedicineProvider>().expiringSoonMedicines;
for (var medicine in medicines) {
final daysUntil = medicine.daysUntilExpiry;
if (daysUntil == 7) {
_showNotification(
'药品即将过期',
'${medicine.name} 将在7天后过期,请及时处理',
);
} else if (daysUntil == 3) {
_showNotification(
'药品即将过期',
'${medicine.name} 将在3天后过期,请尽快处理',
);
} else if (daysUntil == 1) {
_showNotification(
'药品明天过期',
'${medicine.name} 明天就要过期了,请立即处理',
);
}
}
}
在药品过期前7天、3天、1天分别发送提醒通知。通知内容包含药品名称和剩余天数,让用户明确知道需要处理哪些药品。这种渐进式提醒确保用户不会错过重要信息。
过期统计信息
显示过期药品的统计信息:
Widget _buildExpiryStats(List<Medicine> expired, List<Medicine> expiringSoon) {
return Container(
margin: EdgeInsets.all(16.w),
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: Row(
children: [
Expanded(
child: _buildStatItem(
'已过期',
expired.length,
Colors.red,
Icons.warning,
),
),
Container(width: 1, height: 50.h, color: Colors.grey[300]),
Expanded(
child: _buildStatItem(
'即将过期',
expiringSoon.length,
Colors.orange,
Icons.access_time,
),
),
],
),
);
}
Widget _buildStatItem(String label, int count, Color color, IconData icon) {
return Column(
children: [
Icon(icon, color: color, size: 24.sp),
SizedBox(height: 8.h),
Text(
'$count',
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: color,
),
),
Text(label, style: TextStyle(fontSize: 12.sp, color: Colors.grey[600])),
],
);
}
统计卡片显示已过期和即将过期的药品数量,使用不同颜色区分。这让用户能够快速了解整体情况,决定优先处理哪些药品。
性能优化
过期提醒功能的性能优化包括:
- 计算属性缓存:过期判断使用计算属性,避免重复计算
- 列表排序:按过期紧急程度排序,最紧急的排在前面
- 懒加载:ListView.builder只构建可见的卡片
- 条件渲染:空状态和有数据状态使用条件渲染
用户体验设计
过期提醒的用户体验设计包括:
- 颜色编码:红色表示已过期,橙色表示即将过期
- 左侧边框:快速识别药品状态
- 状态标签:显示具体的过期天数
- 批量操作:支持一次性删除多个过期药品
- 渐进提醒:在过期前多次提醒用户
总结
过期提醒功能通过Tab分页和颜色编码,让用户能够快速识别和处理过期药品。左侧边框和状态标签提供了清晰的视觉提示,日期格式化让信息易于理解。Provider管理过期判断逻辑,保持代码的清晰和可维护性。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)