flutter setState() or markNeedsBuild() called during build.This Obx widmet cannot be marked as ne ing to build because the framework is already in the process of building widgets.
-
setState() or markNeedsBuild() called during build.This Obx widmet cannot be marked as ne ing to build because the framework is already in the process of building widgets.
-
问题现象
-
问题原因
- 构建期间状态更新:在
build()方法执行期间,你可能在某个地方触发了状态更新(例如直接修改Rx变量或调用setState())。 - GetX 的
Obx问题:使用Obx时,如果在构建期间修改其依赖的 observable 变量,会触发markNeedsBuild()。
-
解决方案
-
避免在
build()中直接修改状态
检查
build() 方法中是否有直接或间接修改状态的代码(例如赋值 Rx变量.value = ...)。将其移至事件回调(如 onPressed)或使用延迟执行。// ❌ 错误示例:在 build 中修改状态Widget build(BuildContext context) {
varcontroller= Get.find<MyController>();
controller.rxValue.value = 42; // 触发 Obx 更新return Obx(() => Text(controller.rxValue.value.toString()));
}
// ✅ 正确做法:将状态修改移至安全位置(如生命周期方法或事件)Widget build(BuildContext context) {
varcontroller= Get.find<MyController>();
return ElevatedButton(
onPressed: () => controller.rxValue.value = 42, // 在事件中更新 child: Obx(() => Text(controller.rxValue.value.toString())),
);
}
-
使用
WidgetsBinding.instance.addPostFrameCallback
如果必须在初始化时更新状态,将操作推迟到当前构建帧之后:
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
// 在当前帧结束后执行
var controller = Get.find<MyController>();
controller.rxValue.value = 42;
});
return Obx(() => Text(Get.find<MyController>().rxValue.value.toString()));
}
-
检查 GetX Controller 的初始化
确保 Controller 的初始化不会触发不必要的更新。例如,避免在
onInit 中立即修改状态(除非必要),或使用 Get.put(Controller()) 而非直接依赖 Obx 的上下文。-
避免在 onInit 中立即修改状态
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ProblematicController extends GetxController {
final userData = <String, dynamic>{}.obs;
final isLoading = true.obs;
@override
void onInit() {
super.onInit();
// ❌ 问题:在 onInit 中立即修改状态
// 这会在构建过程中触发 Obx 更新,导致错误
isLoading.value = false;
userData.value = {
'name': 'John Doe',
'email': 'john@example.com',
'age': 30
};
// 模拟异步数据加载
loadUserData();
}
Future<void> loadUserData() async {
// 模拟 API 调用延迟
await Future.delayed(Duration(seconds: 2));
// 再次更新状态
userData.value = {
'name': 'Jane Smith',
'email': 'jane@example.com',
'age': 28
};
}
}
//方案一
class ProblematicExample extends StatelessWidget {
final ProblematicController controller = Get.put(ProblematicController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Controller 初始化问题示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 监听 isLoading 状态
Obx(() => controller.isLoading.value
? CircularProgressIndicator()
: SizedBox.shrink()),
SizedBox(height: 20),
// 监听 userData 状态
Obx(() => Text(
'用户: ${controller.userData['name']}',
style: TextStyle(fontSize: 20),
)),
SizedBox(height: 20),
ElevatedButton(
onPressed: controller.loadUserData,
child: Text('重新加载数据'),
),
],
),
),
);
}
}
class FixedController extends GetxController {
final userData = <String, dynamic>{}.obs;
final isLoading = true.obs;
@override
void onInit() {
super.onInit();
// ✅ 正确做法:不在 onInit 中立即修改状态
// 初始状态已经在变量定义时设置好了
// 直接调用异步方法
loadUserData();
}
Future<void> loadUserData() async {
// 模拟 API 调用延迟
await Future.delayed(Duration(seconds: 2));
// 更新状态
isLoading.value = false;
userData.value = {
'name': 'Jane Smith',
'email': 'jane@example.com',
'age': 28
};
}
}
-
使用 GetBuilder 替代 Obx 进行精细控制
class ControlledInitController extends GetxController {
final userData = <String, dynamic>{};
final isLoading = true;
@override
void onInit() {
super.onInit();
// 直接修改普通变量,不会触发更新
isLoading = false;
userData = {
'name': 'John Doe',
'email': 'john@example.com',
'age': 30
};
loadUserData();
}
Future<void> loadUserData() async {
await Future.delayed(Duration(seconds: 2));
// 更新状态并手动通知监听器
isLoading = false;
userData = {
'name': 'Jane Smith',
'email': 'jane@example.com',
'age': 28
};
update(); // 手动触发更新
}
}
class ControlledInitExample extends StatelessWidget {
final ControlledInitController controller = Get.put(ControlledInitController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('精细控制示例'),
),
body: Center(
child: GetBuilder<ControlledInitController>(
builder: (controller) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
controller.isLoading
? CircularProgressIndicator()
: SizedBox.shrink(),
SizedBox(height: 20),
Text(
'用户: ${controller.userData['name']}',
style: TextStyle(fontSize: 20),
),
],
),
),
),
);
}
}
-
检查嵌套的
Obx或GetBuilder
避免在多个嵌套的响应式组件中同时更新状态,这可能导致循环更新。
-
错误示例
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class NestedObxController extends GetxController {
var counter1 = 0.obs;
var counter2 = 0.obs;
void incrementCounter1() {
counter1.value++;
}
void incrementCounter2() {
counter2.value++;
}
}
class NestedObxExample extends StatelessWidget {
final NestedObxController controller = Get.put(NestedObxController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('嵌套 Obx 示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 外部 Obx
Obx(() {
print("外部 Obx 重建: ${controller.counter1.value}");
// ❌ 问题代码:在构建过程中修改另一个状态变量
// 这会导致内部 Obx 也触发重建,形成循环
if (controller.counter1.value > 0) {
controller.counter2.value = controller.counter1.value * 2;
}
return Text(
'计数器 1: ${controller.counter1.value}',
style: TextStyle(fontSize: 20),
);
}),
SizedBox(height: 20),
// 内部 Obx
Obx(() {
print("内部 Obx 重建: ${controller.counter2.value}");
// ❌ 同样的问题:在构建过程中修改另一个状态变量
if (controller.counter2.value > 10) {
controller.counter1.value = controller.counter2.value ~/ 2;
}
return Text(
'计数器 2: ${controller.counter2.value}',
style: TextStyle(fontSize: 20),
);
}),
SizedBox(height: 20),
ElevatedButton(
onPressed: controller.incrementCounter1,
child: Text('增加计数器 1'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: controller.incrementCounter2,
child: Text('增加计数器 2'),
),
],
),
),
);
}
}
-
问题分析
在上面示例中,我们有两个嵌套的 Obx 组件:
- 外部 Obx 监听
counter1,但在构建过程中修改了counter2 - 内部 Obx 监听
counter2,但在构建过程中修改了counter1
这会导致:
- 当
counter1变化时,外部 Obx 重建,修改counter2 counter2的变化触发内部 Obx 重建,修改counter1counter1的变化再次触发外部 Obx 重建...- 形成无限循环,最终导致 Flutter 抛出错误
-
解决方案
- 避免在构建过程中修改状态
将状态修改逻辑移到事件处理函数中:
class FixedNestedObxController extends GetxController {
var counter1 = 0.obs;
var counter2 = 0.obs;
void incrementCounter1() {
counter1.value++;
// 将状态更新逻辑移到事件处理中
if (counter1.value > 0) {
counter2.value = counter1.value * 2;
}
}
void incrementCounter2() {
counter2.value++;
// 将状态更新逻辑移到事件处理中
if (counter2.value > 10) {
counter1.value = counter2.value ~/ 2;
}
}
}
class FixedNestedObxExample extends StatelessWidget {
final FixedNestedObxController controller = Get.put(FixedNestedObxController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('修复后的嵌套 Obx 示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 外部 Obx - 只负责显示,不修改状态
Obx(() => Text(
'计数器 1: ${controller.counter1.value}',
style: TextStyle(fontSize: 20),
)),
SizedBox(height: 20),
// 内部 Obx - 只负责显示,不修改状态
Obx(() => Text(
'计数器 2: ${controller.counter2.value}',
style: TextStyle(fontSize: 20),
)),
SizedBox(height: 20),
ElevatedButton(
onPressed: controller.incrementCounter1,
child: Text('增加计数器 1'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: controller.incrementCounter2,
child: Text('增加计数器 2'),
),
],
),
),
);
}
}
- 使用延迟更新
如果确实需要在状态间建立依赖关系,可以使用
WidgetsBinding.instance.addPostFrameCallback 延迟更新:Obx(() {
print("外部 Obx 重建: ${controller.counter1.value}");
// 延迟状态更新到当前帧结束后
WidgetsBinding.instance.addPostFrameCallback((_) {
if (controller.counter1.value > 0) {
controller.counter2.value = controller.counter1.value * 2;
}
});
return Text(
'计数器 1: ${controller.counter1.value}',
style: TextStyle(fontSize: 20),
);
}),
浙公网安备 33010602011771号