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

