Reputation: 13
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
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