Arthur Becker
Arthur Becker

Reputation: 13

Flutter Riverpod not rebuilding after reordering a list

I'm doing a todolist with riverpod. I have a ReordorableListView that contains tasks. When I drag and drop a task to reorder it, I'm calling a function to reorder them (updateTasksOrder).

The algo is good but it's not rebuilding the list. For example, if I delete the task, this one is deleted on screen. It looks like Riverpod don't care about the new order in the list. I used a setState additionally (part in comment in the function) and it works but I guess that's not the right way to do it.

Can someone help me? :)

// all tasks
final tasksProvider = NotifierProvider<TodoList, List<Task>>(TodoList.new);

// to do, in progress or done
final taskTypeProvider = StateProvider<TaskType>((ref) => TaskType.todo);

// return tasks by type (to do, in progress or done)
final filteredTasks = Provider<List<Task>>((ref) {
  final filter = ref.watch(taskTypeProvider);
  final todos = ref.watch(tasksProvider);

  switch (filter) {
    case TaskType.todo:
      return todos.where((task) => task.type == TaskType.todo).toList();
    case TaskType.inProgress:
      return todos.where((task) => task.type == TaskType.inProgress).toList();
    case TaskType.done:
      return todos.where((task) => task.type == TaskType.done).toList();
  }
});
class MyApp extends ConsumerStatefulWidget {
  @override
  ConsumerState<MyApp> createState() => _MyAppState();
}

class _MyAppState extends ConsumerState<MyApp> {
@override
  Widget build(BuildContext context) {
    // filtered tasks
    final tasks = ref.watch(filteredTasks);

    void updateTasksOrder(int oldIndex, int newIndex) {
      // task that has to move
      final Task taskToMove = tasks[oldIndex];

      // task that has the wanted index
      final Task taskBefore = tasks[newIndex];

      ref.watch(tasksProvider.notifier).reorder(taskBefore: taskBefore, taskToMove: taskToMove);

      // setState(() {
      //   tasks.removeAt(oldIndex);
      //   tasks.insert(newIndex, taskToMove);
      // });
    }

    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(title: const Text('To Do List')),
          body: Padding(
            padding: const EdgeInsets.all(15),
            child: Column(children: [
              Row(
                children: [
                  TextButton(
                    child: const Text("To Do"),
                    onPressed: () {
                      ref.watch(taskTypeProvider.notifier).state = TaskType.todo;
                    }
                  ),
                  TextButton(
                    child: const Text("In Progress"),
                    onPressed: () {
                      ref.watch(taskTypeProvider.notifier).state = TaskType.inProgress;
                    }
                  ),
                  TextButton(
                    child: const Text("Done"),
                    onPressed: () {
                      ref.watch(taskTypeProvider.notifier).state = TaskType.done;
                    }
                  )
                ]
              ),
                SizedBox(
                height: 500,
                child: ReorderableListView(
                  onReorder: (oldIndex, newIndex) => updateTasksOrder(oldIndex, newIndex),
                  children: [
                    for (var i = 0; i < tasks.length; i++) ...[
                      ProviderScope(
                          key: ValueKey(tasks[i]),
                          overrides: [
                            _currentTask.overrideWithValue(tasks[i]),
                          ],
                          child: const TaskItem()),
                    ]
                  ],
                ),
              ),
            ]),
          )),
    );
  }
}
class Task{
  final String id;
  String description;
  TaskType type;

  Task({
    required this.description,
    required this.id,
    this.type = TaskType.todo,
  });
}

class TodoList extends Notifier<List<Task>> {
  @override
  List<Task> build() => [
    Task(id: 'todo-0', description: 'Task 0'),
    Task(id: 'todo-1', description: 'Task 1', type: TaskType.inProgress),
    Task(id: 'todo-2', description: 'Task 2', type: TaskType.done),
    Task(id: 'todo-3', description: 'Task 3'),
    Task(id: 'todo-4', description: 'Task 4')
  ];

  void remove(Task target) {
    state = state.where((task) => task.id != target.id).toList();
  }

  void reorder({required Task taskBefore, required Task taskToMove}){
    state.remove(taskToMove);
    int beforeIndex = state.indexOf(taskBefore);
    state.insert(beforeIndex, taskToMove);
  }

}

Upvotes: 1

Views: 230

Answers (1)

Randal Schwartz
Randal Schwartz

Reputation: 44186

Your reorder function is mutating state, not replacing it completely. You need to update the identity of state in order for it to trigger an event to be emitted.

Upvotes: 0

Related Questions