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

Flutter中基于流式数据的单可展开列表两种实现方案性能对比及选型疑问

Performance Comparison & Selection Guide for Single-Expansion List in Flutter

Great question! Let’s break down the performance differences between your two approaches, fix the key issue you’re facing with ExpansionTile, and help you pick the best option for your use case.

Core Performance Differences

First, let’s clarify how each approach handles rendering and state changes:

1. ListView.builder + ExpansionTile (Original Implementation)

Your initial code has a critical flaw with the PageStorageKey: using DateTime.now().millisecondsSinceEpoch means every time the UI rebuilds (e.g., when selecting a new item), every existing ExpansionTile gets a new unique key. This forces Flutter to completely destroy and re-create all visible tiles instead of just updating their expanded state. This wipes out the lazy-loading benefit of ListView.builder (which only builds items as they enter the viewport) and causes unnecessary performance overhead.

That said, when implemented correctly (with stable keys), this approach is the most performant option—especially for large datasets.

2. ExpansionPanelList + ExpansionPanel

This approach has a fundamental limitation: it does not support lazy loading. It builds all ExpansionPanel widgets upfront, even if they’re not visible on the screen. For small datasets (e.g., 10-20 items), this is manageable, but for larger lists, the initial build time and memory usage will increase significantly.

Additionally, every time the selected item changes, you’re re-generating the entire listData array. While Flutter’s diffing algorithm will optimize some of this, it still means more work for the framework compared to updating individual tiles.

Fixing the ExpansionTile Key Issue

You mentioned that setting a unique key breaks animations, but that’s only because you’re using an unstable key. The fix is simple: use a stable, item-specific key instead of a timestamp. For example, use your item’s unique id:

return ListView.builder(
  padding: EdgeInsets.all(10),
  itemCount: data.length,
  itemBuilder: ((context, index) {
    final item = data[index];
    return ExpansionTile(
      key: PageStorageKey(item.id.toString()), // Stable key tied to the item
      initiallyExpanded: selectedItem == item.id.toString(),
      onExpansionChanged: ((value) => ref.read(selectedProductListItem.notifier).state = value ? item.id.toString() : null),
      tilePadding: EdgeInsets.all(10),
      leading: const Icon(Icons.account_box),
      title: Text(item.name, style: const TextStyle(fontWeight: FontWeight.bold)),
      childrenPadding: EdgeInsets.all(10),
      expandedAlignment: Alignment.center,
      children: [
        Text(item.description),
        const SizedBox(height: 10),
        ElevatedButton(onPressed: () {}, child: const Text('Apply Now')),
      ],
    );
  }),
);

With this change:

  • Flutter can track each ExpansionTile’s state correctly, so animations will work as expected.
  • When the selected item changes, Flutter only updates the expanded state of the relevant tiles (instead of rebuilding all of them).
  • You retain the lazy-loading benefit of ListView.builder, keeping performance high even for large datasets.

Final Selection Guidance

Based on performance and scalability:

  • For large datasets (10+ items): Use the corrected ListView.builder + ExpansionTile approach. It’s lazy-loaded, minimizes unnecessary rebuilds, and scales far better than ExpansionPanelList.
  • For small datasets (10 items or fewer): ExpansionPanelList is acceptable, but you can optimize it by memoizing the listData array (e.g., using memoized or Riverpod’s computed provider) to avoid re-generating it on every build.

The corrected ExpansionTile approach is almost always the better choice unless you have a very small list and prefer the default styling of ExpansionPanelList (though even then, you can customize ExpansionTile to match your needs).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 17:32:35