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

