Question #280MediumState Management

How to manage multiple bloc, riverpod statemanagements in the projects ? like if a project have 20 + riverpod statemanagements then how to handle them all in a single project ? do we handle them each individually or else any common gateway to access them which is good or better ?

#state#bloc#riverpod

Answer

Overview

Managing 20+ Riverpod providers in a single project is straightforward with proper file organization and provider granularity. Riverpod is designed to scale — you access each provider individually, but reference them as needed.


Organizing 20+ Providers

Recommended File Structure

text
lib/
├── providers/
│   ├── auth_provider.dart
│   ├── user_provider.dart
│   └── providers.dart        ← barrel file (export all)
├── features/
│   ├── products/
│   │   └── providers/
│   │       ├── products_provider.dart
│   │       └── product_detail_provider.dart
│   ├── cart/
│   │   └── providers/
│   │       └── cart_provider.dart
│   └── orders/
│       └── providers/
│           └── orders_provider.dart
└── main.dart

Barrel Export File

dart
// lib/providers/providers.dart
export 'auth_provider.dart';
export 'user_provider.dart';

// Import all at once
import 'package:my_app/providers/providers.dart';

Individual Provider Access (No Gateway Needed)

Riverpod's design: each provider is accessed individually — no central gateway needed.

dart
// Define once
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>(
  (ref) => AuthNotifier(ref.watch(authRepoProvider)),
);

final cartProvider = StateNotifierProvider<CartNotifier, CartState>(
  (ref) => CartNotifier(ref.watch(cartRepoProvider)),
);

// Use in widgets — independently
class HomeScreen extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final authState = ref.watch(authProvider);       // only auth
    final cartState = ref.watch(cartProvider);       // only cart
    // No gateway — just reference what you need
    return ...;
  }
}

Provider Composition (The "Gateway" Pattern)

For shared cross-cutting concerns, compose providers:

dart
// A "gateway" provider that combines multiple providers
final dashboardProvider = Provider((ref) {
  final user = ref.watch(userProvider);
  final cart = ref.watch(cartProvider);
  final notifications = ref.watch(notificationsProvider);
  return DashboardViewModel(user: user, cart: cart, notifications: notifications);
});

// Widget uses only the composed provider
final dashboard = ref.watch(dashboardProvider);

Scoping Providers per Feature

dart
// Products feature — scoped with ProviderScope overrides
ProviderScope(
  overrides: [
    // Scope selectedProductProvider to this subtree only
    selectedProductProvider.overrideWith((ref) => ProductNotifier(productId)),
  ],
  child: ProductDetailScreen(),
)

Best Practices for 20+ Providers

PracticeWhy
One file per provider (or per feature)Clear ownership
Use
text
.autoDispose
wherever possible
Memory efficiency
Barrel files per featureClean imports
Provider naming convention:
text
xyzProvider
Consistency
Compose related providersReduce redundancy
Use
text
ProviderObserver
for debugging
Track all provider changes

ProviderObserver (Debug all providers)

dart
class AppProviderObserver extends ProviderObserver {
  
  void didUpdateProvider(ProviderBase provider, Object? prev, Object? next, ProviderContainer container) {
    print('[Provider] ${provider.name ?? provider.runtimeType}: $next');
  }
}

void main() {
  runApp(ProviderScope(
    observers: [AppProviderObserver()],
    child: MyApp(),
  ));
}

Answer to the question: Riverpod has NO central gateway — each provider is accessed individually. This is intentional and scales well to 20, 50, or 100+ providers. Use a barrel file + feature-based folder structure to keep things organized.