Reputation: 1530
The riverpod (v2) documentation contains two great examples how a TODO-list could be implemented using either a Notifier or an AsyncNotifier. Both examples are functionally equivalent.
To pick one particular detail the non-async example contains a remove method like this
// Let's allow removing todos
void removeTodo(String todoId) {
// Again, our state is immutable. So we're making a new list instead of
// changing the existing list.
state = [
for (final todo in state)
if (todo.id != todoId) todo,
];
}
Whereas the async version looks like this
// Let's allow removing todos
Future<void> removeTodo(String todoId) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await http.delete('api/todos/$todoId');
return _fetchTodo();
});
}
I'd now like to modify the async version to remove the deleted TODO item from it's internal state instead of re-fetching the whole collection via HTTP (which is what _fetchTodo does). I guess a practical reason could be to implement something like optimistic updates, but in this case it's rather a learning experience for me.
Upvotes: 4
Views: 3586
Reputation: 4844
I can assume that you can quickly search and change/delete an object in an array (Map) by knowing its individual id. You can delete an object remotely and also change the local state manually without getting the list again. You may have to pass an additional parameter bool notifyListeners = false
in this case, so that you do not have to rebuild the provider when the remote provider changes (if the remote one depends on the local one). It all looks pretty strange, though.
Upvotes: 0
Reputation: 1405
If I understood correctly, you want to remove a TODO item from the list in the state without re-fetching everything from the API/scratch. In that case:
Assuming you have a List<ToDoItem> listOfTodoItems
inside your state, this is how you'd remove that item:
void updateToDoList(ToDoItem itemToRemove){
//Step 1: find the index of the item we're searching the list for
final indexOfSearchItem = listOfToDoItems.indexWhere((currentToDoItem)=> currentToDoItem == ToDoItem(toDoValue: 'Math class'));
//Step 2: copy the whole list so we can replace the old list with the updated one
final listToUpdate = state.listOfTodoItems;
//Step 3: remove the item from the list we're going to update
listToUpdate.removeAt(indexOfSearchItem);
//Step 4: update the state's list with the new updated list
state = state.copyWith(listOfTodoItems: listToUpdate);
}
Points to note, I directly used Equatable for this, but this could've been done by overriding == operator
and hashCode
methods.
I directly used copyWith
method, again assuming that you already have this in your data class or generated using freezed
package.
I used a simple sample of ToDoListItem
but this idea applies to most of these examples. A more complex situation would be working with nested lists within nested lists in data classes.
This way you're only removing data within state management boundaries and you're not sending/receiving updated ToDoItem's to/from the backend.
Upvotes: 2
Reputation: 138
Without having tested this code in any way, I think this might be able to do the trick.
The issue is with error handling in the API call and how that will influence the assigning of the new filtered state to your actual AsyncNotifier
state. For that you might want to wrap the API call with a try/catch or take action depending on the response from your server.
Future<void> removeTodo(String todoId) async {
// We need the original state, but with the todo removed.
final filteredState = [
for (final todo in state.requireValue)
if (todo.id != todoId) todo,
];
// With the correct new state now saved in a local variable, set the loading value to the state.
state = const AsyncValue.loading();
// Perform delete
await http.delete('api/todos/$todoId');
// Set the value of the new filtered state to our state
// Note: we do need to assign this value with an assign action, because of the immutable nature of the state.
state = AsyncValue.data(filteredState);
}
Upvotes: 1