📢 Beta Documentation – This documentation and plugin are under active development. The plugin is live and ready to explore, but docs are in beta. Final release coming in May 2026. Please explore, test, and share feedback!

AsyncNotifierProvider

AsyncNotifier is powerful. It allows handling complex state that has an asynchronous initialization phase or requires mutating operations and reloading.

AsyncNotifier combines the flexibility of Notifier with the asynchronous capabilities of FutureProvider. It manages states wrapped in anAsyncValue and handles background loading and data mutations.

When to Use AsyncNotifier

  • Authentication state with login/logout operations
  • Fetching and creating database records sequentially
  • Refreshing or retrying data fetches manually

Basic Syntax

Create an AsyncNotifier using the generator:

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'todos.g.dart';

@riverpod
class Todos extends _$Todos {
  @override
  FutureOr<List<Todo>> build() async {
    // Perform initial fetch
    return await ref.watch(apiServiceProvider).fetchTodos();
  }

  Future<void> addTodo(Todo todo) async {
    // Set state to loading while keeping previous data (optimistic/background update)
    state = const AsyncValue.loading();
    
    // Add todo via API, then replace state with new valid data
    state = await AsyncValue.guard(() async {
      final newTodo = await ref.read(apiServiceProvider).addTodo(todo);
      final prevTodos = state.valueOrNull ?? [];
      return [...prevTodos, newTodo];
    });
  }
}

State Mutations with AsyncValue.guard

Notice the use of AsyncValue.guard above. This utility simplifies executing an asynchronous callback, catching errors, and automatically returning an AsyncData or AsyncError. It handles stack traces so you keep detailed error tracking.

Using AsyncNotifier in Widgets

Similar to FutureProvider, use the .when() method:

class TodoListWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // Read the async value
    final todosAsync = ref.watch(todosProvider);
    
    return todosAsync.when(
      data: (todos) => ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(title: Text(todos[index].title));
        },
      ),
      loading: () => Center(child: CircularProgressIndicator()),
      error: (error, stack) => Text('Error: $error'),
    );
  }
}