StreamNotifierProvider
StreamNotifierProvider is a provider that exposes a StreamNotifier, allowing you to create, manage, and control a stream of values while encapsulating business logic.
What is it?
StreamNotifierProvider is the recommended provider for mutable streaming state in Riverpod.
Unlike StreamProvider, which simply listens to an existing stream, StreamNotifierProvider lets you:
- Create streams
- Control emitted values
- Expose business logic
- React to user actions
- Access dependencies
- Manage stream lifecycle
It combines:
- Stream generation
- State management
- Business logic
into a single notifier.
Why does it exist?
Consider a chat application.
With StreamProvider, you can easily listen to incoming messages.
Server
│
▼
StreamProvider
│
▼
UI
But what if you also need to:
- Send messages
- Delete messages
- Filter messages
- Reconnect automatically
- Retry failed connections
StreamProvider has no place to store that business logic.
StreamNotifierProvider solves this by giving your stream its own controller class.
Benefits include:
- Centralized stream logic
- Better testability
- Dependency injection
- Lifecycle management
- Cleaner architecture
Syntax
Creating a StreamNotifier
class CounterNotifier extends StreamNotifier<int> {
@override
Stream<int> build() async* {
for (var i = 0; i < 5; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
}
Explanation:
build()returns aStream<int>.- Every
yieldemits a new value. - Consumers rebuild automatically.
Creating a StreamNotifierProvider
final counterProvider =
StreamNotifierProvider<
CounterNotifier,
int
>(CounterNotifier.new);
Explanation:
- Exposes the notifier.
- Widgets watch an
AsyncValue<int>. - Methods are accessed through
.notifier.
Reading the State
final counter = ref.watch(counterProvider);
Explanation:
- Returns an
AsyncValue<int>. - Automatically updates for each emitted value.
Calling Methods
ref.read(counterProvider.notifier).restart();
Explanation:
.notifierreturns theStreamNotifier.- Methods control stream behavior.
Mental Model
Think of StreamNotifier as a live event controller.
Widget
│
▼
StreamNotifierProvider
│
▼
StreamNotifier
┌───────┴────────┐
▼ ▼
Business Logic Stream
│
AsyncValue
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Loading Data Error
The notifier owns both the stream and the business logic that controls it.
Examples
Timer
class TimerNotifier extends StreamNotifier<int> {
@override
Stream<int> build() async* {
var seconds = 0;
while (true) {
await Future.delayed(const Duration(seconds: 1));
yield seconds++;
}
}
}
Explanation:
- Emits a new value every second.
- Widgets rebuild automatically.
Chat Messages
class ChatNotifier extends StreamNotifier<Message> {
@override
Stream<Message> build() {
return socket.messages();
}
Future<void> send(Message message) async {
await socket.send(message);
}
}
Explanation:
- The notifier streams incoming messages.
- It also exposes a method to send messages.
Location Tracking
class LocationNotifier
extends StreamNotifier<Location> {
@override
Stream<Location> build() {
return locationService.locationStream();
}
Future<void> refresh() async {
locationService.requestUpdate();
}
}
Explanation:
- Streams location updates.
- Allows requesting a fresh location through a method.
State Flow
Provider Created
│
▼
build()
│
▼
AsyncLoading
│
▼
First Stream Event
│
▼
AsyncData
│
▼
More Events
│
▼
AsyncData
│
▼
Method Called
│
▼
Business Logic
│
▼
Future Stream Events
The provider continues emitting values until the stream ends or the provider is disposed.
StreamNotifierProvider vs StreamProvider
| Feature | StreamProvider | StreamNotifierProvider |
|---|---|---|
| Listen to existing streams | ✅ | ✅ |
| Create streams | Limited | ✅ |
| Expose methods | ❌ | ✅ |
| Business logic | ❌ | ✅ |
| Dependency injection | Limited | ✅ |
| Mutable stream behavior | ❌ | ✅ |
When to Use
Use StreamNotifierProvider for:
- Chat systems
- Live dashboards
- WebSockets
- Real-time collaboration
- Sensor streams
- Location tracking
- Event-driven applications
- Streaming business logic
Use it when your stream needs more than simple consumption.
When NOT to Use
Avoid StreamNotifierProvider when:
- You only need to listen to an existing stream.
- No business logic is required.
- The operation returns a single asynchronous value.
- The state is synchronous.
Instead, use:
| Scenario | Recommended Provider |
|---|---|
| Existing stream | StreamProvider |
| One-time async request | FutureProvider |
| Mutable async state | AsyncNotifierProvider |
| Mutable sync state | NotifierProvider |
Best Practices
- Keep stream creation inside the notifier.
- Expose meaningful methods for stream-related operations.
- Keep widgets free of business logic.
- Use immutable models for emitted values.
- Use
autoDisposefor short-lived streams when appropriate.
Common Mistakes
1. Using StreamProvider for Mutable Stream Logic
❌ Wrong
final chatProvider =
StreamProvider<Message>((ref) {
return socket.messages();
});
// Sending messages from widgets
socket.send(message);
Why it's wrong:
- Business logic is scattered outside the provider.
- The provider only consumes the stream.
✔ Correct
ref.read(chatProvider.notifier).send(message);
Keep stream operations inside the notifier.
2. Performing Business Logic in Widgets
❌ Wrong
onPressed: () {
socket.send(message);
}
Why it's wrong:
- Widgets should not communicate directly with services.
- This makes testing and maintenance harder.
✔ Correct
onPressed: () {
ref.read(chatProvider.notifier).send(message);
}
Delegate operations to the notifier.
3. Using StreamNotifierProvider for Read-Only Streams
❌ Wrong
class NotificationsNotifier
extends StreamNotifier<Notification> {
@override
Stream<Notification> build() {
return notificationService.stream;
}
}
Why it's wrong:
- If no business logic or methods are needed, a
StreamProvideris simpler.
✔ Correct
final notificationsProvider =
StreamProvider<Notification>((ref) {
return notificationService.stream;
});
Use the simplest provider that meets your needs.
Recommendation
Use
StreamNotifierProviderwhen your stream requires business logic or user interactions.If you're only consuming an existing stream without modifying its behavior, prefer
StreamProvider.
Related APIs
- StreamNotifier
- StreamProvider
- AsyncNotifierProvider
- NotifierProvider
- AsyncValue
- ref.watch()
- ref.read()
Summary
StreamNotifierProvider is Riverpod's provider for mutable streaming state. It combines stream creation, business logic, and state management inside a StreamNotifier, exposing values as an AsyncValue while allowing methods to control stream behavior. It is the preferred choice when your application needs to both consume and manage real-time data streams.