Reputation: 39
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
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
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