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

Flutter中基于Bloc/Cubit实现全局API异常统一处理方案

Flutter flutter_bloc 全局统一错误处理实现方案

核心思路是把错误识别、特殊错误分发、通用业务响应三层解耦,完全不需要在每个业务Bloc中重复编写相同错误处理逻辑。


1. 网络层全局拦截,统一标记特殊错误

无论使用dio还是http库,都在最外层的全局拦截/统一请求封装中识别登录失效类错误,不要把错误判断散落在单个接口逻辑中。
首先定义专属异常类型,只要接口返回匹配登录失效规则(HTTP 401状态码、后端约定的失效错误码等),就直接抛出该自定义异常,其他业务错误正常透传即可。
以dio为例的实现代码:

// 自定义登录凭证失效专属异常
class AppUnauthenticatedException implements Exception {
  final String message;
  AppUnauthenticatedException([this.message = "登录已过期,请重新登录"]);
}

// 全局网络拦截器
class GlobalAuthInterceptor extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // 按自身项目后端返回结构调整判断规则,示例为code=401代表登录失效
    final bizCode = response.data?["code"] as int?;
    if (bizCode == 401) {
      handler.reject(
        DioError(
          requestOptions: response.requestOptions,
          error: AppUnauthenticatedException(),
        ),
        false,
      );
      return;
    }
    super.onResponse(response, handler);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    // 兼容HTTP状态码直接返回401的场景
    if (err.response?.statusCode == 401) {
      err.error = AppUnauthenticatedException();
    }
    super.onError(err, handler);
  }
}

如果使用http库,只需要封装统一的get/post请求方法,所有接口调用都走该封装,在封装内统一做上述错误判断抛出异常即可。


2. 全局Bloc观察者捕获特殊错误,自动触发登出逻辑

利用flutter_bloc自带的BlocObserver能力,所有Bloc/Cubit中未被捕获的异常都会触发onError回调,我们在这里统一识别登录失效异常,直接触发全局AuthBloc的登出事件,完全不需要单个业务Bloc感知。
实现代码:

class AppBlocObserver extends BlocObserver {
  final AuthBloc authBloc;
  AppBlocObserver(this.authBloc);

  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    // 识别到登录失效异常,直接触发登出,加判断避免重复触发
    if (error is AppUnauthenticatedException && authBloc.state is! AuthUnauthenticated) {
      authBloc.add(AuthLogoutEvent());
    }
    super.onError(bloc, error, stackTrace);
  }
}

在App入口初始化时完成全局配置:

void main() {
  // 初始化网络实例,加入全局拦截器
  final dio = Dio();
  dio.interceptors.add(GlobalAuthInterceptor());
  // 初始化全局生命周期的AuthBloc
  final authBloc = AuthBloc(dio: dio, localStorage: SharedPrefsStorage());
  // 注册全局Bloc观察者
  Bloc.observer = AppBlocObserver(authBloc);

  runApp(
    MultiBlocProvider(
      providers: [
        // 根节点注入全局AuthBloc,不随页面销毁
        BlocProvider.value(value: authBloc),
        // 其他业务Bloc正常注册
      ],
      child: const MyApp(),
    ),
  );
}

3. AuthBloc统一处理登出,UI层全局响应跳转

AuthBloc在收到登出事件时,统一完成本地缓存清理(删除token、用户信息等),然后发出未登录状态即可。
UI层只需要在根组件(一般是MaterialApp外层)做一次全局状态监听,只要收到未登录状态,就清空路由栈跳转至登录页,不需要单个页面单独处理:

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocListener<AuthBloc, AuthState>(
        listener: (context, state) {
          if (state is AuthUnauthenticated) {
            // 清空所有历史路由,跳转登录页
            Navigator.of(context).pushNamedAndRemoveUntil(
              "/login",
              (route) => false,
            );
          }
        },
        child: const AppRootPage(),
      ),
    );
  }
}

可选优化:封装BaseBloc/BaseCubit统一处理通用错误

如果还有网络超时、服务端通用报错等其他需要统一处理的错误,可以封装业务Bloc基类,内置统一的安全请求方法,减少重复的try-catch代码:

abstract class BaseCubit<State> extends Cubit<State> {
  BaseCubit(super.initialState);

  Future<void> safeCall(
    Future<void> Function() request, {
    required State Function(String errorMsg) buildErrorState,
  }) async {
    try {
      await request();
    } on AppUnauthenticatedException {
      // 登录失效异常已经由全局观察者处理,直接重抛即可,无需额外逻辑
      rethrow;
    } on DioError catch (e) {
      // 统一处理网络类错误,返回对应错误状态
      emit(buildErrorState(e.message ?? "网络异常,请稍后重试"));
    } catch (e) {
      emit(buildErrorState(e.toString()));
    }
  }
}

// 业务Bloc使用示例
class UserInfoCubit extends BaseCubit<UserInfoState> {
  final Dio dio;
  UserInfoCubit(this.dio) : super(UserInfoInitial());

  Future<void> loadUserInfo() async {
    emit(UserInfoLoading());
    await safeCall(
      () async {
        final res = await dio.get("/api/user/info");
        emit(UserInfoLoaded(UserModel.fromJson(res.data)));
      },
      buildErrorState: (msg) => UserInfoLoadError(msg),
    );
  }
}

注意事项

  • 不要在Repository层或其他中间层吞掉AppUnauthenticatedException,保证异常能正常透传到Bloc层被观察者捕获
  • 全局AuthBloc需要放在App根节点注入,保持应用级单例,不要随页面销毁
  • 触发登出逻辑前要加状态判断,避免同一个错误重复触发多次登出操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:33:16