客户端开发
Obx 里读了 Rx,为什么仍提示 improper use?
GetX 仅在 Obx 回调同步执行栈内追踪响应式变量;当读取被 Builder 或 ListView.builder 推迟到后续构建阶段时即会报错。
在维护使用 GetX 的 Flutter 页面时,偶尔会遇到页面构建直接抛出异常:
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
检查报错位置的 Obx(() { ... }),代码块里已经包含了 controller.someRx.value,初看容易怀疑是 Controller 注册顺序或响应式字段声明有误。
原因:同步追踪栈被延迟回调隔断
Obx 并不是通过静态语法扫描来识别响应式依赖的,而是在执行它自己的 builder 闭包前打开一个临时的全局监听上下文,把本次函数同步调用栈期间触发了 getter 的 Rx 变量登记为当前 Widget 的依赖,函数返回后立即关闭监听。
如果 Obx 直接返回了一个 Builder、LayoutBuilder 或 ListView.builder,而 .value 的读取全部写在内层 builder 回调里:
// 错误写法:Obx 同步执行时只创建了 Builder 对象,并未真正执行内层闭包
return Obx(() {
return Builder(
builder: (context) {
final status = controller.vipStatus.value;
return Text(status.label);
},
);
});
在 Obx 自己的闭包返回那一刻,内层 Builder.builder 还没有被 Flutter 框架调用。对 GetX 而言,这次构建期间捕获到的可观察对象数量为 0,于是直接判定为 improper use。
修复对照
把需要监听的 Rx 变量提到 Obx 的直接同步作用域内读取,再把读出的普通值传给内层组件即可:
// 正确写法:在 Obx 直接执行栈内完成 Rx 读取
return Obx(() {
final status = controller.vipStatus.value;
return Builder(
builder: (context) {
return Text(status.localizedLabel(context));
},
);
});
同理,在搭配 ListView.builder 时,如果外层需要根据列表增删重绘,也必须在 Obx 顶层先读取 itemCount: controller.items.length,而不能只在 itemBuilder 回调里访问 controller.items[index]:
return Obx(() {
final items = controller.items;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Text(item.title);
},
);
});
如果内层套 Builder 只是为了获取 BuildContext,直接从外层方法传入 context 会更简洁;若单个列表项内部还有独立高频变化的字段,可以将小范围的 Obx 放到列表项内部,避免用一个外层 Obx 包裹整棵页面子树。