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

Flutter:如何在SliverList中正确使用FutureBuilder并动态设置childCount?

问题描述

需要通过FutureBuilder在SliverList中展示列表数据,尝试过相关方案但未解决核心问题:当前代码可运行,但由于childCount定义在FutureBuilder外部,无法将其设置为snapshot.data.length,希望得到更优的实现方式与最佳实践。

当前代码:

return Scaffold(
  body: CustomScrollView(
    slivers: [
      SliverList(
        delegate: SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            return FutureBuilder(
              future: getList1(),
              builder: (BuildContext context, snapshot) {
                if (snapshot.hasData) {
                  return Padding(
                    padding: const EdgeInsets.all(10.0),
                    child: ListTile(
                      title: Text(
                        snapshot.data?[index]['docName'],
                      ),
                    ),
                  );
                } else {
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                }
              },
            );
          },
          childCount: 2,
        ),
      ),
    ],
  ),
);
解决方案与最佳实践

你当前的写法存在严重问题:每个列表项都会单独发起一次getList1()请求,不仅会造成重复请求、性能浪费,还会因为每个FutureBuilder状态独立,无法统一控制列表长度。

正确的思路是将FutureBuilder放在SliverList外层,先完成数据请求,再基于完整数据渲染列表:

核心优化点

  • 把数据请求的Future放在SliverList外层的FutureBuilder中,确保只请求一次数据
  • 拿到完整数据后,直接用数据长度作为childCount
  • 统一处理加载中、请求错误、空数据等状态,提升用户体验

修正后的代码

return Scaffold(
  body: FutureBuilder(
    // 建议将Future存到State变量中,避免组件重建时重复发起请求
    future: getList1(),
    builder: (BuildContext context, snapshot) {
      // 加载中状态
      if (snapshot.connectionState == ConnectionState.waiting) {
        return const CustomScrollView(
          slivers: [
            SliverFillRemaining(
              child: Center(child: CircularProgressIndicator()),
            ),
          ],
        );
      }
      // 请求错误状态
      if (snapshot.hasError) {
        return const CustomScrollView(
          slivers: [
            SliverFillRemaining(
              child: Center(child: Text('加载失败,请重试')),
            ),
          ],
        );
      }
      // 数据为空状态
      if (!snapshot.hasData || snapshot.data!.isEmpty) {
        return const CustomScrollView(
          slivers: [
            SliverFillRemaining(
              child: Center(child: Text('暂无数据')),
            ),
          ],
        );
      }
      // 数据加载完成,渲染列表
      final listData = snapshot.data!;
      return CustomScrollView(
        slivers: [
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: ListTile(
                    title: Text(listData[index]['docName']),
                  ),
                );
              },
              childCount: listData.length, // 直接使用数据长度作为列表项数量
            ),
          ),
        ],
      );
    },
  ),
);

额外建议

  • 避免直接在FutureBuilder的future参数中调用异步方法:如果父组件重建,getList1()会被重新执行。正确做法是在initState()中把Future赋值给一个State变量,比如:
    late final Future<List> _listFuture;
    
    @override
    void initState() {
      super.initState();
      _listFuture = getList1();
    }
    
    之后将_listFuture传给FutureBuilder即可。
  • 若列表数据量较大,可结合SliverChildBuilderDelegate的findChildIndexCallback参数优化列表项的复用逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:25:29