Module 1
Topic 4

Riverpod Basics

Introducing Riverpod — a modern, robust, and testable state management library for Flutter.

Riverpod.dev flutter_riverpod Riverpod Docs
What Is Riverpod?

Riverpod is a state management library that takes the Provider pattern and makes it more robust, testable, and flexible. It's built on top of the Provider package but offers several improvements:

🔒
Compile‑time Safe
Providers are defined globally, making them safe and easy to access.
🧪
Testable
Providers can be overridden in tests for easier mocking.
Performance
Only widgets that depend on a provider rebuild when it changes.
🔄
No BuildContext Needed
Providers can be accessed outside of widgets, making them more flexible.

✅ Why Riverpod?

Riverpod was created because the original Provider package had some limitations: it relied heavily on BuildContext , made testing difficult, and didn't offer great compile‑time safety. Riverpod solves these problems and adds powerful features like StateNotifier , FutureProvider , and StreamProvider . It's now the recommended approach by the Flutter team for new projects.

Core Concepts of Riverpod

Riverpod is built around a few key concepts. Understanding these is essential to using it effectively.

1. Providers

A provider is a piece of logic that holds a value and can be listened to by widgets. Providers are defined globally and can be accessed from anywhere in your app.

2. Ref

Ref (short for "reference") is the object that lets you read, watch, or listen to providers. It's passed to the provider's creation function and is the primary way to interact with providers.

3. ProviderScope

ProviderScope is the root widget that makes Riverpod work. It stores the state of all your providers and must be placed at the top of your widget tree.

Simple Example: A Riverpod Counter

Let's start with a classic counter example using Riverpod. This will show you the basic structure of a Riverpod app.

Step-by-Step Explanation
1.
Define a Provider counterProvider is a StateProvider that holds an integer. The arrow function (ref) => 0 initializes it to 0. The ref parameter is the reference object we'll use to interact with other providers.
2.
ProviderScope – This widget is required for Riverpod to work. It stores the state of all providers and manages their lifecycle. Without it, Riverpod won't work.
3.
ConsumerWidget – Instead of StatelessWidget , we use ConsumerWidget . This gives us access to the ref parameter in the build() method.
4.
ref.watch() – This tells Riverpod to listen to the provider. Whenever the provider's state changes, this widget will rebuild. The current value is returned.
5.
ref.read() – This lets you read a provider without listening to it. We use it here to get the provider's notifier and update its state . ref.read() is typically used inside event handlers or callbacks.
Key Riverpod Methods

Riverpod provides three main methods for interacting with providers:

👀 ref.watch()

Use when: You want to read a provider and rebuild the widget when it changes.

  • Causes the widget to rebuild on changes
  • Always use inside the build() method
  • Provides the current value

📖 ref.read()

Use when: You want to read a provider without rebuilding the widget.

  • Does NOT cause the widget to rebuild
  • Used in event handlers, callbacks, and side effects
  • Good for updating state from user actions

🔔 ref.listen()

Use when: You want to run side effects (like showing a snackbar) when a provider changes, without rebuilding the widget.

  • Does NOT cause the widget to rebuild
  • Runs a callback when the provider's state changes
  • Perfect for navigation, showing dialogs, or logging

⚠️ Important: When to Use Each

  • Use watch in build() to react to changes.
  • Use read in event handlers to perform actions without rebuilding.
  • Use listen for side effects that don't affect the UI directly.
Types of Providers

Riverpod offers several types of providers for different use cases. Here's a quick overview:

StateProvider

The simplest provider. Holds a mutable piece of state.

FutureProvider

Holds a Future . Handles loading and error states.

StreamProvider

Holds a Stream . Perfect for real‑time data.

StateNotifierProvider

The most powerful provider. Manages complex state with methods.

We'll cover each of these provider types in detail in the upcoming topics.

Common Mistakes
❌ Mistake 1: Forgetting ProviderScope

If you forget to wrap your app with ProviderScope , Riverpod will throw an error. Always ensure ProviderScope is at the root of your widget tree.

✅ Correct: Always wrap your app with ProviderScope

runApp(ProviderScope(child: MyApp())); is the standard way to initialize Riverpod.

❌ Mistake 2: Using ref.watch() outside build()

ref.watch() can only be called during the build() method or in the Provider creation function. Using it in event handlers will cause errors.

✅ Correct: Use ref.read() in event handlers

For event handlers, use ref.read(provider.notifier).state = newValue to update state.

❌ Mistake 3: Mutating state directly without notifier

With StateProvider , you must update the state through the notifier . Directly assigning to state without the notifier won't trigger rebuilds.

✅ Correct: Always use .notifier to update state

Use ref.read(provider.notifier).state = newValue to properly update state.

🎯 Key Takeaway

Riverpod is a modern, robust state management library that makes it easy to manage global state in Flutter apps. It introduces the concept of providers that hold state, and ref to interact with them. With its compile‑time safety, testability, and performance, Riverpod is an excellent choice for intermediate Flutter developers. Start with StateProvider for simple state, and explore other provider types as needed.