I have a StatefulWidget
that contains a ListView()
with a ListView.builder()
. The ListView.builder()
builds several CheckboxListTile()
's using two Lists, one for the labels and one for the booleans. However it is not working as intended. I tried to create a minimal example of the problem, it looks like this:
import 'package:flutter/material.dart';
class TestClass extends StatefulWidget {
@override
_TestClassState createState() => _TestClassState();
}
class _TestClassState extends State<TestClass> {
@override
Widget build(BuildContext context) {
bool firstBoolean = false;
bool secondBoolean = false;
List labels = [
"First Checkbox",
"Second Checkbox",
];
List<bool> booleans = [
firstBoolean,
secondBoolean,
];
return ListView(
children: [
Container(
height: MediaQuery.of(context).size.height * 0.5,
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text("${labels[index]}"),
value: booleans[index],
onChanged: (bool newValue) {
setState(() {
secondBoolean = newValue;
print(
"firstBoolean, secondBoolean: [$firstBoolean, $secondBoolean]");
});
},
);
}),
),
],
);
}
}
This does not change the checkboxes and also not the assigned booleans. If I replace this line:
value: booleans[index]
with
value: firstBoolean
And move the bool firstBoolean = false;
above the @override
in the _TestCassState
It 'works' but since all the checkboxes now have the same boolean variable assigned to them it changes all of them at the same time. But I dont understand why this 'works' and the code above does not. Also if I try to create all the booleans I need above @override
and then try to create a list containing all of them, I get this error:
The instance member 'firstBoolean' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression
If I move to List with the labels above the @override
it doesnt give me this error and assigns the labels correctly
I feel like I am not understanding something fundamental about StatefulWidgets. So I would really aprecciate if somebody could give me an explanation of how to solve this correctly and why this is not working as intended
You don't even need the individual bool variables.
You can basically have your labels
inside your TestClass
since they are not part of the state since they never change.
Next, you can have just the List<bool> booleans
in your State
class since all your changing stuff is just these booleans
itself.
So your structure will be like this now.
class TestClass extends StatefulWidget {
final List labels = [
"First Checkbox",
"Second Checkbox",
];
@override
_TestClassState createState() => _TestClassState();
}
This is good practice to put them in the StatefulWidget
class. You can then use them inside your State
class like this - widget.labels[index]
.
So, your State
class will be,
class _TestClassState extends State<TestClass> {
List<bool> booleans = [false,false];
@override
Widget build(BuildContext context) {
return ListView(
children: [
Container(
height: MediaQuery.of(context).size.height * 0.5,
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text("${widget.labels[index]}"),
value: booleans[index],
onChanged: (bool newValue) {
setState(() {
// There was a logic error here in your code, so changed it to work correctly
booleans[index] = newValue;
});
},
);
}),
),
],
);
}
}