MVC+S架构Flutter应用中使用Provider的NotifyListeners避免构建报错的最佳实践
解决Provider中
setState() or markNeedsBuild() called during build.错误的最佳实践 你遇到的错误核心原因是:在Flutter Widget的构建流程中调用了notifyListeners(),此时框架正处于构建Widget树的阶段,状态更新操作会打断构建流程,引发冲突。结合你的代码来看,在initState中直接调用Controller的异步方法,方法执行完成后立即触发notifyListeners(),但此时Widget的首次构建尚未完成,导致报错。
以下是规避该问题的最佳实践:
1. 延迟初始化阶段的状态更新到构建完成后
在initState中调用Controller方法时,通过WidgetsBinding.instance.addPostFrameCallback将执行逻辑延迟到Widget首次构建完成后。这样能确保notifyListeners()在构建流程结束后触发,不会干扰当前构建。
修正后的Widget代码:
class Test extends StatefulWidget { @override State<Test> createState() => _TestState(); } class _TestState extends State<Test> { @override void initState() { super.initState(); // 延迟到Widget首次构建完成后执行方法 WidgetsBinding.instance.addPostFrameCallback((_) { locator<TestController>().functionA(); locator<TestController>().functionB(); }); } @override Widget build(BuildContext context) { // 你的Widget构建逻辑 return Container(); } }
2. 异步方法中用微任务延迟状态通知
如果异步方法内的逻辑完成后需要立即更新状态,可以将notifyListeners()包裹在Future.microtask中,让它在当前事件循环的微任务阶段执行,避免与build流程冲突。
修正后的Controller代码:
class TestController extends ChangeNotifier { String? testA; String? testB; Future<void> functionA() async { // 模拟异步业务逻辑 await Future.delayed(const Duration(milliseconds: 100)); testA = 'TestA'; // 延迟通知到微任务队列 Future.microtask(() => notifyListeners()); } Future<void> functionB() async { await Future.delayed(const Duration(milliseconds: 100)); testB = 'TestB'; Future.microtask(() => notifyListeners()); } }
3. 禁止在构建相关方法中直接触发状态更新
永远不要在Widget的build方法、initState的同步代码块、didChangeDependencies等构建流程关键节点中直接调用notifyListeners(),这些阶段的状态更新会触发重复构建或流程冲突。
4. 减少不必要的状态通知
仅当状态确实发生变化时才调用notifyListeners(),避免无意义的更新。比如可以先判断状态值是否真的改变,再执行通知:
Future<void> functionA() async { await Future.delayed(const Duration(milliseconds: 100)); final newTestA = 'TestA'; if (testA != newTestA) { testA = newTestA; Future.microtask(() => notifyListeners()); } }
内容的提问来源于stack exchange,提问作者Henrik
相关产品推荐
相关产品推荐

