使用GetX实现底部导航栏时报Obx无法标记为需构建错误如何解决
异常原文
This Obx widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
错误根本原因
这个异常的核心是你在Flutter的组件build流程运行过程中,修改了GetX的可观测(.obs)变量,触发了对应Obx组件的重绘请求,此时框架还未完成当前批次的组件构建,无法同时处理新的重绘请求,就抛出了冲突异常。
结合你底部导航栏的使用场景,90%以上的概率是以下几种情况导致的:
- 主页面的底部导航选中状态修改逻辑直接放在了build方法中,或者和build同步执行的回调函数中,切换导航时一边build页面一边触发状态更新
- 每个导航项对应的子页面在初始化时,直接在Controller的
onInit生命周期中同步修改了自身的可观测变量,而子页面刚好是在主页面的build过程中被创建加载的,主页面还没完成build,子页面的状态更新就触发了新的重绘请求 - 主页面的导航Controller和子页面的Controller存在状态联动:主页面切换导航索引时,同步调用了子Controller的状态更新方法,没有做异步处理,更新逻辑直接嵌套在了build流程里
排查步骤
- 给所有修改可观测变量的代码加打印日志,运行后看日志输出时机是否和组件build的日志重叠,重叠的就是触发冲突的代码
- 检查主页面Obx的包裹范围,是否把整个Scaffold都包进了Obx,导致切换导航时整个页面重绘,连带子页面同步重建触发状态更新
- 检查所有子页面Controller的
onInit、onReady生命周期逻辑,有没有直接同步修改可观测变量的代码
解决方案
- 所有修改可观测变量的逻辑全部移出build流程:不要在build方法里直接修改
.obs变量,也不要放在build时同步调用的函数中 - 子页面初始化状态更新加异步调度:如果必须在
onInit中修改状态,用Future.delayed(Duration.zero, () => 你的状态修改逻辑),把更新操作推到当前build流程结束后再执行 - 缩小主页面Obx的包裹范围:主页面的Obx只包裹底部导航栏组件本身,不要包裹整个body,减少不必要的重绘范围,导航切换的逻辑只放在底部导航的
onTap回调中 - 跨Controller状态更新加帧结束回调:如果主Controller切换导航时需要通知子Controller更新,把更新逻辑放在
WidgetsBinding.instance.addPostFrameCallback((_) => 你的更新逻辑)中,等当前帧绘制完成后再执行
内容的提问来源于stack exchange,提问作者rubone
相关产品推荐
相关产品推荐

