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

Flutter中BlocListener未监听AuthCubit状态变更问题求助

问题描述

在Widget树顶部配置了lazy: false的BlocProvider(对应AuthCubit),目的是立即监听认证状态变更。但登录页使用BlocListener时出现异常:Firebase Auth先返回user为null(对应isUserLoggedIn为false),随后返回有效FirebaseUser(对应isUserLoggedIn为true),导致BlocListener未触发预期的页面跳转。尝试过listenWhen属性、WidgetsBinding.instance.addPostFrameCallback均未解决。重启应用后,用户状态直接显示为非null的FirebaseUser。

相关代码实现

AuthCubit部分

late StreamSubscription<AuthUserModel>? _authUserSubscription;

_authUserSubscription = _authService.authStateChanges.listen(_listenAuthStateChangesStream);

Future<void> _listenAuthStateChangesStream(AuthUserModel authUser) async {
  emit(state.copyWith(isInProgress: true));

  if (AuthUserModel.empty() == authUser) {
    emit(
      state.copyWith(
        authUser: authUser,
        isUserLoggedIn: false,
        isInProgress: false,
      ),
    );
  } else {
    await _chatService.connectTheCurrentUser();

    emit(
      state.copyWith(
        authUser: authUser,
        isUserLoggedIn: true,
        isInProgress: false,
      ),
    );
  }
}

基础设施层部分

@override
Stream<AuthUserModel> get authStateChanges {
  return _firebaseAuth.authStateChanges().map(
    (User? user) {
      if (user == null) {
        return AuthUserModel.empty();
      } else {
        return user.toDomain();
      }
    },
  );
}

Widget树顶部BlocProvider配置

BlocProvider(
  lazy: false,
  create: (context) => getIt<AuthCubit>(),
      
  child: Listener(
    onPointerUp: (_) {
      if (Platform.isIOS) {
        final FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
          FocusManager.instance.primaryFocus!.unfocus();
        }
      }
    },
    child: MaterialApp.router(...

登录页BlocListener代码

@override
Widget build(BuildContext context) {
  return BlocListener<AuthCubit, AuthState>(
    listener: (context, state) {
      if (state.isUserLoggedIn) {
        context.go(context.namedLocation("channels_page"));
      } else {
        context.go(context.namedLocation("sign_in_page"));
      }
    },
    child: const Scaffold(
      body: Center(
        child: CustomProgressIndicator(
          progressIndicatorColor: Colors.black,
        ),
      ),
    ),
  );
}
解决方案

1. 精准控制BlocListener的触发条件

通过listenWhen过滤无效状态变更,只在isUserLoggedIn状态实际切换且当前非加载状态时触发跳转,避免连续状态变更的干扰:

@override
Widget build(BuildContext context) {
  return BlocListener<AuthCubit, AuthState>(
    listenWhen: (previous, current) {
      // 仅当登录状态发生变化,且当前状态不是加载中时执行监听逻辑
      return previous.isUserLoggedIn != current.isUserLoggedIn && !current.isInProgress;
    },
    listener: (context, state) {
      if (state.isUserLoggedIn) {
        context.go(context.namedLocation("channels_page"));
      } else {
        context.go(context.namedLocation("sign_in_page"));
      }
    },
    child: const Scaffold(
      body: Center(
        child: CustomProgressIndicator(
          progressIndicatorColor: Colors.black,
        ),
      ),
    ),
  );
}

2. 优化AuthCubit的状态发射逻辑

避免连续发射加载状态和最终状态,调整为仅在需要时发射加载状态,确保状态变更的顺序清晰:

Future<void> _listenAuthStateChangesStream(AuthUserModel authUser) async {
  if (AuthUserModel.empty() == authUser) {
    emit(
      state.copyWith(
        authUser: authUser,
        isUserLoggedIn: false,
        isInProgress: false,
      ),
    );
  } else {
    // 仅在处理用户连接时发射加载状态
    emit(state.copyWith(isInProgress: true));
    await _chatService.connectTheCurrentUser();
    emit(
      state.copyWith(
        authUser: authUser,
        isUserLoggedIn: true,
        isInProgress: false,
      ),
    );
  }
}

3. 修正Firebase Auth状态流的初始值

给状态流添加初始的当前用户值,避免首次订阅时先返回null再返回有效用户的问题:

@override
Stream<AuthUserModel> get authStateChanges {
  return _firebaseAuth.authStateChanges()
    .startWith(_firebaseAuth.currentUser) // 初始发送已缓存的当前用户
    .map((User? user) {
      if (user == null) {
        return AuthUserModel.empty();
      } else {
        return user.toDomain();
      }
    });
}

核心要点

  • 过滤无效状态:通过listenWhen跳过加载状态和无意义的状态重复,只处理最终认证结果。
  • 避免状态冲突:确保状态发射的顺序合理,连续快速的状态变更可能导致跳转逻辑被覆盖。
  • 利用缓存用户:Firebase会缓存当前用户,初始发送缓存值可以减少状态流的波动。

内容的提问来源于stack exchange,提问作者Alper Efe Sahin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:35:33