Search code examples
flutterflutter-getx

FlutterError (setState() or markNeedsBuild() called during build when using getX?


I have a timerController:

class TimerController extends GetxController {
  int secondsRemaining = 5;

  late Timer timer;
  void startTimer() {
    timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
      secondsRemaining--;
      update();
      // Check if the timer has reached 0
      if (secondsRemaining <= 0) {
        // Stop the timer when it reaches 0
        stopTimer();
      }
    });
  }

  void stopTimer() {
    timer.cancel();
  }
}

And I init the controller in PageA:

class _PageAState extends State<PageA> {
  TimerController timerController =
      Get.put(TimerController());
}

Then I push to PageB:

Get.to(() => PageB());
class _PageBState extends State<PageB> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
    body: GetBuilder<TimerController>(builder: (controller) {
                      if (controller.secondsRemaining == 0) {
                        //error here!
                        Get.back();
                      }
                      return Text('${controller.secondsRemaining}s',
                          textAlign: TextAlign.right);
                    }),
  );
  }
}

It crashed when calling Get.back(), and the reason is FlutterError (setState() or markNeedsBuild() called during build.

How to solve this?


Solution

  • During build process it's not appropriate calling setState() which invoke the rebuild again. That cause the errore you are getting.

    You can use a different approach to call Get.back(); getting it out from the build method or if it's necessary to call it in that point you have to use something like this (code below) which allows you to register a Callable by WidgetsBinding.instance.addPostFrameCallback after the build process has finished.

    typedef ActionAfterBuild = void Function();
    
    class _PageBState extends State<PageB> {
      ActionAfterBuild? actionAfterBuild;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: GetBuilder<TimerController>(builder: (controller) {
            if (controller.secondsRemaining == 0) {
              //error here!
              actionAfterBuild = Get.back();
            }
            return Text('${controller.secondsRemaining}s', textAlign: TextAlign.right);
          }),
        );
      }
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted && actionAfterBuild != null) {
            actionAfterBuild!.call();
            actionAfterBuild = null;
          }
        });
      }
    }