Flutter GetX报错:Obx部件在构建过程中无法被标记为需要重建如何解决?
你在build方法中直接同步执行了getNewOrders方法,该方法第一行的newOrders.clear()是同步修改响应式变量,会立刻触发监听newOrders的Obx组件重建,而此时当前的NewOrdersList组件还处于构建流程中,框架禁止在构建过程中触发新的重建,因此抛出异常。首次加载没报错是因为首次构建时异步请求的时序刚好没撞上,路由切换回来触发重建时同步修改响应式变量的操作就会直接触发冲突。
1. 不要将修改响应式变量的逻辑放在build方法中
build方法会随父组件更新、路由切换等场景频繁触发,直接在里面写请求逻辑不仅会导致报错,还会产生大量无效重复请求。你可以根据场景选择以下方式触发请求:
场景1:仅页面首次加载时拉取数据
如果你用的是StatefulWidget,将请求逻辑放到initState的帧后回调中,确保当前页面构建完成后再执行修改响应式变量的操作:
@override void initState() { super.initState(); // 等待当前帧构建完成后再执行逻辑 WidgetsBinding.instance.addPostFrameCallback((_) { SizeConfig().init(context); ordersC.trackLocation(); ordersC.getNewOrders(context, false); }); }
如果是GetX的GetView页面,也可以将请求逻辑放到对应GetxController的onInit生命周期中。
场景2:每次切回页面都需要拉取最新数据
如果需要路由切回当前页面时都刷新数据,可以通过RouteObserver监听路由的didPopNext事件,在页面回到前台时触发请求:
// 先全局定义RouteObserver final RouteObserver<ModalRoute<void>> routeObserver = RouteObserver<ModalRoute<void>>(); // MaterialApp中注册 MaterialApp( navigatorObservers: [routeObserver], // ...其他配置 ) // 在页面State中订阅路由事件 class _NewOrdersListState extends State<NewOrdersList> with RouteAware { @override void didChangeDependencies() { super.didChangeDependencies(); routeObserver.subscribe(this, ModalRoute.of(context)!); } @override void dispose() { routeObserver.unsubscribe(this); super.dispose(); } @override void didPopNext() { // 切回当前页面时触发刷新 ordersC.getNewOrders(context, false); } // ...其他逻辑 }
2. 优化Controller中的请求逻辑
你当前的代码存在异步逻辑时序问题:订单详情的fetchNewOrders请求还未返回,就已经把未完成赋值的newOrderModel加入了响应式列表,会导致页面展示不全。可以先把所有异步操作做等待处理,全部完成后再一次性更新响应式变量,减少不必要的重建:
getNewOrders(BuildContext context, bool notification) async { isNotification = notification; isLoading.value = true; // 先用临时列表存数据,不要直接修改响应式变量 List<NewOrderModel> tempOrders = []; final resp = await ApiServices().fetchNewOrderList(context); newNextURL = resp['data']['orders']['next_page_url']; var allItems = resp['data']['orders']['items']; if (allItems != null) { // 用Future.wait等待所有详情请求完成 await Future.wait(allItems.map((item) async { if (["assigned", "accepted", "arrived", "collected"].contains(item['status'])) { driverID = item['creator_id'].toString(); NewOrderModel newOrderModel = NewOrderModel( id: item['id'].toString(), distance: item['distance'], fee: item['fee'].toString(), driverID: item['creator_id'].toString(), status: item['status'], date_time: item['date_time'] ); // 等待详情请求完成再赋值 final secondResp = await ApiServices().fetchNewOrders(context, newOrderModel.id); newOrderModel.firebase_token = secondResp['data']['user_firebase_token']; newOrderModel.lat = secondResp['data']['address']['lat']; newOrderModel.lng = secondResp['data']['address']['lng']; newOrderModel.address = secondResp['data']['address']['address']; newOrderModel.addressNote = secondResp['data']['address']['address_notes']; newOrderModel.driverID = driverID; newOrderModel.name = secondResp['data']['name']; newOrderModel.mobile = secondResp['data']['mobile']; newOrderModel.estimated_delivery_time = secondResp['data']['estimated_delivery_time']; newOrderModel.order_notes = secondResp['data']['order_notes']; tempOrders.add(newOrderModel); } })); } // 所有数据处理完成后一次性更新响应式变量 newOrders.assignAll(tempOrders); isLoading.value = false; }
3. 临时应急方案
如果暂时不想调整调用位置,可以将getNewOrders中所有修改响应式变量的操作包在帧后回调中,避开构建期:
getNewOrders(BuildContext context, bool notification) async { WidgetsBinding.instance.addPostFrameCallback((_) { newOrders.clear(); // 其他原有逻辑 }); }
内容的提问来源于stack exchange,提问作者Ahmed Wagdi

