Flutter中如何在build方法结合groupBy使用ListView.builder
Flutter按日期分组展示列表解决方案
问题场景
已有ProductModel列表,通过groupBy按日期完成分组后,需要在ListView.builder中按日期分组展示商品名称,当前代码中无法正确渲染分组内的商品列表。
原列表:
List<ProductModel> productList = [ ProductModel(name: "Oranges", date: "2023-05-01"), ProductModel(name: "Apples", date: "2023-05-01"), ProductModel(name: "Kiwis", date: "2023-03-01"), ];
期望展示效果:
2023-05-01: Oranges Apples 2023-03-01: Kiwis
分组后groupByDate打印结果:
{2023-05-01: [Instance of 'ProductModel', Instance of 'ProductModel'], 2023-03-01: [Instance of 'ProductModel']}
原代码:
class ProductListPage extends StatefulWidget { @override State<StatefulWidget> createState() => new _ProductListPageState(); } class _ProductListPageState extends State<ProductListPage> { late StreamController<int> _postsController; var groupByDate; @override void initState() { _postsController = new StreamController(); getProducts(); super.initState(); } void getProducts() { Fetch.getProdcuts().then((value) => { _postsController.add(1), List<ProductModel> productList = []; for (var item in value!) { final encode = json.encode(item.toJson()); final product = ProductModel.fromJson(jsonDecode(encode)); productList.add(product); } groupByDate = groupBy(productList, (obj) => (obj! as ProductModel).date); }); } @override Widget build(BuildContext context) { return Scaffold( body: ListView.builder( itemCount: groupByDate.length, itemBuilder: (context, index) { final key = groupByDate.keys.elementAt(index); final values = groupByDate.values.elementAt(index); return Column( children: [ Text("$key"), Column( children: [ //How do I show the list with values here??? entry.value.map((e) => Text(e.name)).toList(), ]) ]) } ) ); } } class ProductModel extends Object { String? name; String? date; ProductModel({this.name, this.date}); ProductModel.fromJson(Map<String, dynamic> json) { name = json["name"]; date = json["date"]; } Map<String, dynamic> toJson() => { 'name': name, 'date': date, }; }
解决方案
关键问题修正
- 变量名错误:原代码误用未定义的
entry.value,应使用已获取的values变量 - 状态更新:数据分组完成后必须调用
setState触发UI刷新 - 空值处理:
ProductModel的name为可空类型,需处理空值避免异常 - 初始化避免空指针:
groupByDate初始化为空Map,防止未加载时的空指针异常 - 优化异步逻辑:移除未实际使用的
StreamController,简化代码
修正后的完整代码
import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; // 引入groupBy依赖 import 'dart:convert'; class ProductListPage extends StatefulWidget { @override State<StatefulWidget> createState() => _ProductListPageState(); } class _ProductListPageState extends State<ProductListPage> { // 初始化空Map,避免空指针 Map<String?, List<ProductModel>> groupByDate = {}; @override void initState() { super.initState(); getProducts(); } void getProducts() { Fetch.getProdcuts().then((value) { List<ProductModel> productList = []; for (var item in value!) { final encode = json.encode(item.toJson()); final product = ProductModel.fromJson(jsonDecode(encode)); productList.add(product); } // 分组后更新状态刷新UI setState(() { groupByDate = groupBy(productList, (obj) => obj.date); }); }); } @override Widget build(BuildContext context) { return Scaffold( body: ListView.builder( itemCount: groupByDate.length, itemBuilder: (context, index) { final key = groupByDate.keys.elementAt(index); final values = groupByDate.values.elementAt(index); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Text( "$key", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), // 展开商品列表 ...values.map((product) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: Text(product.name ?? "未知商品"), // 处理空名称 ); }).toList(), const Divider(height: 16), // 分隔不同日期组 ], ); }, ), ); } } class ProductModel { String? name; String? date; ProductModel({this.name, this.date}); ProductModel.fromJson(Map<String, dynamic> json) { name = json["name"]; date = json["date"]; } Map<String, dynamic> toJson() => { 'name': name, 'date': date, }; } // 模拟数据获取类 class Fetch { static Future<List<ProductModel>?> getProdcuts() async { await Future.delayed(const Duration(seconds: 1)); return [ ProductModel(name: "Oranges", date: "2023-05-01"), ProductModel(name: "Apples", date: "2023-05-01"), ProductModel(name: "Kiwis", date: "2023-03-01"), ]; } }
代码说明
- 使用
...展开运算符直接将商品列表Widget加入Column,避免嵌套冗余组件 - 添加内边距、字体样式和分隔线优化视觉效果
- 处理
name为空的情况,防止UI异常 - 移除无用的
StreamController,简化异步逻辑
内容的提问来源于stack exchange,提问作者BradG
相关产品推荐
相关产品推荐

