setState is not updating UI in Flutter

Issue

I have a dialog box which is a stateful widget with multiple tabs wrapped inside Animated Switcher.
Inside it I have a button which on clicked calls a function switchPage() which has a switch statement with each case setting the state of Widget? currentTab variable to a different one.
The problem here arrives when I use this switchPage() function to change the value of currentTab to a different widget also wrapped in a different method getWidget2()

The code for the
Example on DartPad

Try clicking as I suggest…

  • Click Floating Button.
  • Click on the first checkbox.
  • Now click on PAGE 2 button.
  • Click the second checkbox only once. Now notice the checkbox doesn’t work when clicked.
  • Click PAGE 1 again to go the working checkbox.
  • Now click on PAGE 2 button again. The Checkbox value and state did change but did not update the time it was clicked, but it has updated when we forcefully re visited the checkbox.

I cannot find solution to this anywhere and I really need the code structure to be as optimized as possible.
Please, if anyone has any explanation or any suggestions, it would be greatly appreciated..

Thanks, in advance.

Solution

This is a very rough working example, but you can build upon it.
In the code below, you can see that I have created two methods that handle setting the state of the checkbox for each widget. I have also reset the page once this method is triggered. What this does, is trigger the redrawing of the inner widgets (which is what I explained in my comment).

    class _DemoDialogState extends State<DemoDialog> {
    Widget? currentTab;

    bool valueOfCheckbox1 = false;
    bool valueOfCheckbox2 = false;

    void switchPage(name) {
    switch (name) {
      case 1:
        setState(() {
          currentTab = getWidget1(setCheckbox1State);  // <---- Notice the change here
        });
        break;

      case 2:
        setState(() {
          currentTab = getWidget2(setCheckbox2State);  // <---- Notice the change here
        });
        break;
      }
    }
  
    void setCheckbox1State(bool? newState) {
    if (newState != null) {
       setState(() {  // <---- Notice the change here
        valueOfCheckbox1 = newState;
         currentTab = getWidget1(setCheckbox1State);
      });
    }
    
   }
  
    void setCheckbox2State(bool? newState) {
      if (newState != null) {
       setState(() {  // <---- Notice the change here
        valueOfCheckbox2 = newState;
         currentTab = getWidget2(setCheckbox2State);
      });
     }
    }

  

    Widget getWidget1(Function(bool?) checkboxFunction) {
        return Container(
            child: Row(
          children: [
            Text('Hello from widget 1'),
            Checkbox(
                value: valueOfCheckbox1,
                onChanged: (value) {  // <---- Notice the change here
                  checkboxFunction(value);
                })
          ],
        ));
      }
    
      Widget getWidget2(Function(bool?) checkboxFunction) {
        return Container(
            child: Row(
          children: [
            Text('Hello from widget 2'),
            Checkbox(
                value: valueOfCheckbox2,
                onChanged: (value) {  // <---- Notice the change here
                  checkboxFunction(value);
                })
          ],
        ));
      }

      @override
       Widget build(BuildContext context) {
    return Dialog(
      child: Container(
        width: 280,
        height: 600,
        color: Colors.white,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            AnimatedSwitcher(
              duration: Duration(milliseconds: 250),
              reverseDuration: Duration(milliseconds: 250),
              transitionBuilder: (child, animation) {
                var begin = Offset(0.5, 0);
                var end = Offset.zero;
                var curve = Curves.easeIn;
                var tween = Tween(begin: begin, end: end)
                    .chain(CurveTween(curve: curve));

                var begin2 = 0.0;
                var end2 = 1.0;
                var curve2 = Curves.easeIn;
                var tween2 = Tween(begin: begin2, end: end2)
                    .chain(CurveTween(curve: curve2));

                return SlideTransition(
                  position: animation.drive(tween),
                  child: FadeTransition(
                      opacity: animation.drive(tween2), child: child),
                );
              },
              layoutBuilder: (widget, list) {
                return Align(
                  alignment: Alignment.topCenter,
                  child: widget,
                );
              },  // <---- Notice the change here
              child: currentTab == null ? getWidget1(setCheckbox1State) : currentTab,
            ),
            TextButton(
                onPressed: () {
                  switchPage(1);
                },
                child: Text('PAGE 1')),
            TextButton(
                onPressed: () {
                  switchPage(2);
                },
                child: Text('PAGE 2'))
          ],
        ),
      ),
    );
  }
}

This is just an example that makes things work, but it in no way represents how you should build things appropriately. I would look into separating the code into stateless and stateful widgets.

Answered By – tomerpacific

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *