如何在Riverpod的AsyncNotifier的build方法中处理网络异常
解决Riverpod AsyncNotifier网络异常处理问题
你之前在build()中使用AsyncValue.guard出现语法错误,核心原因是AsyncValue.guard返回的是Future<AsyncValue<T>>,而AsyncNotifier的build()要求返回Future<T>,类型不匹配导致报错。以下是正确的异常处理实现方案:
1. 修正网络请求与异常捕获
首先完善_fetchTodo方法,处理网络连接异常、请求状态错误及JSON解析错误,同时修正http.get的参数格式(需传入Uri而非字符串):
Future<List<Todo>> _fetchTodo() async { try { // 修正:http.get需要传入Uri对象 final response = await http.get(Uri.parse('api/todos')); // 检查响应状态码 if (response.statusCode != 200) { throw Exception('请求失败,状态码:${response.statusCode}'); } final todos = jsonDecode(response.body) as List<Map<String, dynamic>>; return todos.map((todo) => Todo.fromJson(todo)).toList(); } on ClientException catch (e) { // 捕获无网络等连接异常 throw Exception('网络连接失败:${e.message}'); } catch (e) { // 捕获其他异常(如JSON解析错误) throw Exception('加载数据失败:$e'); } }
2. 正确实现build方法的异常处理
AsyncNotifier会自动将build()返回的Future转换为AsyncValue状态(加载/成功/错误),因此你可以直接返回_fetchTodo(),异常会被自动捕获并转为AsyncValue.error:
@override Future<List<Todo>> build() async { return _fetchTodo(); }
如果需要在build()中主动处理异常(比如记录日志),可以用AsyncValue.guard配合类型转换:
@override Future<List<Todo>> build() async { final asyncValue = await AsyncValue.guard(_fetchTodo); if (asyncValue.hasError) { // 可选:添加日志记录等自定义操作 print('加载待办列表出错:${asyncValue.error}'); // 重新抛出异常,让AsyncNotifier将state设置为错误状态 rethrow; } return asyncValue.requireValue; }
3. UI层处理异常状态
必须在UI层使用AsyncValue.when处理三种状态,避免直接访问state.value导致崩溃:
Consumer( builder: (context, ref, child) { final todosState = ref.watch(asyncTodosProvider); return todosState.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, stackTrace) => Center(child: Text('出错了:$error')), data: (todos) => ListView.builder( itemCount: todos.length, itemBuilder: (context, index) { final todo = todos[index]; return ListTile( title: Text(todo.description), leading: Checkbox(value: todo.completed, onChanged: (_) {}), ); }, ), ); }, )
完整修改后的AsyncTodosNotifier代码
class AsyncTodosNotifier extends AsyncNotifier<List<Todo>> { Future<List<Todo>> _fetchTodo() async { try { final response = await http.get(Uri.parse('api/todos')); if (response.statusCode != 200) { throw Exception('请求失败,状态码:${response.statusCode}'); } final todos = jsonDecode(response.body) as List<Map<String, dynamic>>; return todos.map((todo) => Todo.fromJson(todo)).toList(); } on ClientException catch (e) { throw Exception('网络连接失败:${e.message}'); } catch (e) { throw Exception('加载数据失败:$e'); } } @override Future<List<Todo>> build() async { return _fetchTodo(); } Future<void> addTodo(Todo todo) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { await http.post(Uri.parse('api/todos'), body: todo.toJson()); return _fetchTodo(); }); } } final asyncTodosProvider = AsyncNotifierProvider<AsyncTodosNotifier, List<Todo>>(() { return AsyncTodosNotifier(); });
内容的提问来源于stack exchange,提问作者user46655
相关产品推荐
相关产品推荐

