Flutter API请求报错:Null非FutureOr<List>子类型及加载慢求助
解决Flutter API请求的空类型错误与加载不稳定问题
一、修复type 'Null' is not a subtype of type 'FutureOr<List>'异常
这个异常的核心是getSubjects()方法声明返回List<dynamic>,但实际场景中可能返回null(比如API响应无result字段、请求失败、token无效),同时原代码未做任何空安全校验。按以下步骤修复:
1. 强化API请求的校验与异常处理
修改getSubjects()方法,增加token校验、HTTP状态码判断、响应结构校验,确保不会返回null:
Future<List<dynamic>> getSubjects() async { SharedPreferences prefs = await SharedPreferences.getInstance(); var token = prefs.getString('token'); // token为空直接返回空列表,避免无效请求 if (token == null) return []; try { var result = await http.get( Uri.parse("xxxxxxxxxxxx"), headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': "Bearer $token", }, // 设置10秒超时,避免请求无限挂起 timeout: const Duration(seconds: 10), ); // 仅处理200状态码的成功响应 if (result.statusCode == 200) { final responseData = jsonDecode(result.body); // 校验响应中是否存在`result`且为列表类型 if (responseData.containsKey('result') && responseData['result'] is List) { return responseData['result']; } } // 非成功响应或结构不符合预期时返回空列表 return []; } catch (e) { // 捕获网络异常、解析异常,统一返回空列表 return []; } }
2. 优化FutureBuilder的状态处理
原代码仅处理了snapshot.hasData,忽略了错误状态与空安全,同时直接在build中调用getSubjects()会导致重复发起请求:
// 在State类中提前初始化请求,避免build重复调用 late final Future<List<dynamic>> _subjectsFuture; @override void initState() { super.initState(); _subjectsFuture = getSubjects(); } // 替换原FutureBuilder代码 Padding( padding: const EdgeInsets.all(18.0), child: FutureBuilder<List<dynamic>>( future: _subjectsFuture, builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) { // 处理错误状态 if (snapshot.hasError) { return const Center(child: Text('加载失败,请重试')); } // 处理加载中状态 if (snapshot.connectionState == ConnectionState.waiting) { return Center( child: LinearProgressIndicator(backgroundColor: Colors.amber[500]), ); } // 确保数据不为空,空列表直接展示空状态(可选) final subjects = snapshot.data ?? []; if (subjects.isEmpty) { return const Center(child: Text('暂无科目数据')); } // 渲染列表 return ListView.builder( shrinkWrap: true, itemCount: subjects.length, itemBuilder: (BuildContext context, int index) { final subject = subjects[index]; // 字段空安全校验,避免空指针 final subjectName = subject['subject_name']?.toString() ?? '未知科目'; final subjectId = subject['subject_id'] ?? ''; return Padding( padding: const EdgeInsets.all(5.0), child: Container( decoration: BoxDecoration( image: const DecorationImage( image: AssetImage('assets/bg.png'), fit: BoxFit.cover, ), color: Colors.amber[50], borderRadius: BorderRadius.circular(10.0), boxShadow: const [ BoxShadow( color: Colors.grey, blurRadius: 2.0, offset: Offset(2.0, 2.0)) ], ), child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( title: Text( subjectName, style: const TextStyle(color: Colors.white), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.book, color: Colors.amber.shade900), const SizedBox(width: 10.00), ], ), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => GiveFeedbackScreen( subjectId: subjectId, subjectName: subjectName, feedbackId: widget.id, ), ), ); }, ), ], ), ), ), ); }, ); }, ), );
二、解决API加载速度不稳定问题
1. 强制请求超时
已在getSubjects()中添加timeout参数,避免请求因网络问题无限挂起。
2. 实现本地缓存
用SharedPreferences缓存API响应,10分钟内直接读取缓存,减少重复请求:
Future<List<dynamic>> getSubjects() async { SharedPreferences prefs = await SharedPreferences.getInstance(); var token = prefs.getString('token'); if (token == null) return []; // 读取缓存数据与缓存时间 final cachedData = prefs.getString('subjects_cache'); final cacheTime = prefs.getInt('subjects_cache_time'); const cacheDuration = Duration(minutes: 10); // 缓存未过期则直接返回 if (cachedData != null && cacheTime != null) { final now = DateTime.now().millisecondsSinceEpoch; if (now - cacheTime < cacheDuration.inMilliseconds) { return jsonDecode(cachedData); } } try { var result = await http.get( Uri.parse("xxxxxxxxxxxx"), headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': "Bearer $token", }, timeout: const Duration(seconds: 10), ); if (result.statusCode == 200) { final responseData = jsonDecode(result.body); final subjects = responseData['result'] is List ? responseData['result'] : []; // 更新缓存 await prefs.setString('subjects_cache', jsonEncode(subjects)); await prefs.setInt('subjects_cache_time', DateTime.now().millisecondsSinceEpoch); return subjects; } else { // 请求失败时返回缓存作为降级方案 if (cachedData != null) return jsonDecode(cachedData); return []; } } catch (e) { // 网络异常时返回缓存 if (cachedData != null) return jsonDecode(cachedData); return []; } }
3. 排查外部因素
- 确认API服务器的负载情况,是否存在高峰时段性能下降
- 测试不同网络环境(Wi-Fi/移动数据)下的请求速度,排查网络波动问题
内容的提问来源于stack exchange,提问作者swapnil mane
相关产品推荐
相关产品推荐

