abdalla mohamed
abdalla mohamed

Reputation: 39

stateless Widget Keeps Rebuilding

I'M currently working on a to-do app following a tutorial this is the checkbox part

I'm trying to lift it's state up the tree, but when I do so the stateless widget just keeps rebuilding non stop , I tried adding key , adding const converting it to stateful but nothing seems to work it also doesn't provide me with any kind of error or exceptions messages

note : when I set the onChecked to null the statelessWidget runs twice when it should only build once..

below is the code:

    import 'package:flutter/material.dart';
    
    class TaskTile extends StatefulWidget {
      @override
      _TaskTileState createState() => _TaskTileState();
    }
    
    class _TaskTileState extends State {
      bool isChecked = false;
    
      @override
      Widget build(BuildContext context) {
        return ListTile(
          title: Text(
            "Complete Todoey",
            style: TextStyle(
                decoration: isChecked == true
                    ? TextDecoration.lineThrough
                    : TextDecoration.none),
          ),
          trailing: TaskCheckBox(
              checkBoxState: isChecked,
              toggleCheckBoxState:
                  (bool checkBoxState) {
                Future.delayed(Duration.zero,
                    () async {
                  setState(() {
                    print(
                        "===================$isChecked==============$checkBoxState======================");
                    isChecked = checkBoxState;
                  });
                });
              }),
        );
      }
    }
    
    class TaskCheckBox extends StatelessWidget {
      const TaskCheckBox(
          {Key? key,
          required this.checkBoxState,
          required this.toggleCheckBoxState})
          : super(key: key);
    
      final bool checkBoxState;
      final Function toggleCheckBoxState;
    
      @override
      Widget build(BuildContext context) {
        print(
            "=====================================$checkBoxState+++++++++++++++++++++++++++++++++++++++++");
        return Checkbox(
          value: checkBoxState,
          onChanged:
              null, // toggleCheckBoxState(checkBoxState),
        );
      }
    }


a snip from the console output :

 
Performing hot restart...
Syncing files to device Android SDK built for x86...
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): ===================false==============false======================
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++

a snip from the output when onChanged is set to null :

Restarted application in 2,694ms.
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++
I/flutter ( 3401): =====================================false+++++++++++++++++++++++++++++++++++++++++


Upvotes: 3

Views: 1121

Answers (2)

Mboya Michael
Mboya Michael

Reputation: 46

The issue is that in the onchanged you call the togglecheckboxstate function, not pass it as a parameter. This means that every time the widget is built, the function is called, which calls setstate, which causes the rebuild and the cycle continues infinitely. A quick fix would be to change the call in TaskCheckBox to be as follows:

class TaskCheckBox extends StatelessWidget {
      const TaskCheckBox(
          {Key? key,
          required this.checkBoxState,
          required this.toggleCheckBoxState})
          : super(key: key);
    
      final bool checkBoxState;
      final Function toggleCheckBoxState;
    
      @override
      Widget build(BuildContext context) {
        print(
            "=====================================$checkBoxState+++++++++++++++++++++++++++++++++++++++++");
        return Checkbox(
          value: checkBoxState,
          // take note of the change here
          onChanged:
              (checkBoxState) => toggleCheckBoxState(checkBoxState),
        );
      }
    }

Dart can help you spot this issue early, but you'd have to add types to your function declaration.

final void Function(bool?) toggleCheckBoxState;

or since this is a fairly common function signature, the material package has a useful type definition that can be easier to remember

final ValueChanged<bool?> toggleCheckBoxState;

Also as mentioned by a different answer, the Future.delayed isn't necessary. You can call setState without the future.

The build being called twice when you set onChanted to null is not a problem.

Upvotes: 0

Javad Moradi
Javad Moradi

Reputation: 994

The purpose of setState is to tell the framework that a variable in the state has changed and the widget needs to be rebuilt to reflect that change. So calling setState calls the build function again, which in your case recalls your Future, which calls setState again, which triggers build and so on.

To fix this you could get rid of async function with Future.delayed since it's not needed there at all as well as putting print outside your setstate function.

Upvotes: 6

Related Questions