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

Flutter优化含ListView.builder的ExpansionTile性能问题

你的问题根源在于当前配置下,ListView会一次性渲染所有子项——shrinkWrap: true强制ListView计算所有子组件的高度,再加上NeverScrollableScrollPhysics禁用滚动,导致数据量大时全部子项被立刻构建,自然会出现卡顿。下面是几个针对性的优化方案:

1. 让列表可滚动,取消一次性渲染

去掉shrinkWrap: true和NeverScrollableScrollPhysics,改用Expanded包裹ListView,让它占用ExpansionTile展开后的可用空间。这样ListView会按需渲染当前可见的子项,而非一次性构建全部内容,性能会大幅提升。

ExpansionTile(
  title: Text(title),
  controlAffinity: ListTileControlAffinity.trailing,
  initiallyExpanded: true,
  maintainState: true,
  children: [
    // 用Expanded让ListView占满可用空间,无需shrinkWrap
    Expanded(
      child: ListView.builder(
        itemCount: data.length,
        // 移除shrinkWrap和NeverScrollableScrollPhysics
        itemBuilder: (context, index) => widgetList[index],
      ),
    )
  ],
)

2. 改用Sliver组件组合(更高效的滚动方案)

如果你的页面本身是滚动布局,用SliverExpansionTile配合CustomScrollView替代传统组合。Sliver系列是Flutter专为滚动优化的组件,能精准控制子项的创建与销毁,性能最优。

CustomScrollView(
  slivers: [
    SliverExpansionTile(
      title: Text(title),
      controlAffinity: ListTileControlAffinity.trailing,
      initiallyExpanded: true,
      maintainState: true,
      children: [
        // 用SliverList替代ListView.builder
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (context, index) => widgetList[index],
            childCount: data.length,
          ),
        ),
      ],
    ),
    // 页面其他Sliver组件...
  ],
)

3. 优化子组件的渲染效率

即使列表按需渲染,单个子组件的低效也会拖慢体验,可做这些优化:

  • 能用const构造函数的子组件一定要加,避免无意义重建,比如const ListTile(title: Text('xxx'))
  • 给子组件添加稳定的Key(比如ValueKey(data[index].id)),帮助Flutter识别组件身份,减少不必要的重建
  • 如果子组件有复杂计算或网络请求,提前缓存结果,避免每次build重复执行

4. 按需加载数据(针对超大数据集)

如果数据量特别大(上千条级别),实现分页加载:初始只加载前20条,用户滚动到底部时再加载下一批,初始渲染压力会极小。

示例思路:

final _scrollController = ScrollController();
bool hasMore = true;

@override
void initState() {
  super.initState();
  // 监听滚动到底部事件
  _scrollController.addListener(() {
    if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
      if (hasMore) loadMoreData();
    }
  });
}

// 分页加载方法
Future<void> loadMoreData() async {
  final newData = await fetchNextPage(); // 自定义的接口请求方法
  setState(() {
    if (newData.isEmpty) hasMore = false;
    data.addAll(newData);
  });
}

// 列表构建
ListView.builder(
  itemCount: data.length + (hasMore ? 1 : 0), // 增加加载占位项
  controller: _scrollController,
  itemBuilder: (context, index) {
    if (index == data.length) {
      return Center(child: CircularProgressIndicator());
    }
    return widgetList[index];
  },
)

5. 按需维护状态

如果ExpansionTile收起后不需要保留子列表的状态(比如滚动位置、输入框内容),可以把maintainState设为false,收起时Flutter会销毁子树,节省内存和渲染资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:32:44