There is a drawer with list tiles containing check all and uncheck all buttons, When I click on the check all button it should check the checkbox of all the list items but the UI changes or updates only after I click any other button, the uncheck button also produces the same issue.
drawer: Drawer(
child: Obx(() => ListView(
padding: EdgeInsets.zero,
children: <Widget>[
UserAccountsDrawerHeader(
decoration: BoxDecoration(
color: (Get.isDarkMode) ? Colors.black26 : Colors.grey[300],
),
accountName: Text('Reminders',
style: TextStyle(
fontSize: 30.0,
color: Theme.of(context).textTheme.headline1!.color,
)),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
child: Image(
image: AssetImage("assets/App_icon.png"),
)),
accountEmail: null,
),
ListTile(
title: Text("Check all",
style: TextStyle(
fontSize: 17.0,
color: Theme.of(context).textTheme.headline1!.color)),
leading: Icon(Icons.check_box),
onTap: () {
checkAll();
},
),
ListTile(
title: Text("Uncheck all",
style: TextStyle(
fontSize: 17.0,
color: Theme.of(context).textTheme.headline1!.color)),
leading: Icon(Icons.check_box_outline_blank),
onTap: () {
unCheckAll();
},
),
ListTile(
title: Text(
(Get.isDarkMode)
? 'Change theme: light'
: 'change theme: dark',
style: TextStyle(
fontSize: 17.0,
color: Theme.of(context).textTheme.headline1!.color),
),
leading: Icon(Icons.color_lens_sharp),
onTap: () {
if (Get.isDarkMode) {
Get.changeThemeMode(ThemeMode.light);
} else {
Get.changeThemeMode(ThemeMode.dark);
}
},
),
ListTile(
title: Text(
"Delete all",
style: TextStyle(
fontSize: 17.0,
color: Theme.of(context).textTheme.headline1!.color),
),
leading: Icon(Icons.delete),
onTap: () {
todoController.todos.clear();
},
),
ListTile(
title: Text(
"No of tasks: ${todoController.todos.length}",
style: TextStyle(
fontSize: 20,
color: Theme.of(context).textTheme.headline1!.color),
),
onTap: () {},
),
],
)),
Check all and uncheck all function:
void checkAll() {
TodoController todoController = Get.put(TodoController());
for (var i = 0; i < todoController.todos.length; i ) {
todoController.todos[i].done = true;
}
GetStorage().write('todos', todoController.todos.toList());
}
void unCheckAll() {
TodoController todoController = Get.put(TodoController());
for (var i = 0; i < todoController.todos.length; i ) {
todoController.todos[i].done = false;
}
GetStorage().write('todos', todoController.todos.toList());
}
CodePudding user response:
Although it may seem weird but doing this will solve it:
....
todoController.todos.assignAll(todoController.todos.toList());
GetStorage().write('todos', todoController.todos.toList());
Why?
Assuming the todoController.todos
is a RxList, changing the properties of the individual elements of the list has no effect on the underlying stream. And therefore despite the values changed it will not trigger a rebuild on the Obx
.
But doing something like todoController.todos.assignAll(todoController.todos.toList());
actually changes the underlying stream/value of the RxList. Thus triggers the rebuild as it should be.
When working with Rx or observables, always keep in mind that Obx/GetX
will only, and only trigger rebuild if the actual value
of the Rx/Observable is changed.
Something like todoController.todos[i].done = true;
means changing the value of the done
property of i th element of the RxList and not the value of the actual RxList. assignAll
does that.
Same applies to other Rxs as well.
Suppose the following is a Rx
object of the SomeModel
class:
final someModel= SomeModel().obs;
Doing something like:
someModel.value.someProperty = someValue;
will not trigger the UI rebuild but doing something like this:
someModel.value.someProperty = someValue;
someModel.value = someModel.value;
will.
But it's a bit weird right?
In that case something like copyWith
utility method can be useful.