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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:45:25