药品分类功能帮助用户按照药品类型快速查找和管理药品。通过分类视图,用户可以一目了然地看到每个分类下有多少药品,点击分类可以查看该分类的所有药品。
分类功能设计
分类页面包含两个层级:分类网格视图 和分类详情列表。网格视图展示所有分类及药品数量,详情列表展示某个分类下的所有药品。使用图标和颜色区分不同分类,提升视觉识别度。
分类图标映射
为每个分类定义对应的图标:
dart
final categoryIcons = {
'感冒用药': Icons.sick,
'解热镇痛': Icons.thermostat,
'抗生素': Icons.medication_liquid,
'消化系统': Icons.restaurant,
'心血管': Icons.favorite,
'维生素': Icons.local_pharmacy,
'外用药': Icons.healing,
'中成药': Icons.spa,
'儿童用药': Icons.child_care,
'其他': Icons.more_horiz,
};
使用Map将分类名称映射到对应的图标。每个分类都有独特的图标,让用户能够快速识别。如果分类不在映射表中,使用默认的药品图标。
分类网格视图
使用GridView展示所有分类:
dart
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('药品分类'),
),
body: Consumer<MedicineProvider>(
builder: (context, provider, child) {
final categories = provider.categories;
if (categories.isEmpty) {
return Center(
child: Text('暂无药品分类', style: TextStyle(color: Colors.grey[500])),
);
}
return GridView.builder(
padding: EdgeInsets.all(16.w),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12.w,
mainAxisSpacing: 12.h,
childAspectRatio: 1.5,
),
itemCount: categories.length,
itemBuilder: (context, index) {
final category = categories[index];
final count = provider.getMedicinesByCategory(category).length;
final icon = categoryIcons[category] ?? Icons.medication;
return _buildCategoryCard(category, count, icon);
},
);
},
),
);
}
使用Consumer监听Provider,当药品数据变化时自动更新分类列表。GridView.builder创建网格布局,crossAxisCount: 2表示每行显示2个分类。childAspectRatio: 1.5设置卡片的宽高比,让卡片呈现横向矩形。
分类卡片设计
每个分类卡片显示图标、名称和数量:
dart
Widget _buildCategoryCard(String category, int count, IconData icon) {
return GestureDetector(
onTap: () => Get.to(() => CategoryDetailScreen(category: category)),
child: Container(
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(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 32.sp, color: const Color(0xFF00897B)),
SizedBox(height: 8.h),
Text(
category,
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500),
),
Text(
'$count 种',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[500]),
),
],
),
),
);
}
卡片采用垂直布局,从上到下依次是图标、分类名称、药品数量。图标使用主题色,大小为32.sp。分类名称使用中等粗细字体,数量使用灰色小字体。点击卡片跳转到分类详情页面。
分类详情页面
分类详情页面展示该分类下的所有药品:
dart
class CategoryDetailScreen extends StatelessWidget {
final String category;
const CategoryDetailScreen({super.key, required this.category});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(category),
),
body: Consumer<MedicineProvider>(
builder: (context, provider, child) {
final medicines = provider.getMedicinesByCategory(category);
if (medicines.isEmpty) {
return Center(
child: Text('该分类暂无药品', style: TextStyle(color: Colors.grey[500])),
);
}
return ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: medicines.length,
itemBuilder: (context, index) {
final medicine = medicines[index];
return _buildMedicineItem(medicine);
},
);
},
),
);
}
}
AppBar标题显示分类名称。使用Consumer监听Provider,调用getMedicinesByCategory方法获取该分类的药品列表。如果列表为空,显示友好的空状态提示。
药品列表项
详情页面的药品列表项使用ListTile:
dart
Widget _buildMedicineItem(Medicine medicine) {
return ListTile(
leading: Container(
width: 48.w,
height: 48.w,
decoration: BoxDecoration(
color: const Color(0xFF00897B).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(Icons.medication, color: const Color(0xFF00897B)),
),
title: Text(medicine.name),
subtitle: Text('库存: ${medicine.quantity}${medicine.unit}'),
trailing: const Icon(Icons.chevron_right),
onTap: () => Get.to(() => MedicineDetailScreen(medicine: medicine)),
);
}
ListTile左侧是药品图标,标题显示药品名称,副标题显示库存信息,右侧是箭头图标。点击后跳转到药品详情页面。
Provider分类逻辑
Provider中实现分类相关方法:
dart
List<String> get categories =>
_medicines.map((m) => m.category).toSet().toList();
List<Medicine> getMedicinesByCategory(String category) {
return _medicines.where((m) => m.category == category).toList();
}
categories属性通过map提取所有药品的分类,使用toSet()去重,再转换为列表。getMedicinesByCategory方法筛选指定分类的药品。这两个方法配合使用,实现了完整的分类功能。
网格布局优势
使用GridView展示分类有几个优势:可以在一屏内展示更多分类,减少滚动次数。网格布局视觉上更加紧凑,空间利用率高。每个分类卡片大小一致,看起来整齐美观。
空状态处理
分类页面和详情页面都处理了空状态。分类页面为空时提示"暂无药品分类",详情页面为空时提示"该分类暂无药品"。这种友好的提示让用户明确当前状态,不会感到困惑。
响应式更新
使用Consumer监听Provider,当药品数据变化时,分类列表和详情列表都会自动更新。比如添加新药品后,对应分类的数量会立即增加,新分类会自动出现在网格中。
分类颜色映射
为每个分类定义对应的颜色:
dart
final categoryColors = {
'感冒用药': Colors.blue,
'解热镇痛': Colors.red,
'抗生素': Colors.purple,
'消化系统': Colors.orange,
'心血管': Colors.pink,
'维生素': Colors.green,
'外用药': Colors.teal,
'中成药': Colors.brown,
'儿童用药': Colors.cyan,
'其他': Colors.grey,
};
Color _getCategoryColor(String category) {
return categoryColors[category] ?? const Color(0xFF00897B);
}
每个分类使用独特的颜色,让用户能够通过颜色快速识别分类。颜色选择考虑了语义关联,比如心血管使用粉红色(与心脏相关),消化系统使用橙色(与食物相关)。如果分类不在映射表中,使用默认的主题色。
分类卡片动画
为分类卡片添加点击动画效果:
dart
Widget _buildCategoryCard(String category, int count, IconData icon, Color color) {
return TweenAnimationBuilder<double>(
tween: Tween(begin: 1.0, end: 1.0),
duration: const Duration(milliseconds: 150),
builder: (context, scale, child) {
return Transform.scale(
scale: scale,
child: GestureDetector(
onTapDown: (_) => setState(() => _pressedCategory = category),
onTapUp: (_) {
setState(() => _pressedCategory = null);
Get.to(() => CategoryDetailScreen(category: category));
},
onTapCancel: () => setState(() => _pressedCategory = null),
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
transform: Matrix4.identity()..scale(_pressedCategory == category ? 0.95 : 1.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: color.withOpacity(_pressedCategory == category ? 0.2 : 0.1),
blurRadius: _pressedCategory == category ? 15 : 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(icon, size: 28.sp, color: color),
),
SizedBox(height: 8.h),
Text(category, style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500)),
Text('$count 种', style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
],
),
),
),
);
},
);
}
点击卡片时会有轻微的缩放效果,阴影也会相应变化。这种微交互让用户感受到操作的反馈,提升了用户体验。使用AnimatedContainer实现平滑的动画过渡。
分类详情页面头部
详情页面添加分类信息头部:
dart
Widget _buildCategoryHeader(String category, int count, Color color) {
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color, color.withOpacity(0.7)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
categoryIcons[category] ?? Icons.medication,
size: 32.sp,
color: Colors.white,
),
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
category,
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 4.h),
Text(
'共 $count 种药品',
style: TextStyle(fontSize: 14.sp, color: Colors.white70),
),
],
),
),
],
),
);
}
详情页面头部使用分类对应的颜色渐变背景,显示分类图标、名称和药品数量。这种设计让用户明确知道当前查看的是哪个分类,同时保持了视觉的一致性。
搜索过滤功能
在分类详情页面添加搜索功能:
dart
Widget _buildSearchBar() {
return Container(
margin: EdgeInsets.all(16.w),
padding: EdgeInsets.symmetric(horizontal: 16.w),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(24.r),
),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: '在此分类中搜索',
hintStyle: TextStyle(color: Colors.grey[400]),
border: InputBorder.none,
icon: Icon(Icons.search, color: Colors.grey[400]),
),
onChanged: (value) {
setState(() {
_filteredMedicines = _medicines
.where((m) => m.name.toLowerCase().contains(value.toLowerCase()))
.toList();
});
},
),
);
}
搜索框使用圆角矩形设计,与整体风格保持一致。输入时实时过滤药品列表,使用不区分大小写的包含匹配。这让用户能够在分类内快速找到目标药品。
排序功能
支持按不同条件排序药品:
dart
enum SortType { name, quantity, expiryDate }
void _sortMedicines(SortType type) {
setState(() {
switch (type) {
case SortType.name:
_filteredMedicines.sort((a, b) => a.name.compareTo(b.name));
break;
case SortType.quantity:
_filteredMedicines.sort((a, b) => a.quantity.compareTo(b.quantity));
break;
case SortType.expiryDate:
_filteredMedicines.sort((a, b) => a.expiryDate.compareTo(b.expiryDate));
break;
}
_currentSort = type;
});
}
用户可以按名称、库存数量或有效期排序药品。排序状态保存在_currentSort变量中,UI会显示当前的排序方式。这种功能让用户能够根据需要快速找到目标药品。
性能优化
分类功能的性能优化包括:
- 懒加载:GridView.builder只构建可见的卡片,减少内存占用
- 缓存计算:分类列表和数量在Provider中计算,避免重复计算
- 条件渲染:空状态和有数据状态使用条件渲染,减少不必要的Widget
- 图片缓存:如果使用分类图片,应该使用CachedNetworkImage缓存
用户体验设计
分类功能的用户体验设计包括:
- 视觉识别:每个分类使用独特的图标和颜色,便于快速识别
- 数量提示:卡片上显示药品数量,让用户了解分类规模
- 空状态处理:分类为空时显示友好提示,引导用户添加药品
- 搜索过滤:在分类内支持搜索,提高查找效率
- 排序功能:支持多种排序方式,满足不同需求
总结
药品分类功能通过网格视图和详情列表,为用户提供了直观的分类浏览体验。图标映射增强了视觉识别度,响应式更新确保数据的实时性。Provider管理分类逻辑,保持代码的清晰和可维护性。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net