You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GetX二次跳转HomeScreen触发setState构建异常问题咨询

问题描述

使用GetX框架开发Flutter应用时,首次从EventScreen跳转至HomeScreen一切正常;但通过左侧抽屉菜单返回EventScreen后,再次跳转至HomeScreen时触发以下未处理异常:

[GETX] Instance "EventsScreenController" has been initialized
[GETX] GOING TO ROUTE /HomeScreen
[GETX] Instance "HomeScreenController" has been created
[GETX] Instance "HomeScreenController" has been initialized
[GETX] GOING TO ROUTE /EventsScreen
[GETX] GOING TO ROUTE /HomeScreen
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: setState() or markNeedsBuild() called during build.
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.
The widget on which setState() or markNeedsBuild() was called was:
Obx
The widget which was currently being built when the offending call was made was:
Builder
#0 Element.markNeedsBuild. (package:flutter/src/widgets/framework.dart:4634:9)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4646:6)
#2 State.setState (package:flutter/src/widgets/framework.dart:1153:15)
#3 _ObxStat<…>

HomeScreen相关代码片段:

child: Obx(
  () {
    return controller.isLoading.value
        ? const Center(child: CircularProgressIndicator())
        : controller.isError.value
            ? ErrorCard(
                controller: controller,
                retryFunction: () => controller.getEvents(),
              )
            : controller.events.isEmpty
                ? const Text(
                    "There are no events available for the defined filter") //TODO: Show ErrorCard and set retryFuntion: openFilter of events (popup)
                : ListView.builder(
                    // ... 列表构建逻辑
                    )
  })
原因分析

这个异常的核心原因是第二次进入HomeScreen时,响应式变量在Widget构建周期内被修改,触发了Obx的重绘请求,违反了Flutter的构建规则:

  1. 首次进入HomeScreen时,HomeScreenController是全新创建的,初始化逻辑(如getEvents())通常在onInit中执行,此时Widget尚未开始构建,变量更新不会与构建流程冲突。
  2. 第二次进入时,若HomeScreenController是GetX默认的全局单例(Get.put默认permanent: true),控制器实例已存在,onInit不会再次执行。如果此时你在build方法、路由回调等构建阶段调用了controller.getEvents(),该方法会同步修改isLoading、events等响应式变量——而此时Builder正处于构建Obx的过程中,变量更新触发的markNeedsBuild会直接抛出异常。
解决方案

针对这个问题,提供几种可行的修复方式:

  • 延迟数据请求到构建完成后:
    如果之前在build中调用controller.getEvents(),改为在initState中通过addPostFrameCallback延迟执行:

    @override
    void initState() {
      super.initState();
      WidgetsBinding.instance.addPostFrameCallback((_) {
        final controller = Get.find<HomeScreenController>();
        controller.getEvents();
      });
    }
    
  • 在控制器中处理异步延迟:
    修改getEvents方法,将变量更新逻辑延迟到当前构建周期结束后执行:

    void getEvents() async {
      await Future.delayed(Duration.zero); // 确保在当前构建完成后修改变量
      isLoading.value = true;
      try {
        events.value = await eventApi.fetchEvents();
        isError.value = false;
      } catch (e) {
        isError.value = true;
      } finally {
        isLoading.value = false;
      }
    }
    
  • 调整控制器生命周期策略:
    如果不需要HomeScreenController保持全局单例,可在注册时设置permanent: false,这样每次进入HomeScreen都会创建新实例,onInit会重新执行:

    Get.put(HomeScreenController(), permanent: false);
    

内容的提问来源于stack exchange,提问作者skm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 05:37:53