Flutter:如何按日期对API数据进行ListView分组展示
实现日期分组的列表展示效果
1. 定义数据模型
先为API返回的数据定义对应的实体类:
class Item { final DateTime date; final String title; final String subtitle; Item({required this.date, required this.title, required this.subtitle}); }
2. 对API数据做分组处理
从API拿到数据后,按「本月」和「历史年月」维度完成分组:
// 模拟API返回的原始数据 List<Item> apiData = [ Item(date: DateTime.now(), title: "今日事项1", subtitle: "详情1"), Item(date: DateTime.now().subtract(Duration(days: 10)), title: "上旬事项", subtitle: "详情2"), Item(date: DateTime(2022,12,15), title: "2022年12月事项", subtitle: "详情3"), Item(date: DateTime(2021,11,20), title: "2021年11月事项", subtitle: "详情4"), ]; // 分组逻辑实现 Map<String, List<Item>> groupItems() { Map<String, List<Item>> groups = {}; DateTime now = DateTime.now(); DateTime currentMonth = DateTime(now.year, now.month); // 筛选本月数据 List<Item> currentMonthItems = apiData.where((item) { DateTime itemMonth = DateTime(item.date.year, item.date.month); return itemMonth.isAtSameMomentAs(currentMonth); }).toList(); if (currentMonthItems.isNotEmpty) { groups["本月"] = currentMonthItems; } // 处理历史月份数据 for (var item in apiData) { DateTime itemMonth = DateTime(item.date.year, item.date.month); if (itemMonth.isBefore(currentMonth)) { String groupKey = "${item.date.year}年${item.date.month}月"; groups.putIfAbsent(groupKey, () => []); groups[groupKey]!.add(item); } } // 对分组排序:本月在前,历史月份按时间倒序排列 List<String> sortedKeys = groups.keys.toList() ..remove("本月") ..sort((a, b) { DateTime aDate = DateTime.parse(a.replaceAll("年", "-").replaceAll("月", "")); DateTime bDate = DateTime.parse(b.replaceAll("年", "-").replaceAll("月", "")); return bDate.compareTo(aDate); }); if (groups.containsKey("本月")) { sortedKeys.insert(0, "本月"); } // 构建有序分组Map Map<String, List<Item>> sortedGroups = {}; for (var key in sortedKeys) { sortedGroups[key] = groups[key]!; } return sortedGroups; }
3. 构建分组列表UI
将分组后的数据渲染为「标题+ListTile」的结构:
class GroupedDateList extends StatelessWidget { final Map<String, List<Item>> groupedItems; const GroupedDateList({super.key, required this.groupedItems}); @override Widget build(BuildContext context) { List<Widget> listWidgets = []; groupedItems.forEach((key, items) { // 添加分组标题 listWidgets.add(Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Colors.grey[200], child: Text( key, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), )); // 添加分组下的ListTile for (var item in items) { listWidgets.add(ListTile( title: Text(item.title), subtitle: Text(item.subtitle), onTap: () { // 点击事件逻辑 }, )); } }); return ListView(children: listWidgets); } }
4. 页面中使用组件
在页面里调用分组方法并渲染最终UI:
@override Widget build(BuildContext context) { Map<String, List<Item>> groups = groupItems(); return Scaffold( appBar: AppBar(title: Text("日期分组列表")), body: GroupedDateList(groupedItems: groups), ); }
便捷方案:使用第三方包
如果不想手动写分组逻辑,可借助grouped_list包简化开发:
- 在
pubspec.yaml添加依赖:
dependencies: grouped_list: ^4.2.0
- 直接使用组件:
GroupedListView<Item, String>( elements: apiData, groupBy: (item) { DateTime now = DateTime.now(); DateTime currentMonth = DateTime(now.year, now.month); DateTime itemMonth = DateTime(item.date.year, item.date.month); return itemMonth.isAtSameMomentAs(currentMonth) ? "本月" : "${item.date.year}年${item.date.month}月"; }, groupComparator: (value1, value2) { if (value1 == "本月") return -1; if (value2 == "本月") return 1; DateTime a = DateTime.parse(value1.replaceAll("年", "-").replaceAll("月", "")); DateTime b = DateTime.parse(value2.replaceAll("年", "-").replaceAll("月", "")); return b.compareTo(a); }, itemComparator: (item1, item2) => item2.date.compareTo(item1.date), groupSeparatorBuilder: (String value) => Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Colors.grey[200], child: Text(value, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), ), itemBuilder: (context, Item item) => ListTile( title: Text(item.title), subtitle: Text(item.subtitle), ), )
内容的提问来源于stack exchange,提问作者Konstantinos Rotsidis
相关产品推荐
相关产品推荐

