Flutter, Riverpod, flutter_riverpod

Riverpod 3.0: The Complete Guide for Flutter Developers

Published on March 05, 2026

Riverpod 3.0: The Complete Guide for Flutter Developers

Riverpod 3.0: The Complete Guide for Flutter Developers

Flutter developers often struggle with state management — keeping their apps organized, testable, and maintainable. Riverpod 3.0 is a modern solution that improves on Provider with better safety, flexibility, and scalability.

In this article, we’ll explore Riverpod 3.0, its key features, and practical examples for your Flutter projects.


What is Riverpod?

Riverpod is a reactive dependency injection and state management library. Unlike the older Provider package:

  • It’s compile-time safe
  • Supports global and local providers
  • Works seamlessly with Flutter and non-Flutter code
  • Simplifies testing and mocking

Riverpod 3.0 brings even more improvements, like better syntax, improved code completion, and robust state handling.


Why use Riverpod 3.0?

  • Safe state management: Compile-time safety prevents common bugs.
  • Scalable architecture: Works well for large apps.
  • Testable code: Easily mock providers for testing.
  • Async-friendly: Supports FutureProvider and StreamProvider natively.

 

Getting Started

Add the dependency in pubspec.yaml:

dependencies:
  flutter_riverpod: ^3.2.1

Wrap your app with ProviderScope:

void main() {
  runApp(
    ProviderScope(child: MyApp()),
  );
}

ProviderScope is required to store the state of all providers.

Core Providers in Riverpod 3.0

1. Provider

For read-only values:

final greetingProvider = Provider<String>((ref) {
  return 'Hello, Riverpod 3!';
});

class HomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final greeting = ref.watch(greetingProvider);
    return Text(greeting);
  }
}

2. StateProvider

For simple mutable state:

final counterProvider = StateProvider<int>((ref) => 0);

ref.watch(counterProvider); // reactive
ref.read(counterProvider.notifier).state++; // update

3. StateNotifierProvider

For complex state management:

class Counter extends StateNotifier<int> {
  Counter(): super(0);
  void increment() => state++;
}

final counterNotifierProvider = StateNotifierProvider<Counter, int>((ref) => Counter());

Use it in a widget:

final count = ref.watch(counterNotifierProvider);
ref.read(counterNotifierProvider.notifier).increment();

4. FutureProvider

For async data (API calls, DB fetch):

final userProvider = FutureProvider<String>((ref) async {
  await Future.delayed(Duration(seconds: 2));
  return 'John Doe';
});
ref.watch(userProvider).when(
  data: (user) => Text('User: $user'),
  loading: () => CircularProgressIndicator(),
  error: (err, _) => Text('Error: $err'),
);
final numberStreamProvider = StreamProvider<int>((ref) async* {
  for (int i = 0; i < 10; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
});

Advanced Features

  • Provider overrides → Mock API in tests
  • Scoped providers → Only available in a part of the widget tree
  • Theme management → Easily switch app themes with StateProvider
  • Persistent state → Combine with Hive, SharedPreferences

Best Practices

  • Keep logic separate from UI
  • Use StateNotifierProvider for anything more than a simple counter
  • Always prefer ref.watch for reactive updates and ref.read for one-time reads
  • Use ProviderScope overrides in tests
All Articles