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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:50:21