Flutter开发:如何按分类筛选JSON数据并复用GridView Widget
实现Flutter分类筛选功能
需求说明
读取包含分类信息的JSON数据,按分类展示条目,复用现有buildItems Widget,仅通过修改分类名称切换展示内容;现有代码已实现全量条目展示及图片加载,需新增分类筛选功能。
实现步骤
1. 维护分类状态
在页面的State类中添加变量记录当前选中的分类:
String? _selectedCategory;
2. 实现数据筛选逻辑
添加计算属性,根据选中的分类过滤数据:
List<Items> get _filteredItems { if (_selectedCategory == null) { return items; // items是原始全量数据列表 } return items.where((item) => item.category == _selectedCategory).toList(); }
3. 添加分类选择UI
生成所有唯一分类,用可点击的Chip组件让用户切换分类:
Widget _buildCategoryChips() { // 获取所有不重复的分类 final categories = items.map((item) => item.category).toSet().toList(); return Wrap( spacing: 8.0, padding: const EdgeInsets.all(16.0), children: [ // 全部分类选项 Chip( label: const Text('全部'), backgroundColor: _selectedCategory == null ? Colors.blue : Colors.grey[200], onPressed: () { setState(() { _selectedCategory = null; }); }, ), ...categories.map((category) => Chip( label: Text(category), backgroundColor: _selectedCategory == category ? Colors.blue : Colors.grey[200], onPressed: () { setState(() { _selectedCategory = category; }); }, )).toList(), ], ); }
4. 调整现有buildItems Widget
修改buildItems,使用筛选后的列表,移除多余的itemCount变量:
Widget buildItems(List<Items> items) => GridView.builder( physics: const BouncingScrollPhysics(), shrinkWrap: true, scrollDirection: Axis.vertical, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 4.0, mainAxisSpacing: 20.0, mainAxisExtent: 170, ), itemCount: items.length, // 使用传入的筛选后列表长度 itemBuilder: (context, index) { final item = items[index]; return Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: InkWell( onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) => DetailScreen(item: item))), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), boxShadow: const [ BoxShadow( color: Colors.black38, blurRadius: 7, offset: Offset(0, 5.0)), BoxShadow(color: Colors.white, offset: Offset(-1, 0)), BoxShadow(color: Colors.white, offset: Offset(2, 0)), ]), child: Column( children: [ SizedBox( child: ClipRRect( borderRadius: const BorderRadius.only( topLeft: Radius.circular(10), topRight: Radius.circular(10)), child: Image.network( '$imageLink${item.imageUrl}', fit: BoxFit.fill, width: double.infinity, height: 110, ), ), ), SizedBox( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( margin: const EdgeInsets.only(left: 5, top: 10), height: 40, width: 40, child: ClipRRect( borderRadius: BorderRadius.circular(5), child: Image.network( '$iconLink${item.logo}', fit: BoxFit.fill)), ), Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 5.0), child: Container( width: 120, margin: const EdgeInsets.only( top: 7, left: 5), child: Flexible( child: Text(item.name, overflow: TextOverflow.ellipsis, style: Theme.of(context) .textTheme .bodyText1), ), ), ), const SizedBox(height: 2), Padding( padding: const EdgeInsets.only(left: 5.0), child: Text(item.subcategory, style: Theme.of(context) .textTheme .bodyText2! .copyWith( color: kButtonBackground)), ), ], ), ], ), ], ), ), ], ), ), ) ); }, );
5. 整合到页面build方法
在页面的build方法中组合分类选择栏和筛选后的列表:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('分类展示')), body: Column( children: [ _buildCategoryChips(), Expanded( child: buildItems(_filteredItems), ), ], ), ); }
JSON数据示例
[ { "name": "Item1", "category": "Apple", "subcategory": "Productivity", "imageUrl": "Banner.jpg", "logo": "logo.png", "description": "falkr." }, { "name": "ABC", "category": "Testing1", "subcategory": "Productivity", "imageUrl": "banner.jpg", "logo": "System.png", "description": "Testing." } ]
内容的提问来源于stack exchange,提问作者talal habib
相关产品推荐
相关产品推荐

