walther leonardo
walther leonardo

Reputation: 47

riverpod future provider with buttons

My question maybe is simple. I saw tutorials of future provider when loading a page but not in a situation when the user clicks a button or performs an action. Can you share some links of examples where by clicking a button a future provider is used in the same page? I just need something to point me in the right direction.

Upvotes: 2

Views: 833

Answers (1)

Ruble
Ruble

Reputation: 4844

Your answer is as simple as your question :) Consider using AsyncNotifierProvider. This will allow you to have the necessary methods to change your "future state".

In a nutshell, it looks like this:

@immutable
class Todo {
  const Todo({
    required this.id,
    required this.description,
    required this.completed,
  });

  factory Todo.fromJson(Map<String, dynamic> map) {
    return Todo(
      id: map['id'] as String,
      description: map['description'] as String,
      completed: map['completed'] as bool,
    );
  }

  final String id;
  final String description;
  final bool completed;

  Map<String, dynamic> toJson() => <String, dynamic>{
        'id': id,
        'description': description,
        'completed': completed,
      };
}

class AsyncTodosNotifier extends AsyncNotifier<List<Todo>> {
  Future<List<Todo>> _fetchTodo() async {
    final json = await http.get('api/todos');
    final todos = jsonDecode(json) as List<Map<String, dynamic>>;
    return todos.map(Todo.fromJson).toList();
  }

  @override
  Future<List<Todo>> build() async {
    return _fetchTodo();
  }

  Future<void> toggle(String todoId) async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() async {
      await http.patch(
        'api/todos/$todoId',
        <String, dynamic>{'completed': true},
      );
      return _fetchTodo();
    });
  }
}

final asyncTodosProvider =
    AsyncNotifierProvider<AsyncTodosNotifier, List<Todo>>(AsyncTodosNotifier.new);

Your Widget Consumer:

class TodoListView extends ConsumerWidget {
  const TodoListView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncTodos = ref.watch(asyncTodosProvider);

    return asyncTodos.when(
      data: (todos) => ListView(
        children: [
          for (final todo in todos)
            CheckboxListTile(
              value: todo.completed,
              onChanged: (value) =>
                  ref.read(asyncTodosProvider.notifier).toggle(todo.id),
              title: Text(todo.description),
            ),
        ],
      ),
      loading: () => const Center(child: CircularProgressIndicator()),
      error: (err, stack) => Text('Error: $err'),
    );
  }
}

Upvotes: 3

Related Questions