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

Flutter中GridView.builder如何正确使用自定义CategoryItems组件

Flutter GridView自定义分类卡片渲染异常修复

问题现状

  • 已完成基础能力:首页通过GridView.builder渲染5个分类卡片,点击跳转逻辑运行正常
  • 异常表现:无法在GridView的child属性中正确接入自定义模型类,不能实现每个卡片展示对应名称与图标;当前临时在child中写死固定Column组件,硬编码Icons.add图标和'name'占位文本,只要尝试传入分类模型的icon属性就会触发报错

现有代码片段

首页实现代码

Widget build(BuildContext context) {
return Scaffold(
    appBar: AppBar(
      backgroundColor: Colors.deepOrange,
      title: Text('Pocket Chef'),
      centerTitle: true,
    ),
    body: GridView.builder(
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      gridDelegate:
      SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
      itemCount: DUMMY_CATEGORIES
          .map((categoryItem) =>
          CategoryItems(categoryItem.name, categoryItem.icon))
          .toList()
          .length,
      itemBuilder: (_, index) {
        return InkWell(
          onTap: () {
            if (index == 0) {
              Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => Screen1()));
            } else if (index == 1) {
              Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => Screen2()));
            }
            else if (index == 2) {
              Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => Screen3()));
            
          },
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Icon(Icons.add, size: 60,),
              Text('name'),
            ],
          ),
        );
      },
    )

CategoryItems组件类代码

class CategoryItems extends StatelessWidget {
 final String name;
 final IconData icon;

CategoryItems(this.name, this.icon);

@override
Widget build(BuildContext context) {
return Card(
  elevation: 10.0,
  child: InkWell(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Icon(icon, size: 60,),
        Text(name),
      ],
      ),
     ),
   );
  }
}

模拟分类列表数据代码

import 'package:flutter/material.dart';
import 'package:flutter_projects/models/category.dart';

const DUMMY_CATEGORIES = const [
Category(
  name: "Calcolo W", id: "1", icon: Icons.food_bank),
Category(
  name: "Farina", id: "2", icon: Icons.food_bank ),
Category(
  name: "Alcol", id: "3", icon: Icons.food_bank),
Category(
  name: "Panna", id: "4", icon: Icons.food_bank),
Category(
  name: "Cioccolato", id: "5", icon: Icons.face)
];

效果参考

  • 当前实现效果
    当前实现效果
  • 预期实现效果
    预期实现效果

修复方案

核心问题共3处:

  1. itemCount写法冗余无效:不需要提前map转组件列表取长度,直接读取DUMMY_CATEGORIES.length即可,原有map操作属于无效计算,生成的组件列表完全没有被复用
  2. 未按索引读取模型数据:itemBuilder回调的index参数就是当前渲染项的位置,直接通过DUMMY_CATEGORIES[index]就能拿到对应分类的模型数据
  3. 组件重复封装:已经封装好CategoryItems卡片组件,不需要在itemBuilder里重复写Column和InkWell,给CategoryItems加个可选的onTap回调参数接收点击逻辑即可,避免两层InkWell导致的事件冲突

第一步先修改CategoryItems组件,增加点击回调参数:

class CategoryItems extends StatelessWidget {
 final String name;
 final IconData icon;
 final Function()? onTap;

 CategoryItems(this.name, this.icon, {this.onTap});

 @override
 Widget build(BuildContext context) {
  return Card(
    elevation: 10.0,
    child: InkWell(
      onTap: onTap,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Icon(icon, size: 60,),
          Text(name),
        ],
      ),
    ),
  );
 }
}

第二步修改首页的GridView.builder代码:

GridView.builder(
  shrinkWrap: true,
  physics: NeverScrollableScrollPhysics(),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
  // 直接取数据源长度
  itemCount: DUMMY_CATEGORIES.length,
  itemBuilder: (_, index) {
    // 按索引取出当前项对应的分类模型
    final currentCategory = DUMMY_CATEGORIES[index];
    // 直接调用封装好的组件,传入对应参数
    return CategoryItems(
      currentCategory.name,
      currentCategory.icon,
      onTap: () {
        // 保留原有跳转逻辑,剩余索引的跳转规则自行补充即可
        if (index == 0) {
          Navigator.of(context).push(MaterialPageRoute(builder: (context) => Screen1()));
        } else if (index == 1) {
          Navigator.of(context).push(MaterialPageRoute(builder: (context) => Screen2()));
        } else if (index == 2) {
          Navigator.of(context).push(MaterialPageRoute(builder: (context) => Screen3()));
        }
      },
    );
  },
)

内容的提问来源于stack exchange,提问作者Giovanni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 23:42:20