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

Flutter中不使用shrinkWrap提升嵌套ListView性能的方法

优化Flutter分类展开列表:移除shrinkWrap保留懒加载性能

你的当前实现通过嵌套ListView.builder并设置shrinkWrap: true实现分类展开效果,但shrinkWrap会强制列表一次性计算并渲染所有子项,完全丧失builder懒加载的性能优势,当商品数量较多时会明显卡顿。下面提供两种无需shrinkWrap的优化方案:

方案一:CustomScrollView + Sliver 组合(自定义滚动视图)

将分类标题和展开的商品列表拆分为独立的Sliver组件,统一放在CustomScrollView中,利用Sliver的懒加载特性保持性能,同时满足"展开时下推下方分类"的交互需求。

class CategoryPage extends StatefulWidget {
  const CategoryPage({Key? key}) : super(key: key);

  @override
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  Category? selectedCategory;

  final List<Category> categories = [
    Category(name: 'Breakfast', products: ['Cake', 'Sandwich']),
    Category(name: 'Lunch', products: ['Chicken', 'Pizza']),
  ];

  Widget _buildCategoryHeader(Category category) {
    return GestureDetector(
      onTap: () {
        setState(() {
          // 点击同一分类时切换展开/收起状态
          selectedCategory = selectedCategory == category ? null : category;
        });
      },
      child: Container(
        padding: const EdgeInsets.symmetric(vertical: 10),
        margin: const EdgeInsets.only(top: 10),
        alignment: Alignment.center,
        width: double.infinity,
        decoration: BoxDecoration(
          color: selectedCategory == category ? Colors.green : Colors.grey,
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Text(category.name),
            Icon(
              selectedCategory == category
                  ? Icons.keyboard_arrow_up
                  : Icons.keyboard_arrow_down,
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('优化后的分类页')),
      body: CustomScrollView(
        slivers: categories.expand((category) {
          final List<Sliver> slivers = [
            SliverToBoxAdapter(child: _buildCategoryHeader(category)),
          ];
          // 展开时添加商品列表的Sliver
          if (selectedCategory == category) {
            slivers.add(SliverList(
              delegate: SliverChildBuilderDelegate(
                (context, index) {
                  return Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
                    child: Text(category.products[index]),
                  );
                },
                childCount: category.products.length,
              ),
            ));
          }
          return slivers;
        }).toList(),
      ),
    );
  }
}

class Category {
  final String name;
  final List<String> products;

  Category({required this.name, required this.products});
}

优势

  • 所有内容在同一个滚动视图中,避免嵌套滚动的性能问题
  • SliverList仅渲染可见区域的商品项,完全保留懒加载优势
  • 展开/收起时自动调整布局,下方分类自然下推

方案二:使用官方ExpansionPanelList组件

Flutter官方提供的ExpansionPanelList专门用于实现展开/收起的列表场景,内部已做性能优化,无需手动处理嵌套列表,代码更简洁。

class CategoryPage extends StatefulWidget {
  const CategoryPage({Key? key}) : super(key: key);

  @override
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  Category? selectedCategory;

  final List<Category> categories = [
    Category(name: 'Breakfast', products: ['Cake', 'Sandwich']),
    Category(name: 'Lunch', products: ['Chicken', 'Pizza']),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ExpansionPanel实现')),
      body: ListView(
        children: [
          ExpansionPanelList.radio(
            elevation: 1,
            expandedHeaderPadding: EdgeInsets.zero,
            children: categories.map((category) {
              return ExpansionPanelRadio(
                value: category,
                headerBuilder: (context, isExpanded) {
                  return Container(
                    padding: const EdgeInsets.symmetric(vertical: 10),
                    alignment: Alignment.center,
                    width: double.infinity,
                    color: isExpanded ? Colors.green : Colors.grey,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        Text(category.name),
                        Icon(
                          isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
                        ),
                      ],
                    ),
                  );
                },
                body: Column(
                  children: category.products.map((product) {
                    return Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
                      child: Text(product),
                    );
                  }).toList(),
                ),
              );
            }).toList(),
            initialOpenPanelValue: selectedCategory,
            onChanged: (value) {
              setState(() {
                selectedCategory = value;
              });
            },
          ),
        ],
      ),
    );
  }
}

class Category {
  final String name;
  final List<String> products;

  Category({required this.name, required this.products});
}

优势

  • 官方组件,自带单选展开/收起逻辑,无需手动维护选中状态的切换
  • 内部优化了渲染逻辑,仅渲染可见的展开内容
  • 代码量更少,样式和交互可通过参数快速调整

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:35:17