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
相关产品推荐
相关产品推荐

