'Why is my Checkbox not updating when I pass a callback from a stateful widget to stateless widget?
Im trying to use Lift the State for my Checkbox and I know that by changing my TaskTile to a stateful widget ill be able to solve my problem but I want to know why my Checkbox isnt updating when I pass the setstate as a callback from a stateful widget.Im sorry if this question is dumb im just getting started with Flutter so any help is appreciated.
This is my stateless widget which builds the checkbox in a tile
import 'package:flutter/material.dart';
class TaskTile extends StatelessWidget {
final String title;
final bool isChecked;
final void Function(bool?) checkboxCallback;
TaskTile({required this.title,required this.isChecked,required this.checkboxCallback});
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title,style: TextStyle(decoration: isChecked?TextDecoration.lineThrough:null),),
trailing: Checkbox(
value: isChecked,
onChanged: checkboxCallback,
),
);
}
}
And this is the stateful widget where I am giving the callback
import 'package:flutter/material.dart';
import 'task_tile.dart';
import 'task.dart';
class TaskList extends StatefulWidget {
@override
_TaskListState createState() => _TaskListState();
}
class _TaskListState extends State<TaskList> {
@override
Widget build(BuildContext context) {
bool isChecked = false;
List<Task> tasks = [
Task(title: 'Buy Milk', isDone: isChecked),
Task(title: 'Buy Bread', isDone: isChecked),
Task(title: 'Buy Eggs', isDone: isChecked),
];
return ListView.builder(itemBuilder: (context, index) {
return TaskTile(title: tasks[index].title,isChecked: tasks[index].isDone,checkboxCallback: (checkboxState){
setState(() {
tasks[index].toggleIsDone();
});
},);
},itemCount: tasks.length,);
}
}
Also this is the Task Class where I am giving the toggle function to change the state
class Task{
final String title;
bool isDone;
Task({required this.title,required this.isDone});
void toggleIsDone(){
isDone = !isDone;
}
}
Solution 1:[1]
You are not calling the function. Instead of
trailing: Checkbox(
value: isChecked,
onChanged: checkboxCallback,
),
Can you try
trailing: Checkbox(
value: isChecked,
onChanged: (){checkboxCallback();},
),
Solution 2:[2]
You need to make TaskTile
statefull. TaskList
can be StatelessWidget
.
Here is the answer:
import 'package:flutter/material.dart';
class TaskList extends StatelessWidget {
@override
Widget build(BuildContext context) {
bool isChecked = false;
List<Task> tasks = [
Task(title: 'Buy Milk', isDone: isChecked),
Task(title: 'Buy Bread', isDone: isChecked),
Task(title: 'Buy Eggs', isDone: isChecked),
];
return ListView.builder(
itemBuilder: (context, index) {
return TaskTile(
title: tasks[index].title,
isChecked: tasks[index].isDone,
checkboxCallback: (value) {
tasks[index].toggleIsDone();
},
);
},
itemCount: tasks.length,
);
}
}
class TaskTile extends StatefulWidget {
final String title;
final bool isChecked;
final Function checkboxCallback;
TaskTile(
{required this.title,
required this.isChecked,
required this.checkboxCallback,
Key? key})
: super(key: key);
@override
_TaskTileState createState() => _TaskTileState();
}
class _TaskTileState extends State<TaskTile> {
late bool _isChecked;
@override
void initState() {
super.initState();
_isChecked = widget.isChecked;
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
widget.title,
style: TextStyle(
decoration: _isChecked ? TextDecoration.lineThrough : null),
),
trailing: Checkbox(
value: _isChecked,
onChanged: (value) {
widget.checkboxCallback(value);
setState(() {
_isChecked = value!;
});
},
),
);
}
}
class Task {
final String title;
bool isDone;
Task({required this.title, required this.isDone});
void toggleIsDone() {
isDone = !isDone;
}
}
Solution 3:[3]
For any dynamic
change your widget class
need to be stateful
, if you are using vscode
than you can easily change from stateless to statful
. First hover to your statless
widget and than you will get a bulb like indicator , press it than you will get an option of changing the state.
Solution 4:[4]
According to my understanding, You are trying to change the state (isChanged) of the Task object in a list. This list inturn is used in listview builder. Since flutter work with immutable data and you are sending the reference of the list to the listview builder, the changes of the element in the list wont be reflected in the UI, eventhough you used setState function. Instead try creating a new list with the same name with the updated element. This will definitely work.
I got the idea from this solution - https://stackoverflow.com/a/61847327/11844877
PS I am a newbie to flutter and don't mind if my answer wasn't helpful.
Solution 5:[5]
This is the solution from the link below:
import 'package:flutter/material.dart';
class TaskList extends StatelessWidget {
@override
Widget build(BuildContext context) {
bool isChecked = false;
List<Task> tasks = [
Task(title: 'Buy Milk', isDone: isChecked),
Task(title: 'Buy Bread', isDone: isChecked),
Task(title: 'Buy Eggs', isDone: isChecked),
];
return ListView.builder(
itemBuilder: (context, index) {
return TaskTile(
title: tasks[index].title,
isChecked: tasks[index].isDone,
checkboxCallback: (value) {
tasks[index].toggleIsDone();
},
);
},
itemCount: tasks.length,
);
}
}
class TaskTile extends StatefulWidget {
final String title;
final bool isChecked;
final Function checkboxCallback;
TaskTile(
{required this.title,
required this.isChecked,
required this.checkboxCallback,
Key? key})
: super(key: key);
@override
_TaskTileState createState() => _TaskTileState();
}
class _TaskTileState extends State<TaskTile> {
late bool _isChecked;
@override
void initState() {
super.initState();
_isChecked = widget.isChecked;
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
widget.title,
style: TextStyle(
decoration: _isChecked ? TextDecoration.lineThrough : null),
),
trailing: Checkbox(
value: _isChecked,
onChanged: (value) {
widget.checkboxCallback(value);
setState(() {
_isChecked = value!;
});
},
),
);
}
}
class Task {
final String title;
bool isDone;
Task({required this.title, required this.isDone});
void toggleIsDone() {
isDone = !isDone;
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Rahul Mishra |
Solution 2 | Yeasin Sheikh |
Solution 3 | Manishyadav |
Solution 4 | |
Solution 5 | naji Iz-Aldeen |