如何基于日期排序数据并避免前端日期重复展示
解决方案
要实现按日期分组、仅显示一次日期的效果,需要分数据处理和UI组件调整两部分修改代码:
1. 数据处理:按日期分组并排序
首先需要将原始薪资数据按日期分组,同时保证日期有序。推荐在Provider中提前处理,避免build方法重复计算:
修改Provider添加分组逻辑
class EmployeeWageAccountsProvider extends ChangeNotifier { List<EmployeeWageAccountsModel>? _employeeAccountsData; // 新增:获取分组并排序后的薪资数据 Map<String, List<EmployeeWageAccountsModel>>? get groupedSortedWageData { if (_employeeAccountsData == null) return null; // 1. 按日期排序原始数据 final sortedList = List.from(_employeeAccountsData!) ..sort((a, b) => a.date!.compareTo(b.date!)); // 2. 按日期分组 final Map<String, List<EmployeeWageAccountsModel>> groupedData = {}; for (final item in sortedList) { groupedData.putIfAbsent(item.date!, () => []).add(item); } return groupedData; } // 其他原有代码... }
如果不想修改Provider,也可以在列表构建时临时处理:
// 在Consumer内部执行 var wageAccountsData = data.getEmployeeAccountsData!; // 排序 wageAccountsData.sort((a, b) => a.date!.compareTo(b.date!)); // 分组 Map<String, List<EmployeeWageAccountsModel>> groupedData = {}; for (var item in wageAccountsData) { groupedData.putIfAbsent(item.date!, () => []).add(item); } // 转为有序列表 var sortedGroups = groupedData.entries.toList() ..sort((a, b) => a.key.compareTo(b.key));
2. 拆分UI组件:分离组头和明细项
原有组件同时包含日期和明细,现在拆分为两个独立组件:
组头组件:WageGroupHeader
class WageGroupHeader extends StatelessWidget { final String date; final String shiftName; const WageGroupHeader({ Key? key, required this.date, required this.shiftName, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(top: 10, left: 10, right: 10), padding: const EdgeInsets.all(8), decoration: CustomBoxDecoration.cardDecoration(context, shadow: true), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(shiftName, style: wageTextStyle), Text(date, style: wageTextStyle), ], ), ); } }
明细项组件:WageItem
class WageItem extends StatelessWidget { final String name; final String balance; const WageItem({ Key? key, required this.name, required this.balance, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(left: 10, right: 10, bottom: 10), padding: const EdgeInsets.all(8), decoration: CustomBoxDecoration.cardDecoration(context, shadow: true), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(S.of(context).wage_type, style: cTextStyle), Text(S.of(context).balance, style: cTextStyle), ], ), const Divider(color: Colors.black), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(name, style: cTextStyle), Text(balance, style: cTextStyle), ], ), SizedBox(height: Styles.height(context) * 0.01), ], ), ); } }
3. 修改列表构建代码
使用分组后的数据构建ListView,每个分组对应一个组头+多个明细项:
Expanded( child: Consumer<EmployeeWageAccountsProvider>( builder: (context, data, child) { if (!data.isLoading) { final groupedData = data.groupedSortedWageData; if (groupedData != null && groupedData.isNotEmpty) { final sortedGroups = groupedData.entries.toList() ..sort((a, b) => a.key.compareTo(b.key)); return ListView.builder( itemCount: sortedGroups.length, shrinkWrap: true, scrollDirection: Axis.vertical, itemBuilder: (context, groupIndex) { final group = sortedGroups[groupIndex]; final formattedDate = Helper.formatStringDate(group.key); // 取该组第一个非空班次(同一日期班次应一致) final shiftName = group.value .firstWhere((item) => item.shiftName!.isNotEmpty, orElse: () => group.value.first) .shiftName!; return Column( children: [ WageGroupHeader(date: formattedDate, shiftName: shiftName), ...group.value.map((item) => WageItem(name: item.name!, balance: item.balance!)) .toList(), ], ); }, ); } return noDataFound(context, 50); } return const WageAccountsShimmer(); }, ), )
关键说明
- 分组逻辑保证同一日期的薪资项归为一组,避免日期重复显示;
- 排序逻辑确保列表按日期升序展示,如需降序可修改
compareTo的顺序; - 拆分组件后UI结构更清晰,方便后续单独调整样式;
- 班次处理:若同一日期班次唯一,可直接取
group.value.first.shiftName!。
内容的提问来源于stack exchange,提问作者Arslan
相关产品推荐
相关产品推荐

