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处:
itemCount写法冗余无效:不需要提前map转组件列表取长度,直接读取DUMMY_CATEGORIES.length即可,原有map操作属于无效计算,生成的组件列表完全没有被复用- 未按索引读取模型数据:
itemBuilder回调的index参数就是当前渲染项的位置,直接通过DUMMY_CATEGORIES[index]就能拿到对应分类的模型数据 - 组件重复封装:已经封装好
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
相关产品推荐
相关产品推荐

