Flutter中addPostFrameCallback回调未触发的推送跳转问题排查
问题根因
这是Flutter帧调度的时序问题导致的:
你使用的是可缓存最近事件的Stream(比如RxDart的BehaviorSubject),在main函数中发送的跳转事件,会在首页initState执行、刚完成stream订阅时立刻被推送过来。此时如果应用首帧已经完成绘制,后续没有新的帧调度触发(冷启动后无用户操作、无UI更新),你新增的addPostFrameCallback会一直等待下一次帧绘制才会执行,这就是切后台再切回(触发新的帧绘制)才会执行跳转的原因。
解决方案
方案1:判断当前调度阶段,决定直接执行还是注册回调
修改首页的监听逻辑,增加调度阶段判断即可解决:
@override void initState() { super.initState(); MainSingleton().notificationStream.stream.listen((redirect) { if (redirect == null) return; log("Home page found a redirect request: going to $redirect"); final binding = WidgetsBinding.instance!; // 判断是否已经过了当前帧的回调阶段 if (binding.schedulerPhase == SchedulerPhase.postFrameCallbacks) { // 直接执行跳转 log("Directly execute redirect"); Application.router.navigateTo(context, redirect); // 跳转后清空SharedPreferences缓存,避免重复跳转 SharedPreferences.getInstance().then((sp) => sp.remove('redirect')); } else { binding.addPostFrameCallback((_) { log("Callback called"); Application.router.navigateTo(context, redirect); SharedPreferences.getInstance().then((sp) => sp.remove('redirect')); }); } }); }
方案2:用Future.delayed(Duration.zero)替代addPostFrameCallback
该方法也可以保证context已挂载可正常使用,不需要额外判断帧阶段:
@override void initState() { super.initState(); MainSingleton().notificationStream.stream.listen((redirect) { if (redirect == null) return; log("Home page found a redirect request: going to $redirect"); Future.delayed(Duration.zero, () { log("Redirect executed"); Application.router.navigateTo(context, redirect); SharedPreferences.getInstance().then((sp) => sp.remove('redirect')); }); }); }
优化建议
你也可以调整逻辑时序彻底规避该问题:
- main函数只做读取SharedPreferences、暂存路由的操作,不要提前发送到stream
- 在首页
initState的addPostFrameCallback中再读取暂存的路由信息,有有效值就直接跳转,跳转后清空缓存
不需要依赖stream做跨阶段的事件传递,逻辑更简单也更稳定。
内容的提问来源于stack exchange,提问作者Slot
相关产品推荐
相关产品推荐

