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

Flutter如何捕获API请求异常并返回给UI展示服务器错误提示

问题根因
  • 原getBooks方法中catch块仅打印异常,未向上抛出明确的错误信息,末尾统一抛出的无参Exception无法传递友好提示内容
  • UI层调用await BooksApi.getBooks(query)时未做异常捕获逻辑,导致异常抛出后直接触发未捕获错误
  • 异常分支没有对应的UI状态处理逻辑,无法将错误信息展示给用户
解决方案

步骤1:自定义API异常类(可选但推荐)

统一封装错误信息,方便区分错误类型,避免直接使用无意义的泛型Exception

class ApiException implements Exception {
  final String message;
  final int? statusCode;

  ApiException(this.message, [this.statusCode]);

  @override
  String toString() => message;
}

步骤2:改造API请求方法

完善异常处理逻辑,抛出带友好提示的异常,删除末尾多余的异常抛出

class BooksApi {
  static Future<List<Book>> getBooks(String query) async {
    try {
      final url = Uri.parse(
          'https://gist.githubusercontent.com/JohannesMilke/d53fbbe9a1b7e7ca2645db13b995dc6f/raw/eace0e20f86cdde3352b2d92f699b6e9dedd8c70/books.json');
      final response = await http.get(url);

      if (response.statusCode == 200) {
        final List books = json.decode(response.body);
        return books.map((json) => Book.fromJson(json)).where((book) {
          final titleLower = book.title.toLowerCase();
          final authorLower = book.author.toLowerCase();
          final searchLower = query.toLowerCase();
          return titleLower.contains(searchLower) ||
              authorLower.contains(searchLower);
        }).toList();
      } else {
        // 根据状态码返回对应友好提示
        final errorMsg = response.statusCode >= 500 ? "服务器繁忙,请稍后重试" : "请求失败,请稍后再试";
        throw ApiException(errorMsg, response.statusCode);
      }
    } catch (e) {
      print("API请求错误:$e");
      // 处理网络不通、数据解析错误等客户端异常
      if (e is http.ClientException || e is FormatException || e is TypeError) {
        throw ApiException("网络连接异常,请检查网络设置");
      }
      // 其他异常统一返回服务端错误提示
      throw ApiException("服务器繁忙,请稍后重试");
    }
  }
}

步骤3:改造UI层调用逻辑

添加异常捕获,新增错误状态用于展示提示
首先在State类中新增错误状态变量:

String? errorMsg;

改造init方法,添加try-catch-finally逻辑:

Future init() async {
  setState(() {
    isLoading = true;
    errorMsg = null; // 请求前清空历史错误
  });
  try {
    final books = await BooksApi.getBooks(query);
    final response = await obj.getProduct();
    print(response);
    setState(() => this.books = books);
  } catch (e) {
    // 捕获异常,保存错误信息
    setState(() => errorMsg = e.toString());
  } finally {
    // 无论成功失败都关闭加载状态
    setState(() => isLoading = false);
  }
}

步骤4:UI层展示错误提示

在布局中添加错误提示的展示逻辑:

@override
Widget build(BuildContext context) {
  if (isLoading) {
    return const CircularProgressIndicator();
  }
  // 存在错误时展示错误提示
  if (errorMsg != null) {
    return Text(
      errorMsg!,
      style: const TextStyle(color: Colors.red, fontSize: 14),
    );
  }
  // 正常展示书籍列表
  return ListView.builder(
    itemCount: books.length,
    itemBuilder: (context, index) => BookItem(book: books[index]),
  );
}
更安全的进阶方案(可选)

如果想要彻底避免未捕获异常的问题,可以用密封类封装请求结果,强制上层处理成功/失败分支:

// 定义密封类
sealed class Result<T> {}
class Success<T> extends Result<T> {
  final T data;
  Success(this.data);
}
class Failure<T> extends Result<T> {
  final String errorMsg;
  Failure(this.errorMsg);
}

// 改造getBooks返回值
static Future<Result<List<Book>>> getBooks(String query) async {
  try {
    // 原有请求逻辑
    return Success(bookList);
  } catch (e) {
    return Failure(e.toString());
  }
}

// 调用时分支处理
final result = await BooksApi.getBooks(query);
switch (result) {
  case Success(data: final books):
    setState(() => this.books = books);
  case Failure(errorMsg: final msg):
    setState(() => errorMsg = msg);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 14:54:09