'Obx ListView not updating after value changes

I am having a RxList called todoData. On the basis of this list, a ListView.builder builds a list and in each list there is a button. When this button is clicked the done field of the respective item is updated to either true or false. But, though the value is updated the ui is no changing.
Here's the list:

class StateController extends GetxController {
  RxList<Todo> todoData = <Todo>[
Todo(
    name: 'todo1',
    done: true),
Todo(
    name: 'todo2',
    done: false),
Todo(
    name: 'todo3',
    done: true),
Todo(
    name: 'todo4',
    done: false),
Todo(
    name: 'todo5',
    done: false)
 ].obs;
}

Controller:

  final StateController _controller = Get.find();

The update function:

void updateItem(Todo e) {
  /* final int index = _controller.todoData.indexOf(e);
  _controller.todoData[index].done = !e.done; */
  _controller.todoData.firstWhere((Todo i) => i == e).done = !e.done;
  _controller.refresh();
}

void deleteItem(Todo e) {     //**this works**
  final int index = _controller.todoData.indexOf(e);
  _controller.todoData.removeAt(index);
}

Ui:

Obx(() => ListView.builder(
                          itemCount: _controller.todoData.length,
                          itemBuilder: (_, int i) => TodoItem(
                              item: _controller.todoData[i],
                              updateItem: () =>
                                  updateItem(_controller.todoData[i]),
                        ))

Any help is greatly appreciated!



Solution 1:[1]

Try calling refresh() directly on the RxList:

void updateItem(Todo e) {
  /* final int index = _controller.todoData.indexOf(e);
  _controller.todoData[index].done = !e.done; */
  _controller.todoData.firstWhere((Todo i) => i == e).done = !e.done;
  _controller.todoData.refresh(); // <- Here
}

Solution 2:[2]

i just modify some code of yours, just change the RxList to simple List and bool variable to RxBool and use obx on bool I'm sharing code

Your Model class and controller class

    class StateController extends GetxController {
      List<Todo> todoData = <Todo>[
        Todo(name: 'todo1', done: true.obs),
        Todo(name: 'todo2', done: false.obs),
        Todo(name: 'todo3', done: true.obs),
        Todo(name: 'todo4', done: false.obs),
        Todo(name: 'todo5', done: false.obs)
      ].obs;
    }
    
    class Todo {
      String? name;
      RxBool? done;
      Todo({this.name, this.done});
    }

your UI Code

    class TestClass extends StatelessWidget {
      TestClass({Key? key}) : super(key: key);
      final StateController _controller = Get.put(StateController());
    
      void updateItem(Todo e) {
        /* final int index = _controller.todoData.indexOf(e);
      _controller.todoData[index].done = !e.done; */
        _controller.todoData.firstWhere((Todo i) => i == e).done!.value =
            !e.done!.value;
        _controller.refresh();
      }
    
      void deleteItem(Todo e) {
        //**this works**
        final int index = _controller.todoData.indexOf(e);
        _controller.todoData.removeAt(index);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.white,
            body: ListView.builder(
                itemCount: _controller.todoData.length,
                itemBuilder: (_, int i) => TodoItem(
                      item: _controller.todoData[i],
                      updateItem: () => updateItem(_controller.todoData[i]),
                    )));
      }
    }
    
    class TodoItem extends StatelessWidget {
      TodoItem({Key? key, this.item, this.updateItem}) : super(key: key);
      Todo? item;
      VoidCallback? updateItem;
    
      @override
      Widget build(BuildContext context) {
        return ListTile(
          title: Text(
            item!.name!,
            style: TextStyle(color: Colors.black),
          ),
          subtitle: GestureDetector(
            onTap: updateItem,
            child: Obx(() {
              return Text(
                item!.done!.value.toString(),
                style: TextStyle(color: Colors.black),
              );
            }),
          ),
        );
      }
    }

Solution 3:[3]

There are a few things you can do here to make your code work as expected.

First, you need to make sure that your Todo class implements the Equatable mixin. This will allow you to compare two Todo instances by value rather than by reference.

Second, you need to use the update() method on your RxList to update the value of the done field for the appropriate Todo instance.

Third, you need to call the refresh() method on your StateController after updating the done field so that the UI will be updated to reflect the new value.

Fourth, you should consider using the TodoItem widget's built-in updateItem() method rather than passing a callback to the widget. This will ensure that the TodoItem widget is updated correctly when the done field is changed.

Solution 4:[4]

Try calling update() function after update List.

void updateItem(Todo e) {
     final int index = _controller.todoData.indexOf(e);
     _controller.todoData[index].done = !e.done;
     _controller.todoData.firstWhere((Todo i) => i == e).done = !e.done;
     _controller.update(); // <- Here
   }

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 Suporte01
Solution 2 Hamza Siddiqui
Solution 3 Carter McKay
Solution 4 Abdul Rahman Panhyar