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

Flutter FutureBuilder加载API数据时列表不显示持续加载问题

Flutter FutureBuilder 已持有数据但持续显示加载指示器问题修复

核心错误点

  • ListView.builder 未设置必填的itemCount参数,组件无法确定列表渲染长度,触发布局异常导致渲染分支无法正常执行
  • 数据链路错位:snapshot已持有接口返回数据,但列表渲染时未使用snapshot.data,反而读取父组件传入的初始空列表widget.breedlist
  • 布局约束缺失:ListView直接嵌套在Column中未做高度适配,触发垂直方向无边界布局错误
  • 状态判断不严谨:未校验snapshot.connectionState,仅通过hasData判断状态容易遗漏异常场景

可直接运行的修复代码

组件部分修正

class BookPageBody extends StatefulWidget {
  // 不需要父组件提前传入列表,数据由组件内Future自行获取
  const BookPageBody({Key? key}) : super(key: key);

  @override
  _BookPageBodyState createState() => _BookPageBodyState();
}

class _BookPageBodyState extends State<BookPageBody> {
  Future<List<dynamic>>? futureData;

  @override
  void initState() {
    super.initState();
    futureData = fetchData(AppConstants.APIBASE_URL);
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Column(children: [
        SizedBox(height: Dimensions.height20),
        Container(
            margin: EdgeInsets.only(left: Dimensions.width20),
            child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  BigText(text: 'Breed List'),
                ])),
        // 明确Future返回类型,避免类型推断错误
        FutureBuilder<List<dynamic>>(
          future: futureData,
          builder: (context, snapshot) {
            // 先判断异步任务状态,任务执行完成后再走数据/错误分支
            if (snapshot.connectionState == ConnectionState.done) {
              if (snapshot.hasError) {
                return Center(child: Text("加载失败:${snapshot.error}"));
              }
              final breedList = snapshot.data ?? [];
              // 用Expanded包裹ListView,适配Column内的剩余高度
              return Expanded(
                child: ListView.builder(
                  // 补全itemCount,告诉ListView要渲染多少项
                  itemCount: breedList.length,
                  itemBuilder: (BuildContext context, int index) {
                    // 直接使用snapshot返回的列表数据,不依赖外部全局变量
                    return ListCard(index: index, doglist: breedList);
                  }
                ),
              );
            }
            // 异步任务未完成时显示加载态
            return const Center(child: CircularProgressIndicator());
          },
        ),
      ]),
    );
  }
}

接口请求方法修正

移除不必要的全局变量中转逻辑,避免多页面状态污染:

Future<List<dynamic>> fetchData(url) async {
  var client = http.Client();
  try {
    final response = await client.get(Uri.parse(url));
    await Future.delayed(const Duration(seconds:2));
    if (response.statusCode == 200) {
      var jsonDecoded = json.decode(response.body);
      final result = jsonDecoded.map((data) => DogClass.fromJson(data)).toList();
      // 若其他模块需要用到该列表可保留全局赋值,否则建议删除全局变量
      glossarList = result;
      return result;
    } else {
      throw Exception('接口请求失败,状态码:${response.statusCode}');
    }
  } finally {
    // 请求结束后关闭client,避免内存泄漏
    client.close();
  }
}

避坑提示

不要依赖全局变量的赋值状态判断FutureBuilder的渲染时机,所有渲染判断必须以builder函数回调的snapshot属性为唯一依据
ListView/GridView这类滚动组件嵌套在Column中时,必须使用Expanded包裹或设置shrinkWrap: true,否则会触发布局溢出报错
使用ListView.builder懒加载构造函数时必须传入itemCount,否则组件无法计算视口渲染范围,直接抛出布局异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 14:42:24