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

Flutter技术问询:如何确保PlayersCubit发射PlayerLoaded状态后再执行NavigatorCubit的导航逻辑

问题分析与解决方案

你遇到的核心问题是:虽然await addPlayer()会等待方法内部的异步操作完成(包括执行emit(PlayerLoaded)),但NavigatorCubit中缓存的_playerState并没有同步更新到最新状态——因为它是通过getPlayerState手动设置的,既没有在addPlayer完成后主动刷新,也没有自动监听PlayersCubit的状态变化,导致moveToLobby判断时用的还是旧状态。

下面给出几个无需定时器的可行方案:

方案1:直接在导航方法中获取最新状态

最简单的修复方式,就是让moveToLobby不再依赖内部缓存的状态,而是每次调用时直接从上下文获取RoomCubit和PlayersCubit的当前状态:

class NavigatorCubit extends Cubit<NavigatorState> {
  NavigatorCubit() : super(NavigatorInitial());

  void moveToLobby(BuildContext context) {
    // 直接获取最新状态
    final roomState = BlocProvider.of<RoomCubit>(context).state;
    final playerState = BlocProvider.of<PlayersCubit>(context).state;
    
    print("$roomState, $playerState");
    if (roomState is RoomLoaded && playerState is PlayerLoaded) {
      Navigator.popAndPushNamed(context, "/Room");
    } else {
      Navigator.pop(context);
    }
  }
}

这样完全避免了状态缓存不同步的问题,你的按钮调用代码不需要任何修改,await addPlayer()完成后,导航逻辑就能拿到最新的状态。

方案2:让NavigatorCubit自动监听其他Cubit的状态

如果希望NavigatorCubit能自动保持状态同步,可以让它在初始化时监听RoomCubit和PlayersCubit的状态流,实时更新内部状态:

class NavigatorCubit extends Cubit<NavigatorState> {
  NavigatorCubit({required RoomCubit roomCubit, required PlayersCubit playersCubit}) 
    : super(NavigatorInitial()) {
    // 监听RoomCubit的状态变化
    _roomSubscription = roomCubit.stream.listen((state) {
      _roomState = state;
    });
    // 监听PlayersCubit的状态变化
    _playerSubscription = playersCubit.stream.listen((state) {
      _playerState = state;
    });
    // 初始化时获取当前状态
    _roomState = roomCubit.state;
    _playerState = playersCubit.state;
  }

  late RoomState _roomState;
  late PlayerState _playerState;
  late StreamSubscription _roomSubscription;
  late StreamSubscription _playerSubscription;

  void moveToLobby(BuildContext context) {
    print("$_roomState, $_playerState");
    if (_roomState is RoomLoaded && _playerState is PlayerLoaded) {
      Navigator.popAndPushNamed(context, "/Room");
    } else {
      Navigator.pop(context);
    }
  }

  @override
  Future<void> close() {
    // 取消订阅避免内存泄漏
    _roomSubscription.cancel();
    _playerSubscription.cancel();
    return super.close();
  }
}

然后在提供NavigatorCubit时,需要传入已存在的其他Cubit实例:

BlocProvider(
  create: (context) => NavigatorCubit(
    roomCubit: BlocProvider.of<RoomCubit>(context),
    playersCubit: BlocProvider.of<PlayersCubit>(context),
  ),
  child: YourPageWidget(),
)

这样NavigatorCubit会自动同步另外两个Cubit的状态,你原来的按钮调用逻辑不需要修改。

方案3:用BlocListener监听状态变化后再导航

如果不想修改NavigatorCubit,可以在页面中通过BlocListener监听PlayersCubit的状态,确保只有当PlayerLoaded发射后才执行导航:

BlocListener<PlayersCubit, PlayerState>(
  listener: (context, state) {
    if (state is PlayerLoaded) {
      final roomState = BlocProvider.of<RoomCubit>(context).state;
      if (roomState is RoomLoaded) {
        BlocProvider.of<NavigatorCubit>(context).moveToLobby(context);
      } else {
        Navigator.pop(context);
      }
    }
  },
  child: TextButton(
    onPressed: () async {
      try {
        await BlocProvider.of<RoomCubit>(context).createRoom(test);
        await BlocProvider.of<PlayersCubit>(context).addPlayer(test.roomId, testPlayer);
        // 这里不再直接调用moveToLobby,交给BlocListener处理
      } catch (e) {
        Navigator.pop(context);
      }
    },
    child: Text("Create room"),
  ),
)

这种方式把导航逻辑和状态变化强绑定,确保流程的顺序性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 18:27:49