You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,
  };
}

解决方案

关键问题修正

  1. 变量名错误:原代码误用未定义的entry.value,应使用已获取的values变量
  2. 状态更新:数据分组完成后必须调用setState触发UI刷新
  3. 空值处理:ProductModel的name为可空类型,需处理空值避免异常
  4. 初始化避免空指针:groupByDate初始化为空Map,防止未加载时的空指针异常
  5. 优化异步逻辑:移除未实际使用的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 15:52:05