Flutter调用API数据无法加载,加载指示器一直处于运行状态如何修复?
问题原因
- 首先是返回类型不匹配:你的
getLiveMatches方法声明返回Future<List<dynamic>>,但你调用的API返回的liveMatch字段是Map类型,不是列表,类型转换失败会抛出异常,而你没有在FutureBuilder里处理异常情况,所以hasData始终为false,加载指示器一直显示。 - 其次是FutureBuilder未覆盖全状态:你只判断了
snapshot.hasData的情况,没有处理加载完成但有错误的分支,异常被吞了自然看不到问题。 - 最后是future调用方式不规范:直接在FutureBuilder的
future参数里调用getLiveMatches(),每次组件重绘都会重新发起请求,同时RefreshIndicator的刷新逻辑没有触发状态更新,刷新后也不会刷新界面。
修复后的完整代码
import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class Test extends StatefulWidget { const Test({super.key}); @override State<Test> createState() => _TestState(); } class _TestState extends State<Test> { // 提前声明future变量,避免每次build重复请求 late Future<Map<String, dynamic>> _liveMatchFuture; Future<Map<String, dynamic>> getLiveMatches() async { http.Response response = await http.get( Uri.parse("https://api.cricket.com.au/matches/2780/50837/live") ); final Map parseData = json.decode(response.body); var matches = parseData['liveMatch']; // 确认返回类型是Map,和方法声明一致 return matches; } @override void initState() { super.initState(); // 初始化时只请求一次 _liveMatchFuture = getLiveMatches(); } @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.only(left: 10, right: 10, top: 15), child: RefreshIndicator( color: Colors.white, backgroundColor: Colors.purple, strokeWidth: 5, onRefresh: ()async{ // 刷新时重新请求,调用setState更新future触发重绘 setState(() { _liveMatchFuture = getLiveMatches(); }); // 等待请求完成再结束刷新动画 await _liveMatchFuture; }, child: Container( height: double.infinity, decoration: const BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(6), topRight: Radius.circular(6) ), color: Colors.white ), child: FutureBuilder<Map<String, dynamic>>( future: _liveMatchFuture, builder: (context, snapshot) { // 处理加载中状态 if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CupertinoActivityIndicator( animating: true, radius: 10)); } // 处理错误状态 if (snapshot.hasError) { return Center( child: Text('加载出错:${snapshot.error}'), ); } // 正常返回数据的情况 Map<String, dynamic> match = snapshot.data!; return SingleChildScrollView( // 因为内容可能超出屏幕,加滚动同时适配RefreshIndicator的滚动要求 child: Column( children: [ Text(match.toString()) ], ), ); } ), ), ), ), ); } }
补充说明
- 如果你后续需要展示多条赛事数据,确认API返回字段的类型后再调整返回值类型即可,不要强行把Map转成List。
- 加了SingleChildScrollView是因为RefreshIndicator要求子组件是可滚动组件,之前你用Container作为子组件,下拉刷新的手势也会失效。
内容的提问来源于stack exchange,提问作者Haseeb Ahmad
相关产品推荐
相关产品推荐

