/// Barrel file for all providers - central export point for dependency injection /// /// This file exports all provider files to provide a single import point /// for accessing all application providers following clean architecture principles. /// /// Usage: /// ```dart /// import '../core/providers/providers.dart'; /// /// // Access any provider from the exported modules /// final theme = ref.watch(currentThemeModeProvider); /// final isConnected = ref.watch(isConnectedProvider); /// ``` // Core application providers export 'app_providers.dart'; // Network and API providers export 'network_providers.dart'; export 'api_providers.dart'; // Theme and UI state providers export 'theme_providers.dart'; // Storage and persistence providers export 'storage_providers.dart' hide secureStorageProvider; // Shared connectivity providers export '../../../shared/presentation/providers/connectivity_providers.dart' hide connectivityProvider, isConnectedProvider; /// Provider initialization helper /// /// This class provides utilities for initializing and managing providers /// across the application lifecycle. class ProviderInitializer { const ProviderInitializer._(); /// List of providers that need to be initialized early in the app lifecycle /// These providers will be warmed up during app initialization to ensure /// they're ready when needed. static const List criticalProviders = [ 'appInitializationProvider', 'appSettingsNotifierProvider', 'networkStatusNotifierProvider', 'secureStorageNotifierProvider', ]; /// List of providers that can be initialized lazily when first accessed static const List lazyProviders = [ 'featureFlagsProvider', 'appConfigurationProvider', 'networkHistoryNotifierProvider', 'errorTrackerProvider', ]; /// Provider categories for better organization and management static const Map> providerCategories = { 'core': [ 'appInitializationProvider', 'globalAppStateProvider', 'appVersionProvider', 'appBuildModeProvider', ], 'theme': [ 'appSettingsNotifierProvider', 'currentThemeModeProvider', 'effectiveThemeModeProvider', 'isDarkModeProvider', 'currentLocaleProvider', ], 'storage': [ 'secureStorageNotifierProvider', 'hiveStorageNotifierProvider', 'storageHealthMonitorProvider', 'storageManagerProvider', ], 'network': [ 'networkStatusNotifierProvider', 'networkConnectivityStreamProvider', 'isConnectedProvider', 'connectionTypeProvider', 'networkQualityProvider', ], 'utilities': [ 'featureFlagsProvider', 'appConfigurationProvider', 'errorTrackerProvider', 'appLifecycleNotifierProvider', ], }; /// Get providers by category static List getProvidersByCategory(String category) { return providerCategories[category] ?? []; } /// Get all provider names static List getAllProviders() { return providerCategories.values.expand((providers) => providers).toList(); } /// Check if provider is critical (needs early initialization) static bool isCriticalProvider(String providerName) { return criticalProviders.contains(providerName); } /// Check if provider can be loaded lazily static bool isLazyProvider(String providerName) { return lazyProviders.contains(providerName); } } /// Provider documentation and usage guidelines /// /// This class contains documentation for proper provider usage patterns /// following Riverpod 2.x best practices. class ProviderDocumentation { const ProviderDocumentation._(); /// Usage examples for different provider types static const Map usageExamples = { 'AsyncNotifierProvider': ''' // For async mutable state management final notifier = ref.watch(appSettingsNotifierProvider.notifier); await notifier.updateThemeMode(AppThemeMode.dark); // Watch for state changes ref.listen(appSettingsNotifierProvider, (previous, next) { next.when( data: (settings) => print('Settings updated'), loading: () => print('Loading...'), error: (error, stack) => print('Error: \$error'), ); }); ''', 'NotifierProvider': ''' // For synchronous mutable state final flags = ref.watch(featureFlagsProvider); final notifier = ref.watch(featureFlagsProvider.notifier); // Update state notifier.enableFeature('darkMode'); notifier.toggleFeature('analytics'); ''', 'StreamProvider': ''' // For reactive data streams final connectivityStream = ref.watch(networkConnectivityStreamProvider); connectivityStream.when( data: (status) => Text('Connected: \${status.isConnected}'), loading: () => const CircularProgressIndicator(), error: (error, stack) => Text('Error: \$error'), ); ''', 'Provider': ''' // For dependency injection and immutable values final storage = ref.watch(secureStorageProvider); final connectivity = ref.watch(connectivityProvider); // Use in other providers @riverpod MyService myService(MyServiceRef ref) { final storage = ref.watch(secureStorageProvider); return MyService(storage); } ''', 'FutureProvider': ''' // For async operations (read-only) final initData = ref.watch(appInitializationProvider); initData.when( data: (data) => data.state == AppInitializationState.initialized ? const HomeScreen() : const LoadingScreen(), loading: () => const SplashScreen(), error: (error, stack) => ErrorScreen(error: error), ); ''', }; /// Best practices for provider usage static const List bestPractices = [ '1. Use @riverpod annotation for new providers (Riverpod 2.x)', '2. Prefer AsyncNotifierProvider for mutable async state', '3. Use NotifierProvider for mutable synchronous state', '4. Use StreamProvider for reactive data streams', '5. Use Provider for dependency injection and immutable values', '6. Always handle error states in AsyncValue.when()', '7. Use ref.invalidate() to refresh provider state', '8. Implement proper disposal in ref.onDispose()', '9. Keep providers focused on a single responsibility', '10. Use meaningful provider names that indicate their purpose', ]; /// Common patterns and solutions static const Map commonPatterns = { 'Combining Providers': ''' @riverpod String userDisplayName(UserDisplayNameRef ref) { final user = ref.watch(currentUserProvider); final settings = ref.watch(userSettingsProvider); return settings.showFullName ? '\${user.firstName} \${user.lastName}' : user.username; } ''', 'Error Handling': ''' @riverpod class DataNotifier extends _\$DataNotifier { @override Future build() async { return await _loadData(); } Future refresh() async { state = const AsyncValue.loading(); try { final data = await _loadData(); state = AsyncValue.data(data); } catch (error, stackTrace) { state = AsyncValue.error(error, stackTrace); // Log error for debugging ref.read(errorTrackerProvider.notifier).logError(error, stackTrace); } } } ''', 'State Persistence': ''' @riverpod class PersistentCounter extends _\$PersistentCounter { @override int build() { // Load from storage on build final storage = ref.watch(storageProvider); return storage.getInt('counter') ?? 0; } void increment() { state = state + 1; // Persist state changes final storage = ref.read(storageProvider); storage.setInt('counter', state); } } ''', }; /// Performance tips static const List performanceTips = [ '1. Use select() to watch specific parts of complex state', '2. Avoid creating providers in build methods', '3. Use autoDispose for temporary providers', '4. Keep provider state minimal and focused', '5. Use family providers for parameterized providers', '6. Implement proper caching for expensive operations', '7. Dispose of resources in ref.onDispose()', '8. Use keepAlive() for providers that should survive rebuilds', ]; }