init cc
This commit is contained in:
360
lib/core/providers/README.md
Normal file
360
lib/core/providers/README.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# Riverpod State Management Setup
|
||||
|
||||
This directory contains a comprehensive Riverpod state management setup following Riverpod 2.x best practices with modern provider patterns.
|
||||
|
||||
## 📁 Directory Structure
|
||||
|
||||
```
|
||||
lib/core/providers/
|
||||
├── providers.dart # Barrel file - single import point
|
||||
├── app_providers.dart # Global app state and initialization
|
||||
├── theme_providers.dart # Theme and UI state management
|
||||
├── storage_providers.dart # Secure storage and Hive management
|
||||
├── network_providers.dart # HTTP clients and API providers
|
||||
├── api_providers.dart # API-specific providers
|
||||
└── provider_usage_example.dart # Usage examples and patterns
|
||||
|
||||
lib/shared/presentation/providers/
|
||||
└── connectivity_providers.dart # Network connectivity monitoring
|
||||
```
|
||||
|
||||
## 🚀 Key Features
|
||||
|
||||
### Modern Riverpod 2.x Patterns
|
||||
- **AsyncNotifierProvider**: For async mutable state management
|
||||
- **NotifierProvider**: For synchronous mutable state management
|
||||
- **StreamProvider**: For reactive data streams
|
||||
- **Provider**: For dependency injection and immutable values
|
||||
- **Code Generation**: Using `@riverpod` annotation for type safety
|
||||
|
||||
### Comprehensive State Management
|
||||
- **App Initialization**: Multi-stage app startup with error handling
|
||||
- **Theme Management**: Dark/light mode with system preference support
|
||||
- **Storage Integration**: Hive + Secure Storage with health monitoring
|
||||
- **Network Connectivity**: Real-time connection status and history
|
||||
- **Feature Flags**: Dynamic feature toggling
|
||||
- **Error Tracking**: Centralized error logging and monitoring
|
||||
|
||||
### Performance Optimized
|
||||
- **State Persistence**: Automatic state saving and restoration
|
||||
- **Efficient Rebuilds**: Minimal widget rebuilds with proper selectors
|
||||
- **Resource Management**: Automatic disposal and cleanup
|
||||
- **Caching Strategy**: Intelligent data caching with expiration
|
||||
|
||||
## 📋 Provider Categories
|
||||
|
||||
### 🏗️ Core Application (`app_providers.dart`)
|
||||
```dart
|
||||
// App initialization with multi-stage loading
|
||||
final initData = ref.watch(appInitializationProvider);
|
||||
|
||||
// Global app state management
|
||||
final globalState = ref.watch(globalAppStateProvider);
|
||||
|
||||
// Feature flags for conditional features
|
||||
final featureFlags = ref.watch(featureFlagsProvider);
|
||||
```
|
||||
|
||||
### 🎨 Theme & UI (`theme_providers.dart`)
|
||||
```dart
|
||||
// Theme mode management with persistence
|
||||
final themeMode = ref.watch(currentThemeModeProvider);
|
||||
await ref.read(appSettingsNotifierProvider.notifier)
|
||||
.updateThemeMode(AppThemeMode.dark);
|
||||
|
||||
// Reactive theme changes
|
||||
final isDark = ref.watch(isDarkModeProvider);
|
||||
```
|
||||
|
||||
### 💾 Storage Management (`storage_providers.dart`)
|
||||
```dart
|
||||
// Secure storage operations
|
||||
final secureNotifier = ref.read(secureStorageNotifierProvider.notifier);
|
||||
await secureNotifier.store('token', 'secure_value');
|
||||
|
||||
// Hive storage with health monitoring
|
||||
final storageHealth = ref.watch(storageHealthMonitorProvider);
|
||||
```
|
||||
|
||||
### 🌐 Network Connectivity (`connectivity_providers.dart`)
|
||||
```dart
|
||||
// Real-time connectivity monitoring
|
||||
final isConnected = ref.watch(isConnectedProvider);
|
||||
final connectionType = ref.watch(connectionTypeProvider);
|
||||
|
||||
// Network history and statistics
|
||||
final networkHistory = ref.watch(networkHistoryNotifierProvider);
|
||||
```
|
||||
|
||||
## 🔧 Usage Patterns
|
||||
|
||||
### 1. Basic Provider Consumption
|
||||
```dart
|
||||
class MyWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final settingsAsync = ref.watch(appSettingsNotifierProvider);
|
||||
|
||||
return settingsAsync.when(
|
||||
data: (settings) => Text('Theme: ${settings.themeMode}'),
|
||||
loading: () => const CircularProgressIndicator(),
|
||||
error: (error, stack) => Text('Error: $error'),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. State Mutation
|
||||
```dart
|
||||
// Theme change
|
||||
await ref.read(appSettingsNotifierProvider.notifier)
|
||||
.updateThemeMode(AppThemeMode.dark);
|
||||
|
||||
// Feature flag toggle
|
||||
ref.read(featureFlagsProvider.notifier)
|
||||
.toggleFeature('darkMode');
|
||||
|
||||
// Storage operations
|
||||
await ref.read(secureStorageNotifierProvider.notifier)
|
||||
.store('api_key', newApiKey);
|
||||
```
|
||||
|
||||
### 3. Provider Listening
|
||||
```dart
|
||||
// Listen for state changes
|
||||
ref.listen(networkStatusNotifierProvider, (previous, next) {
|
||||
if (next.isConnected && !previous?.isConnected) {
|
||||
// Connection restored
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Connection restored')),
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Combining Providers
|
||||
```dart
|
||||
@riverpod
|
||||
String userStatus(UserStatusRef ref) {
|
||||
final isConnected = ref.watch(isConnectedProvider);
|
||||
final settings = ref.watch(appSettingsNotifierProvider);
|
||||
|
||||
return settings.when(
|
||||
data: (data) => isConnected
|
||||
? 'Online - ${data.locale}'
|
||||
: 'Offline - ${data.locale}',
|
||||
loading: () => 'Loading...',
|
||||
error: (_, __) => 'Error',
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 📱 App Integration
|
||||
|
||||
### 1. Provider Setup
|
||||
```dart
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Initialize Hive before app starts
|
||||
await HiveService.init();
|
||||
|
||||
runApp(
|
||||
ProviderScope(
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. App Initialization
|
||||
```dart
|
||||
class MyApp extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final initAsync = ref.watch(appInitializationProvider);
|
||||
|
||||
return initAsync.when(
|
||||
data: (data) => data.state == AppInitializationState.initialized
|
||||
? const MainApp()
|
||||
: const SplashScreen(),
|
||||
loading: () => const SplashScreen(),
|
||||
error: (error, stack) => ErrorApp(error: error),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Theme Integration
|
||||
```dart
|
||||
class MainApp extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final themeMode = ref.watch(effectiveThemeModeProvider);
|
||||
|
||||
return MaterialApp(
|
||||
themeMode: themeMode,
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
home: const HomeScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 State Persistence
|
||||
|
||||
### Automatic Persistence
|
||||
- **Theme Mode**: Automatically saved to Hive
|
||||
- **User Preferences**: Persisted across app restarts
|
||||
- **Feature Flags**: Maintained in local storage
|
||||
- **Network History**: Cached for analytics
|
||||
|
||||
### Manual Persistence
|
||||
```dart
|
||||
// Save custom app state
|
||||
await ref.read(appSettingsNotifierProvider.notifier)
|
||||
.setCustomSetting('user_preference', value);
|
||||
|
||||
// Restore state on app start
|
||||
final customValue = settings.getCustomSetting<String>('user_preference');
|
||||
```
|
||||
|
||||
## 🚨 Error Handling
|
||||
|
||||
### Provider-Level Error Handling
|
||||
```dart
|
||||
@riverpod
|
||||
class DataNotifier extends _$DataNotifier {
|
||||
@override
|
||||
Future<Data> build() async {
|
||||
try {
|
||||
return await loadData();
|
||||
} catch (error, stackTrace) {
|
||||
// Log error for debugging
|
||||
ref.read(errorTrackerProvider.notifier)
|
||||
.logError(error, stackTrace);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Global Error Tracking
|
||||
```dart
|
||||
// Access error history
|
||||
final recentErrors = ref.read(errorTrackerProvider.notifier)
|
||||
.getRecentErrors(count: 5);
|
||||
|
||||
// Clear error history
|
||||
ref.read(errorTrackerProvider.notifier).clearErrors();
|
||||
```
|
||||
|
||||
## 🔍 Debugging & Monitoring
|
||||
|
||||
### Provider Inspector
|
||||
```dart
|
||||
// Check provider state in debug mode
|
||||
if (kDebugMode) {
|
||||
final globalState = ref.read(globalAppStateProvider);
|
||||
debugPrint('App State: $globalState');
|
||||
}
|
||||
```
|
||||
|
||||
### Storage Health Monitoring
|
||||
```dart
|
||||
// Perform health check
|
||||
await ref.read(storageHealthMonitorProvider.notifier)
|
||||
.performHealthCheck();
|
||||
|
||||
// Check storage statistics
|
||||
final stats = ref.read(hiveStorageNotifierProvider.notifier)
|
||||
.getStorageStats();
|
||||
```
|
||||
|
||||
## 🛠️ Best Practices
|
||||
|
||||
1. **Import**: Use the barrel file `import '../core/providers/providers.dart'`
|
||||
2. **Error Handling**: Always handle AsyncValue error states
|
||||
3. **Disposal**: Providers auto-dispose, but manual cleanup when needed
|
||||
4. **Performance**: Use `select()` for specific state slices
|
||||
5. **Testing**: Mock providers using `ProviderContainer`
|
||||
6. **State Size**: Keep provider state minimal and focused
|
||||
7. **Provider Names**: Use descriptive names indicating purpose
|
||||
8. **Documentation**: Document complex provider logic
|
||||
|
||||
## 📚 Code Generation
|
||||
|
||||
The providers use Riverpod's code generation. Run this command after making changes:
|
||||
|
||||
```bash
|
||||
dart run build_runner build
|
||||
```
|
||||
|
||||
For continuous generation during development:
|
||||
|
||||
```bash
|
||||
dart run build_runner watch
|
||||
```
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Provider Testing
|
||||
```dart
|
||||
test('theme provider updates correctly', () async {
|
||||
final container = ProviderContainer();
|
||||
|
||||
final notifier = container.read(appSettingsNotifierProvider.notifier);
|
||||
await notifier.updateThemeMode(AppThemeMode.dark);
|
||||
|
||||
final settings = container.read(appSettingsNotifierProvider);
|
||||
expect(settings.value?.themeMode, 'dark');
|
||||
|
||||
container.dispose();
|
||||
});
|
||||
```
|
||||
|
||||
### Widget Testing with Providers
|
||||
```dart
|
||||
testWidgets('widget shows correct theme', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(
|
||||
home: MyWidget(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Theme: system'), findsOneWidget);
|
||||
});
|
||||
```
|
||||
|
||||
## 📄 Dependencies
|
||||
|
||||
This setup requires the following dependencies in `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
flutter_riverpod: ^2.5.1
|
||||
riverpod_annotation: ^2.3.5
|
||||
connectivity_plus: ^6.0.5
|
||||
flutter_secure_storage: ^9.2.2
|
||||
hive: ^2.2.3
|
||||
hive_flutter: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
riverpod_generator: ^2.4.0
|
||||
build_runner: ^2.4.7
|
||||
```
|
||||
|
||||
## 🔄 Migration from Provider/Bloc
|
||||
|
||||
If migrating from other state management solutions:
|
||||
|
||||
1. Replace Provider with Riverpod providers
|
||||
2. Convert Bloc to AsyncNotifierProvider
|
||||
3. Update UI to use ConsumerWidget
|
||||
4. Migrate state persistence logic
|
||||
5. Update testing to use ProviderContainer
|
||||
|
||||
This setup provides a solid foundation for scalable Flutter applications with proper state management, error handling, and performance optimization.
|
||||
34
lib/core/providers/api_providers.dart
Normal file
34
lib/core/providers/api_providers.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../services/api_service.dart';
|
||||
import 'network_providers.dart';
|
||||
|
||||
/// Provider for ExampleApiService
|
||||
final exampleApiServiceProvider = Provider<ExampleApiService>((ref) {
|
||||
final dioClient = ref.watch(dioClientProvider);
|
||||
return ExampleApiService(dioClient);
|
||||
});
|
||||
|
||||
/// Provider for AuthApiService
|
||||
final authApiServiceProvider = Provider<AuthApiService>((ref) {
|
||||
final dioClient = ref.watch(dioClientProvider);
|
||||
return AuthApiService(dioClient);
|
||||
});
|
||||
|
||||
/// Provider to check authentication status
|
||||
final isAuthenticatedProvider = FutureProvider<bool>((ref) async {
|
||||
final authService = ref.watch(authApiServiceProvider);
|
||||
return await authService.isAuthenticated();
|
||||
});
|
||||
|
||||
/// Example provider for user profile data
|
||||
final userProfileProvider = FutureProvider.family<Map<String, dynamic>, String>((ref, userId) async {
|
||||
final apiService = ref.watch(exampleApiServiceProvider);
|
||||
return await apiService.getUserProfile(userId);
|
||||
});
|
||||
|
||||
/// Example provider for posts list with pagination
|
||||
final postsProvider = FutureProvider.family<List<Map<String, dynamic>>, ({int page, int limit})>((ref, params) async {
|
||||
final apiService = ref.watch(exampleApiServiceProvider);
|
||||
return await apiService.getPosts(page: params.page, limit: params.limit);
|
||||
});
|
||||
348
lib/core/providers/app_providers.dart
Normal file
348
lib/core/providers/app_providers.dart
Normal file
@@ -0,0 +1,348 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../database/hive_service.dart';
|
||||
import '../database/models/app_settings.dart';
|
||||
import '../database/providers/database_providers.dart';
|
||||
import '../database/repositories/settings_repository.dart';
|
||||
import '../database/repositories/cache_repository.dart';
|
||||
import '../database/repositories/user_preferences_repository.dart';
|
||||
|
||||
part 'app_providers.g.dart';
|
||||
|
||||
/// App initialization state
|
||||
enum AppInitializationState {
|
||||
uninitialized,
|
||||
initializing,
|
||||
initialized,
|
||||
error,
|
||||
}
|
||||
|
||||
/// App initialization data
|
||||
class AppInitializationData {
|
||||
final AppInitializationState state;
|
||||
final String? error;
|
||||
final DateTime? initializedAt;
|
||||
|
||||
const AppInitializationData({
|
||||
required this.state,
|
||||
this.error,
|
||||
this.initializedAt,
|
||||
});
|
||||
|
||||
AppInitializationData copyWith({
|
||||
AppInitializationState? state,
|
||||
String? error,
|
||||
DateTime? initializedAt,
|
||||
}) {
|
||||
return AppInitializationData(
|
||||
state: state ?? this.state,
|
||||
error: error ?? this.error,
|
||||
initializedAt: initializedAt ?? this.initializedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Repository providers
|
||||
@riverpod
|
||||
CacheRepository cacheRepository(CacheRepositoryRef ref) {
|
||||
return CacheRepository();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
UserPreferencesRepository userPreferencesRepository(UserPreferencesRepositoryRef ref) {
|
||||
return UserPreferencesRepository();
|
||||
}
|
||||
|
||||
/// App initialization provider
|
||||
@riverpod
|
||||
class AppInitialization extends _$AppInitialization {
|
||||
@override
|
||||
Future<AppInitializationData> build() async {
|
||||
return _initializeApp();
|
||||
}
|
||||
|
||||
Future<AppInitializationData> _initializeApp() async {
|
||||
try {
|
||||
debugPrint('🚀 Starting app initialization...');
|
||||
|
||||
// Initialize Hive
|
||||
debugPrint('📦 Initializing Hive database...');
|
||||
await HiveService.initialize();
|
||||
|
||||
// Initialize repositories
|
||||
debugPrint('🗂️ Initializing repositories...');
|
||||
final settingsRepo = ref.read(settingsRepositoryProvider);
|
||||
final cacheRepo = ref.read(cacheRepositoryProvider);
|
||||
final userPrefsRepo = ref.read(userPreferencesRepositoryProvider);
|
||||
|
||||
// Load initial settings
|
||||
debugPrint('⚙️ Loading app settings...');
|
||||
final settings = settingsRepo.getSettings();
|
||||
|
||||
// Initialize user preferences if needed
|
||||
debugPrint('👤 Initializing user preferences...');
|
||||
userPrefsRepo.getPreferences();
|
||||
|
||||
// Perform any necessary migrations
|
||||
debugPrint('🔄 Checking for migrations...');
|
||||
await _performMigrations(settingsRepo, settings);
|
||||
|
||||
// Clean up expired cache entries
|
||||
debugPrint('🧹 Cleaning up expired cache...');
|
||||
await cacheRepo.cleanExpiredItems();
|
||||
|
||||
debugPrint('✅ App initialization completed successfully');
|
||||
|
||||
return AppInitializationData(
|
||||
state: AppInitializationState.initialized,
|
||||
initializedAt: DateTime.now(),
|
||||
);
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint('❌ App initialization failed: $error');
|
||||
debugPrint('Stack trace: $stackTrace');
|
||||
|
||||
return AppInitializationData(
|
||||
state: AppInitializationState.error,
|
||||
error: error.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _performMigrations(SettingsRepository repo, AppSettings settings) async {
|
||||
// Add any migration logic here
|
||||
if (settings.version < 1) {
|
||||
debugPrint('🔄 Migrating settings to version 1...');
|
||||
// Migration logic would go here
|
||||
}
|
||||
}
|
||||
|
||||
/// Retry initialization
|
||||
Future<void> retry() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = AsyncValue.data(await _initializeApp());
|
||||
}
|
||||
|
||||
/// Force re-initialization
|
||||
Future<void> reinitialize() async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
// Close all Hive boxes
|
||||
await HiveService.closeAll();
|
||||
|
||||
// Re-initialize everything
|
||||
state = AsyncValue.data(await _initializeApp());
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// App version provider
|
||||
@riverpod
|
||||
String appVersion(AppVersionRef ref) {
|
||||
// This would typically come from package_info_plus
|
||||
return '1.0.0+1';
|
||||
}
|
||||
|
||||
/// App build mode provider
|
||||
@riverpod
|
||||
String appBuildMode(AppBuildModeRef ref) {
|
||||
if (kDebugMode) return 'debug';
|
||||
if (kProfileMode) return 'profile';
|
||||
return 'release';
|
||||
}
|
||||
|
||||
/// App ready state provider
|
||||
@riverpod
|
||||
bool isAppReady(IsAppReadyRef ref) {
|
||||
final initData = ref.watch(appInitializationProvider);
|
||||
|
||||
return initData.when(
|
||||
data: (data) => data.state == AppInitializationState.initialized,
|
||||
loading: () => false,
|
||||
error: (_, __) => false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Global app state notifier
|
||||
@riverpod
|
||||
class GlobalAppState extends _$GlobalAppState {
|
||||
@override
|
||||
Map<String, dynamic> build() {
|
||||
final initAsync = ref.watch(appInitializationProvider);
|
||||
final appVersion = ref.watch(appVersionProvider);
|
||||
final buildMode = ref.watch(appBuildModeProvider);
|
||||
|
||||
return initAsync.when(
|
||||
data: (initData) => {
|
||||
'isInitialized': initData.state == AppInitializationState.initialized,
|
||||
'initializationState': initData.state,
|
||||
'initializationError': initData.error,
|
||||
'initializedAt': initData.initializedAt?.toIso8601String(),
|
||||
'appVersion': appVersion,
|
||||
'buildMode': buildMode,
|
||||
'isReady': initData.state == AppInitializationState.initialized,
|
||||
},
|
||||
loading: () => {
|
||||
'isInitialized': false,
|
||||
'initializationState': AppInitializationState.initializing,
|
||||
'initializationError': null,
|
||||
'initializedAt': null,
|
||||
'appVersion': appVersion,
|
||||
'buildMode': buildMode,
|
||||
'isReady': false,
|
||||
},
|
||||
error: (error, _) => {
|
||||
'isInitialized': false,
|
||||
'initializationState': AppInitializationState.error,
|
||||
'initializationError': error.toString(),
|
||||
'initializedAt': null,
|
||||
'appVersion': appVersion,
|
||||
'buildMode': buildMode,
|
||||
'isReady': false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Update global state
|
||||
void updateState(String key, dynamic value) {
|
||||
final currentState = Map<String, dynamic>.from(state);
|
||||
currentState[key] = value;
|
||||
state = currentState;
|
||||
}
|
||||
|
||||
/// Reset global state
|
||||
void reset() {
|
||||
ref.invalidate(appInitializationProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/// Feature flags provider
|
||||
@riverpod
|
||||
class FeatureFlags extends _$FeatureFlags {
|
||||
@override
|
||||
Map<String, bool> build() {
|
||||
final buildMode = ref.watch(appBuildModeProvider);
|
||||
|
||||
return {
|
||||
'enableDebugMode': buildMode == 'debug',
|
||||
'enableAnalytics': buildMode == 'release',
|
||||
'enableCrashReporting': buildMode != 'debug',
|
||||
'enablePerformanceMonitoring': true,
|
||||
'enableOfflineMode': true,
|
||||
'enableDarkMode': true,
|
||||
'enableNotifications': true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Check if feature is enabled
|
||||
bool isEnabled(String feature) {
|
||||
return state[feature] ?? false;
|
||||
}
|
||||
|
||||
/// Enable feature
|
||||
void enableFeature(String feature) {
|
||||
state = {...state, feature: true};
|
||||
}
|
||||
|
||||
/// Disable feature
|
||||
void disableFeature(String feature) {
|
||||
state = {...state, feature: false};
|
||||
}
|
||||
|
||||
/// Toggle feature
|
||||
void toggleFeature(String feature) {
|
||||
final currentValue = state[feature] ?? false;
|
||||
state = {...state, feature: !currentValue};
|
||||
}
|
||||
}
|
||||
|
||||
/// App configuration provider
|
||||
@riverpod
|
||||
class AppConfiguration extends _$AppConfiguration {
|
||||
@override
|
||||
Map<String, dynamic> build() {
|
||||
final buildMode = ref.watch(appBuildModeProvider);
|
||||
|
||||
return {
|
||||
'apiTimeout': 30000, // 30 seconds
|
||||
'cacheTimeout': 3600000, // 1 hour in milliseconds
|
||||
'maxRetries': 3,
|
||||
'retryDelay': 1000, // 1 second
|
||||
'enableLogging': buildMode == 'debug',
|
||||
'logLevel': buildMode == 'debug' ? 'verbose' : 'error',
|
||||
'maxCacheSize': 100 * 1024 * 1024, // 100MB
|
||||
'imageQuality': 85,
|
||||
'compressionEnabled': true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Get configuration value
|
||||
T? getValue<T>(String key) {
|
||||
return state[key] as T?;
|
||||
}
|
||||
|
||||
/// Update configuration
|
||||
void updateConfiguration(String key, dynamic value) {
|
||||
state = {...state, key: value};
|
||||
}
|
||||
|
||||
/// Update multiple configurations
|
||||
void updateConfigurations(Map<String, dynamic> updates) {
|
||||
state = {...state, ...updates};
|
||||
}
|
||||
}
|
||||
|
||||
/// App lifecycle state provider
|
||||
@riverpod
|
||||
class AppLifecycleNotifier extends _$AppLifecycleNotifier {
|
||||
@override
|
||||
String build() {
|
||||
return 'resumed'; // Default state
|
||||
}
|
||||
|
||||
void updateState(String newState) {
|
||||
state = newState;
|
||||
debugPrint('📱 App lifecycle state changed to: $newState');
|
||||
}
|
||||
}
|
||||
|
||||
/// Error tracking provider
|
||||
@riverpod
|
||||
class ErrorTracker extends _$ErrorTracker {
|
||||
@override
|
||||
List<Map<String, dynamic>> build() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/// Log error
|
||||
void logError(dynamic error, StackTrace? stackTrace, {Map<String, dynamic>? context}) {
|
||||
final errorEntry = {
|
||||
'error': error.toString(),
|
||||
'stackTrace': stackTrace?.toString(),
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'context': context,
|
||||
};
|
||||
|
||||
state = [...state, errorEntry];
|
||||
|
||||
// Keep only last 100 errors to prevent memory issues
|
||||
if (state.length > 100) {
|
||||
state = state.sublist(state.length - 100);
|
||||
}
|
||||
|
||||
debugPrint('🐛 Error logged: $error');
|
||||
}
|
||||
|
||||
/// Clear errors
|
||||
void clearErrors() {
|
||||
state = [];
|
||||
}
|
||||
|
||||
/// Get recent errors
|
||||
List<Map<String, dynamic>> getRecentErrors({int count = 10}) {
|
||||
return state.reversed.take(count).toList();
|
||||
}
|
||||
}
|
||||
200
lib/core/providers/app_providers.g.dart
Normal file
200
lib/core/providers/app_providers.g.dart
Normal file
@@ -0,0 +1,200 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_providers.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$cacheRepositoryHash() => r'0137087454bd51e0465886de0eab7acdc124ecb9';
|
||||
|
||||
/// Repository providers
|
||||
///
|
||||
/// Copied from [cacheRepository].
|
||||
@ProviderFor(cacheRepository)
|
||||
final cacheRepositoryProvider = AutoDisposeProvider<CacheRepository>.internal(
|
||||
cacheRepository,
|
||||
name: r'cacheRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$cacheRepositoryHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef CacheRepositoryRef = AutoDisposeProviderRef<CacheRepository>;
|
||||
String _$userPreferencesRepositoryHash() =>
|
||||
r'0244be191fd7576cbfc90468fe491306ed06d537';
|
||||
|
||||
/// See also [userPreferencesRepository].
|
||||
@ProviderFor(userPreferencesRepository)
|
||||
final userPreferencesRepositoryProvider =
|
||||
AutoDisposeProvider<UserPreferencesRepository>.internal(
|
||||
userPreferencesRepository,
|
||||
name: r'userPreferencesRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$userPreferencesRepositoryHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef UserPreferencesRepositoryRef
|
||||
= AutoDisposeProviderRef<UserPreferencesRepository>;
|
||||
String _$appVersionHash() => r'2605d9c0fd6d6a24e56caceadbe25b8370fedc4f';
|
||||
|
||||
/// App version provider
|
||||
///
|
||||
/// Copied from [appVersion].
|
||||
@ProviderFor(appVersion)
|
||||
final appVersionProvider = AutoDisposeProvider<String>.internal(
|
||||
appVersion,
|
||||
name: r'appVersionProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$appVersionHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef AppVersionRef = AutoDisposeProviderRef<String>;
|
||||
String _$appBuildModeHash() => r'fa100842dc5c894edb352036f8d887d97618f696';
|
||||
|
||||
/// App build mode provider
|
||||
///
|
||||
/// Copied from [appBuildMode].
|
||||
@ProviderFor(appBuildMode)
|
||||
final appBuildModeProvider = AutoDisposeProvider<String>.internal(
|
||||
appBuildMode,
|
||||
name: r'appBuildModeProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$appBuildModeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef AppBuildModeRef = AutoDisposeProviderRef<String>;
|
||||
String _$isAppReadyHash() => r'b23a0450aa7bb2c9e3ea07630429118f239e610a';
|
||||
|
||||
/// App ready state provider
|
||||
///
|
||||
/// Copied from [isAppReady].
|
||||
@ProviderFor(isAppReady)
|
||||
final isAppReadyProvider = AutoDisposeProvider<bool>.internal(
|
||||
isAppReady,
|
||||
name: r'isAppReadyProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$isAppReadyHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef IsAppReadyRef = AutoDisposeProviderRef<bool>;
|
||||
String _$appInitializationHash() => r'eb87040a5ee3d20a172bef9221c2c56d7e07fe77';
|
||||
|
||||
/// App initialization provider
|
||||
///
|
||||
/// Copied from [AppInitialization].
|
||||
@ProviderFor(AppInitialization)
|
||||
final appInitializationProvider = AutoDisposeAsyncNotifierProvider<
|
||||
AppInitialization, AppInitializationData>.internal(
|
||||
AppInitialization.new,
|
||||
name: r'appInitializationProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appInitializationHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppInitialization = AutoDisposeAsyncNotifier<AppInitializationData>;
|
||||
String _$globalAppStateHash() => r'fd0daa69a2a1dc4aaa3af95a1b148ba1e6de0e3f';
|
||||
|
||||
/// Global app state notifier
|
||||
///
|
||||
/// Copied from [GlobalAppState].
|
||||
@ProviderFor(GlobalAppState)
|
||||
final globalAppStateProvider =
|
||||
AutoDisposeNotifierProvider<GlobalAppState, Map<String, dynamic>>.internal(
|
||||
GlobalAppState.new,
|
||||
name: r'globalAppStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$globalAppStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$GlobalAppState = AutoDisposeNotifier<Map<String, dynamic>>;
|
||||
String _$featureFlagsHash() => r'747e9d64c73eed5b374f37a8f28eb4b7fc94e53d';
|
||||
|
||||
/// Feature flags provider
|
||||
///
|
||||
/// Copied from [FeatureFlags].
|
||||
@ProviderFor(FeatureFlags)
|
||||
final featureFlagsProvider =
|
||||
AutoDisposeNotifierProvider<FeatureFlags, Map<String, bool>>.internal(
|
||||
FeatureFlags.new,
|
||||
name: r'featureFlagsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$featureFlagsHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$FeatureFlags = AutoDisposeNotifier<Map<String, bool>>;
|
||||
String _$appConfigurationHash() => r'115fff1ac67a37ff620bbd15ea142a7211e9dc9c';
|
||||
|
||||
/// App configuration provider
|
||||
///
|
||||
/// Copied from [AppConfiguration].
|
||||
@ProviderFor(AppConfiguration)
|
||||
final appConfigurationProvider = AutoDisposeNotifierProvider<AppConfiguration,
|
||||
Map<String, dynamic>>.internal(
|
||||
AppConfiguration.new,
|
||||
name: r'appConfigurationProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appConfigurationHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppConfiguration = AutoDisposeNotifier<Map<String, dynamic>>;
|
||||
String _$appLifecycleNotifierHash() =>
|
||||
r'344a33715910c38bccc596ac0b543e59cb5752a0';
|
||||
|
||||
/// App lifecycle state provider
|
||||
///
|
||||
/// Copied from [AppLifecycleNotifier].
|
||||
@ProviderFor(AppLifecycleNotifier)
|
||||
final appLifecycleNotifierProvider =
|
||||
AutoDisposeNotifierProvider<AppLifecycleNotifier, String>.internal(
|
||||
AppLifecycleNotifier.new,
|
||||
name: r'appLifecycleNotifierProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appLifecycleNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppLifecycleNotifier = AutoDisposeNotifier<String>;
|
||||
String _$errorTrackerHash() => r'c286897f0ac33b2b619be30d3fd8d18331635b88';
|
||||
|
||||
/// Error tracking provider
|
||||
///
|
||||
/// Copied from [ErrorTracker].
|
||||
@ProviderFor(ErrorTracker)
|
||||
final errorTrackerProvider = AutoDisposeNotifierProvider<ErrorTracker,
|
||||
List<Map<String, dynamic>>>.internal(
|
||||
ErrorTracker.new,
|
||||
name: r'errorTrackerProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$errorTrackerHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ErrorTracker = AutoDisposeNotifier<List<Map<String, dynamic>>>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
51
lib/core/providers/network_providers.dart
Normal file
51
lib/core/providers/network_providers.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
import '../network/dio_client.dart';
|
||||
import '../network/network_info.dart';
|
||||
|
||||
/// Provider for Connectivity instance
|
||||
final connectivityProvider = Provider<Connectivity>((ref) {
|
||||
return Connectivity();
|
||||
});
|
||||
|
||||
/// Provider for FlutterSecureStorage instance
|
||||
final secureStorageProvider = Provider<FlutterSecureStorage>((ref) {
|
||||
return const FlutterSecureStorage();
|
||||
});
|
||||
|
||||
/// Provider for NetworkInfo implementation
|
||||
final networkInfoProvider = Provider<NetworkInfo>((ref) {
|
||||
final connectivity = ref.watch(connectivityProvider);
|
||||
return NetworkInfoImpl(connectivity);
|
||||
});
|
||||
|
||||
/// Provider for DioClient - the main HTTP client
|
||||
final dioClientProvider = Provider<DioClient>((ref) {
|
||||
final networkInfo = ref.watch(networkInfoProvider);
|
||||
final secureStorage = ref.watch(secureStorageProvider);
|
||||
|
||||
return DioClient(
|
||||
networkInfo: networkInfo,
|
||||
secureStorage: secureStorage,
|
||||
);
|
||||
});
|
||||
|
||||
/// Provider for network connectivity stream
|
||||
final networkConnectivityProvider = StreamProvider<bool>((ref) {
|
||||
final networkInfo = ref.watch(networkInfoProvider);
|
||||
return networkInfo.connectionStream;
|
||||
});
|
||||
|
||||
/// Provider for current network status
|
||||
final isConnectedProvider = FutureProvider<bool>((ref) async {
|
||||
final networkInfo = ref.watch(networkInfoProvider);
|
||||
return await networkInfo.isConnected;
|
||||
});
|
||||
|
||||
/// Utility provider to get detailed network connection information
|
||||
final networkConnectionDetailsProvider = FutureProvider((ref) async {
|
||||
final networkInfo = ref.watch(networkInfoProvider) as NetworkInfoImpl;
|
||||
return await networkInfo.getConnectionDetails();
|
||||
});
|
||||
381
lib/core/providers/provider_usage_example.dart
Normal file
381
lib/core/providers/provider_usage_example.dart
Normal file
@@ -0,0 +1,381 @@
|
||||
/// Example usage of the Riverpod state management system
|
||||
///
|
||||
/// This file demonstrates how to properly use the various providers
|
||||
/// created in this comprehensive Riverpod setup following 2.x best practices.
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'providers.dart'; // Import the barrel file
|
||||
|
||||
/// Example widget showing theme provider usage
|
||||
class ThemeExampleWidget extends ConsumerWidget {
|
||||
const ThemeExampleWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch theme-related providers
|
||||
final settingsAsync = ref.watch(appSettingsNotifierProvider);
|
||||
final currentTheme = ref.watch(currentThemeModeProvider);
|
||||
final isDark = ref.watch(isDarkModeProvider);
|
||||
|
||||
return settingsAsync.when(
|
||||
data: (settings) => Card(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Current Theme'),
|
||||
subtitle: Text(currentTheme.value),
|
||||
trailing: Switch(
|
||||
value: isDark,
|
||||
onChanged: (value) {
|
||||
// Update theme mode
|
||||
final notifier = ref.read(appSettingsNotifierProvider.notifier);
|
||||
notifier.updateThemeMode(
|
||||
value ? AppThemeMode.dark : AppThemeMode.light,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Notifications'),
|
||||
trailing: Switch(
|
||||
value: settings.notificationsEnabled,
|
||||
onChanged: (value) {
|
||||
final notifier = ref.read(appSettingsNotifierProvider.notifier);
|
||||
notifier.updateNotificationsEnabled(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loading: () => const Card(
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (error, stack) => Card(
|
||||
child: ListTile(
|
||||
title: const Text('Error loading settings'),
|
||||
subtitle: Text(error.toString()),
|
||||
leading: const Icon(Icons.error),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example widget showing connectivity provider usage
|
||||
class ConnectivityExampleWidget extends ConsumerWidget {
|
||||
const ConnectivityExampleWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch connectivity providers
|
||||
final isConnected = ref.watch(isConnectedProvider);
|
||||
final connectionType = ref.watch(connectionTypeProvider);
|
||||
final networkQuality = ref.watch(networkQualityProvider);
|
||||
final isWifi = ref.watch(isWifiConnectedProvider);
|
||||
|
||||
return Card(
|
||||
color: isConnected == true ? Colors.green.shade50 : Colors.red.shade50,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
isConnected == true ? Icons.wifi : Icons.wifi_off,
|
||||
color: isConnected == true ? Colors.green : Colors.red,
|
||||
),
|
||||
title: Text(isConnected == true ? 'Connected' : 'Disconnected'),
|
||||
subtitle: Text('Connection: ${connectionType.toString().split('.').last}'),
|
||||
),
|
||||
if (isConnected == true) ...[
|
||||
ListTile(
|
||||
title: const Text('Network Quality'),
|
||||
subtitle: Text(networkQuality),
|
||||
),
|
||||
if (isWifi == true)
|
||||
const ListTile(
|
||||
leading: Icon(Icons.wifi, color: Colors.blue),
|
||||
title: Text('Using Wi-Fi'),
|
||||
),
|
||||
],
|
||||
ListTile(
|
||||
title: const Text('Refresh Network Status'),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
ref.read(networkStatusNotifierProvider.notifier).refresh();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example widget showing app initialization and global state
|
||||
class AppStateExampleWidget extends ConsumerWidget {
|
||||
const AppStateExampleWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch app state providers
|
||||
final initAsync = ref.watch(appInitializationProvider);
|
||||
final globalState = ref.watch(globalAppStateProvider);
|
||||
final isReady = ref.watch(isAppReadyProvider);
|
||||
|
||||
return Card(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('App Status'),
|
||||
subtitle: Text(isReady == true ? 'Ready' : 'Initializing...'),
|
||||
leading: Icon(
|
||||
isReady == true ? Icons.check_circle : Icons.hourglass_empty,
|
||||
color: isReady == true ? Colors.green : Colors.orange,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('App Version'),
|
||||
subtitle: Text(globalState['appVersion'] ?? 'Unknown'),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Build Mode'),
|
||||
subtitle: Text(globalState['buildMode'] ?? 'Unknown'),
|
||||
),
|
||||
initAsync.when(
|
||||
data: (data) => ListTile(
|
||||
title: const Text('Initialization Status'),
|
||||
subtitle: Text(data.state.toString().split('.').last),
|
||||
trailing: data.state == AppInitializationState.error
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
ref.read(appInitializationProvider.notifier).retry();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
),
|
||||
loading: () => const ListTile(
|
||||
title: Text('Initialization Status'),
|
||||
subtitle: Text('Loading...'),
|
||||
trailing: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
),
|
||||
error: (error, stack) => ListTile(
|
||||
title: const Text('Initialization Error'),
|
||||
subtitle: Text(error.toString()),
|
||||
leading: const Icon(Icons.error, color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example widget showing storage provider usage
|
||||
class StorageExampleWidget extends ConsumerWidget {
|
||||
const StorageExampleWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch storage providers
|
||||
final storageManager = ref.watch(storageManagerProvider);
|
||||
|
||||
final hiveStats = storageManager['hive'] as Map<String, dynamic>? ?? {};
|
||||
final secureStorageInfo = storageManager['secureStorage'] as Map<String, dynamic>? ?? {};
|
||||
final healthInfo = storageManager['health'] as Map<String, dynamic>? ?? {};
|
||||
|
||||
return Card(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Storage Health'),
|
||||
subtitle: Text(healthInfo['isHealthy'] == true ? 'Healthy' : 'Issues detected'),
|
||||
leading: Icon(
|
||||
healthInfo['isHealthy'] == true ? Icons.check_circle : Icons.warning,
|
||||
color: healthInfo['isHealthy'] == true ? Colors.green : Colors.orange,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Hive Storage'),
|
||||
subtitle: Text('${hiveStats['appSettingsCount'] ?? 0} settings, ${hiveStats['cacheItemsCount'] ?? 0} cache items'),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Secure Storage'),
|
||||
subtitle: Text('${secureStorageInfo['keyCount'] ?? 0} secure keys stored'),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
ref.read(storageManagerProvider.notifier).performMaintenance();
|
||||
},
|
||||
child: const Text('Maintenance'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
ref.read(storageHealthMonitorProvider.notifier).performHealthCheck();
|
||||
},
|
||||
child: const Text('Health Check'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example widget showing feature flags usage
|
||||
class FeatureFlagsExampleWidget extends ConsumerWidget {
|
||||
const FeatureFlagsExampleWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final featureFlags = ref.watch(featureFlagsProvider);
|
||||
final flagsNotifier = ref.watch(featureFlagsProvider.notifier);
|
||||
|
||||
return Card(
|
||||
child: Column(
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text('Feature Flags'),
|
||||
leading: Icon(Icons.flag),
|
||||
),
|
||||
...featureFlags.entries.map((entry) => ListTile(
|
||||
title: Text(entry.key),
|
||||
trailing: Switch(
|
||||
value: entry.value,
|
||||
onChanged: (value) {
|
||||
if (value) {
|
||||
flagsNotifier.enableFeature(entry.key);
|
||||
} else {
|
||||
flagsNotifier.disableFeature(entry.key);
|
||||
}
|
||||
},
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example screen demonstrating all provider usage
|
||||
class ProviderDemoScreen extends ConsumerWidget {
|
||||
const ProviderDemoScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Provider Demo'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
// Refresh all providers
|
||||
ref.invalidate(appSettingsNotifierProvider);
|
||||
ref.invalidate(networkStatusNotifierProvider);
|
||||
ref.read(globalAppStateProvider.notifier).reset();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: const SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
AppStateExampleWidget(),
|
||||
SizedBox(height: 16),
|
||||
ThemeExampleWidget(),
|
||||
SizedBox(height: 16),
|
||||
ConnectivityExampleWidget(),
|
||||
SizedBox(height: 16),
|
||||
StorageExampleWidget(),
|
||||
SizedBox(height: 16),
|
||||
FeatureFlagsExampleWidget(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example of using providers in a lifecycle-aware way
|
||||
class ProviderLifecycleExample extends ConsumerStatefulWidget {
|
||||
const ProviderLifecycleExample({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ProviderLifecycleExample> createState() => _ProviderLifecycleExampleState();
|
||||
}
|
||||
|
||||
class _ProviderLifecycleExampleState extends ConsumerState<ProviderLifecycleExample>
|
||||
with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
// Listen to app initialization
|
||||
ref.listenManual(appInitializationProvider, (previous, next) {
|
||||
next.when(
|
||||
data: (data) {
|
||||
if (data.state == AppInitializationState.initialized) {
|
||||
debugPrint('✅ App initialized successfully');
|
||||
}
|
||||
},
|
||||
loading: () => debugPrint('🔄 App initializing...'),
|
||||
error: (error, stack) => debugPrint('❌ App initialization failed: $error'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
super.didChangeAppLifecycleState(state);
|
||||
|
||||
// Update app lifecycle state provider
|
||||
ref.read(appLifecycleNotifierProvider.notifier).updateState(state.name);
|
||||
|
||||
// Handle different lifecycle states
|
||||
switch (state) {
|
||||
case AppLifecycleState.paused:
|
||||
// App is paused, save important state
|
||||
ref.read(storageManagerProvider.notifier).performMaintenance();
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
// App is resumed, refresh network status
|
||||
ref.read(networkStatusNotifierProvider.notifier).refresh();
|
||||
break;
|
||||
case AppLifecycleState.detached:
|
||||
// App is detached, cleanup if needed
|
||||
break;
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.hidden:
|
||||
// Handle inactive/hidden states
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This would be your main app content
|
||||
return const ProviderDemoScreen();
|
||||
}
|
||||
}
|
||||
267
lib/core/providers/providers.dart
Normal file
267
lib/core/providers/providers.dart
Normal file
@@ -0,0 +1,267 @@
|
||||
/// 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<String> criticalProviders = [
|
||||
'appInitializationProvider',
|
||||
'appSettingsNotifierProvider',
|
||||
'networkStatusNotifierProvider',
|
||||
'secureStorageNotifierProvider',
|
||||
];
|
||||
|
||||
/// List of providers that can be initialized lazily when first accessed
|
||||
static const List<String> lazyProviders = [
|
||||
'featureFlagsProvider',
|
||||
'appConfigurationProvider',
|
||||
'networkHistoryNotifierProvider',
|
||||
'errorTrackerProvider',
|
||||
];
|
||||
|
||||
/// Provider categories for better organization and management
|
||||
static const Map<String, List<String>> 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<String> getProvidersByCategory(String category) {
|
||||
return providerCategories[category] ?? [];
|
||||
}
|
||||
|
||||
/// Get all provider names
|
||||
static List<String> 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<String, String> 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<String> 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<String, String> 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<Data> build() async {
|
||||
return await _loadData();
|
||||
}
|
||||
|
||||
Future<void> 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<String> 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',
|
||||
];
|
||||
}
|
||||
373
lib/core/providers/storage_providers.dart
Normal file
373
lib/core/providers/storage_providers.dart
Normal file
@@ -0,0 +1,373 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../database/hive_service.dart';
|
||||
import '../database/models/app_settings.dart';
|
||||
import '../database/models/cache_item.dart';
|
||||
import '../database/models/user_preferences.dart';
|
||||
|
||||
part 'storage_providers.g.dart';
|
||||
|
||||
/// Secure storage configuration
|
||||
const _secureStorageOptions = FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(
|
||||
encryptedSharedPreferences: true,
|
||||
),
|
||||
iOptions: IOSOptions(
|
||||
accessibility: KeychainAccessibility.first_unlock_this_device,
|
||||
),
|
||||
);
|
||||
|
||||
/// Secure storage provider
|
||||
@riverpod
|
||||
FlutterSecureStorage secureStorage(SecureStorageRef ref) {
|
||||
return _secureStorageOptions;
|
||||
}
|
||||
|
||||
/// Secure storage notifier for managing secure data
|
||||
@riverpod
|
||||
class SecureStorageNotifier extends _$SecureStorageNotifier {
|
||||
late FlutterSecureStorage _storage;
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> build() async {
|
||||
_storage = ref.read(secureStorageProvider);
|
||||
return await _loadAllSecureData();
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _loadAllSecureData() async {
|
||||
try {
|
||||
final allData = await _storage.readAll();
|
||||
return allData;
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error loading secure storage data: $e');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/// Store secure value
|
||||
Future<void> store(String key, String value) async {
|
||||
try {
|
||||
await _storage.write(key: key, value: value);
|
||||
state = AsyncValue.data({...state.value ?? {}, key: value});
|
||||
debugPrint('🔐 Securely stored: $key');
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint('❌ Error storing secure value: $error');
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve secure value
|
||||
Future<String?> retrieve(String key) async {
|
||||
try {
|
||||
return await _storage.read(key: key);
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error retrieving secure value: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete secure value
|
||||
Future<void> delete(String key) async {
|
||||
try {
|
||||
await _storage.delete(key: key);
|
||||
final currentData = Map<String, String>.from(state.value ?? {});
|
||||
currentData.remove(key);
|
||||
state = AsyncValue.data(currentData);
|
||||
debugPrint('🗑️ Deleted secure key: $key');
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint('❌ Error deleting secure value: $error');
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all secure storage
|
||||
Future<void> clearAll() async {
|
||||
try {
|
||||
await _storage.deleteAll();
|
||||
state = const AsyncValue.data({});
|
||||
debugPrint('🧹 Cleared all secure storage');
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint('❌ Error clearing secure storage: $error');
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if key exists
|
||||
Future<bool> containsKey(String key) async {
|
||||
try {
|
||||
return await _storage.containsKey(key: key);
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error checking key existence: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Refresh storage data
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = AsyncValue.data(await _loadAllSecureData());
|
||||
}
|
||||
}
|
||||
|
||||
/// Hive storage providers
|
||||
@riverpod
|
||||
Box<AppSettings> appSettingsBox(AppSettingsBoxRef ref) {
|
||||
return HiveService.appSettingsBox;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Box<CacheItem> cacheBox(CacheBoxRef ref) {
|
||||
return HiveService.cacheBox;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Box<UserPreferences> userPreferencesBox(UserPreferencesBoxRef ref) {
|
||||
return HiveService.userDataBox;
|
||||
}
|
||||
|
||||
/// Hive storage notifier for managing Hive data
|
||||
@riverpod
|
||||
class HiveStorageNotifier extends _$HiveStorageNotifier {
|
||||
@override
|
||||
Map<String, dynamic> build() {
|
||||
final appSettingsBox = ref.watch(appSettingsBoxProvider);
|
||||
final cacheBox = ref.watch(cacheBoxProvider);
|
||||
final userPreferencesBox = ref.watch(userPreferencesBoxProvider);
|
||||
|
||||
return {
|
||||
'appSettingsCount': appSettingsBox.length,
|
||||
'cacheItemsCount': cacheBox.length,
|
||||
'userPreferencesCount': userPreferencesBox.length,
|
||||
'totalSize': _calculateTotalSize(),
|
||||
'lastUpdated': DateTime.now().toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
int _calculateTotalSize() {
|
||||
try {
|
||||
final appSettingsBox = ref.read(appSettingsBoxProvider);
|
||||
final cacheBox = ref.read(cacheBoxProvider);
|
||||
final userPreferencesBox = ref.read(userPreferencesBoxProvider);
|
||||
|
||||
// Rough estimation of storage size
|
||||
return appSettingsBox.length + cacheBox.length + userPreferencesBox.length;
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error calculating storage size: $e');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compact all Hive boxes
|
||||
Future<void> compactAll() async {
|
||||
try {
|
||||
final appSettingsBox = ref.read(appSettingsBoxProvider);
|
||||
final cacheBox = ref.read(cacheBoxProvider);
|
||||
final userPreferencesBox = ref.read(userPreferencesBoxProvider);
|
||||
|
||||
await Future.wait([
|
||||
appSettingsBox.compact(),
|
||||
cacheBox.compact(),
|
||||
userPreferencesBox.compact(),
|
||||
]);
|
||||
|
||||
_updateStats();
|
||||
debugPrint('🗜️ Compacted all Hive storage');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error compacting storage: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all cache data
|
||||
Future<void> clearCache() async {
|
||||
try {
|
||||
final cacheBox = ref.read(cacheBoxProvider);
|
||||
await cacheBox.clear();
|
||||
|
||||
_updateStats();
|
||||
debugPrint('🧹 Cleared all cache data');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error clearing cache: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Get storage statistics
|
||||
Map<String, dynamic> getStorageStats() {
|
||||
try {
|
||||
final appSettingsBox = ref.read(appSettingsBoxProvider);
|
||||
final cacheBox = ref.read(cacheBoxProvider);
|
||||
final userPreferencesBox = ref.read(userPreferencesBoxProvider);
|
||||
|
||||
return {
|
||||
'appSettings': {
|
||||
'count': appSettingsBox.length,
|
||||
'keys': appSettingsBox.keys.toList(),
|
||||
'isEmpty': appSettingsBox.isEmpty,
|
||||
},
|
||||
'cache': {
|
||||
'count': cacheBox.length,
|
||||
'keys': cacheBox.keys.take(10).toList(), // Show only first 10 keys
|
||||
'isEmpty': cacheBox.isEmpty,
|
||||
},
|
||||
'userPreferences': {
|
||||
'count': userPreferencesBox.length,
|
||||
'keys': userPreferencesBox.keys.toList(),
|
||||
'isEmpty': userPreferencesBox.isEmpty,
|
||||
},
|
||||
'total': {
|
||||
'items': appSettingsBox.length + cacheBox.length + userPreferencesBox.length,
|
||||
'estimatedSize': _calculateTotalSize(),
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error getting storage stats: $e');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void _updateStats() {
|
||||
state = {
|
||||
...state,
|
||||
'appSettingsCount': ref.read(appSettingsBoxProvider).length,
|
||||
'cacheItemsCount': ref.read(cacheBoxProvider).length,
|
||||
'userPreferencesCount': ref.read(userPreferencesBoxProvider).length,
|
||||
'totalSize': _calculateTotalSize(),
|
||||
'lastUpdated': DateTime.now().toIso8601String(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage health monitor
|
||||
@riverpod
|
||||
class StorageHealthMonitor extends _$StorageHealthMonitor {
|
||||
@override
|
||||
Map<String, dynamic> build() {
|
||||
return {
|
||||
'isHealthy': true,
|
||||
'lastCheck': DateTime.now().toIso8601String(),
|
||||
'errors': <String>[],
|
||||
'warnings': <String>[],
|
||||
};
|
||||
}
|
||||
|
||||
/// Perform storage health check
|
||||
Future<void> performHealthCheck() async {
|
||||
final errors = <String>[];
|
||||
final warnings = <String>[];
|
||||
|
||||
try {
|
||||
// Check secure storage
|
||||
final secureStorage = ref.read(secureStorageProvider);
|
||||
try {
|
||||
await secureStorage.containsKey(key: 'health_check');
|
||||
} catch (e) {
|
||||
errors.add('Secure storage error: $e');
|
||||
}
|
||||
|
||||
// Check Hive boxes
|
||||
try {
|
||||
final appSettingsBox = ref.read(appSettingsBoxProvider);
|
||||
final cacheBox = ref.read(cacheBoxProvider);
|
||||
final userPreferencesBox = ref.read(userPreferencesBoxProvider);
|
||||
|
||||
if (!appSettingsBox.isOpen) errors.add('App settings box is not open');
|
||||
if (!cacheBox.isOpen) errors.add('Cache box is not open');
|
||||
if (!userPreferencesBox.isOpen) errors.add('User preferences box is not open');
|
||||
|
||||
// Check for large cache
|
||||
if (cacheBox.length > 1000) {
|
||||
warnings.add('Cache has more than 1000 items, consider cleanup');
|
||||
}
|
||||
} catch (e) {
|
||||
errors.add('Hive storage error: $e');
|
||||
}
|
||||
|
||||
state = {
|
||||
'isHealthy': errors.isEmpty,
|
||||
'lastCheck': DateTime.now().toIso8601String(),
|
||||
'errors': errors,
|
||||
'warnings': warnings,
|
||||
};
|
||||
|
||||
debugPrint('🏥 Storage health check completed: ${errors.isEmpty ? '✅ Healthy' : '❌ Issues found'}');
|
||||
} catch (e) {
|
||||
state = {
|
||||
'isHealthy': false,
|
||||
'lastCheck': DateTime.now().toIso8601String(),
|
||||
'errors': ['Health check failed: $e'],
|
||||
'warnings': warnings,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Get health status
|
||||
bool get isHealthy => state['isHealthy'] ?? false;
|
||||
|
||||
/// Get errors
|
||||
List<String> get errors => List<String>.from(state['errors'] ?? []);
|
||||
|
||||
/// Get warnings
|
||||
List<String> get warnings => List<String>.from(state['warnings'] ?? []);
|
||||
}
|
||||
|
||||
/// Unified storage manager
|
||||
@riverpod
|
||||
class StorageManager extends _$StorageManager {
|
||||
@override
|
||||
Map<String, dynamic> build() {
|
||||
final hiveStats = ref.watch(hiveStorageNotifierProvider);
|
||||
final secureStorageAsync = ref.watch(secureStorageNotifierProvider);
|
||||
final healthStatus = ref.watch(storageHealthMonitorProvider);
|
||||
|
||||
return {
|
||||
'hive': hiveStats,
|
||||
'secureStorage': secureStorageAsync.when(
|
||||
data: (data) => {
|
||||
'keyCount': data.length,
|
||||
'isAvailable': true,
|
||||
},
|
||||
loading: () => {'isAvailable': false, 'isLoading': true},
|
||||
error: (_, __) => {'isAvailable': false, 'hasError': true},
|
||||
),
|
||||
'health': healthStatus,
|
||||
'lastUpdated': DateTime.now().toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Clear all storage
|
||||
Future<void> clearAllStorage() async {
|
||||
try {
|
||||
// Clear secure storage
|
||||
await ref.read(secureStorageNotifierProvider.notifier).clearAll();
|
||||
|
||||
// Clear Hive storage
|
||||
await ref.read(hiveStorageNotifierProvider.notifier).clearCache();
|
||||
|
||||
debugPrint('🧹 Cleared all storage');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error clearing all storage: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform maintenance
|
||||
Future<void> performMaintenance() async {
|
||||
try {
|
||||
// Compact storage
|
||||
await ref.read(hiveStorageNotifierProvider.notifier).compactAll();
|
||||
|
||||
// Perform health check
|
||||
await ref.read(storageHealthMonitorProvider.notifier).performHealthCheck();
|
||||
|
||||
debugPrint('🔧 Storage maintenance completed');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Error during maintenance: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Get storage overview
|
||||
Map<String, dynamic> getStorageOverview() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
151
lib/core/providers/storage_providers.g.dart
Normal file
151
lib/core/providers/storage_providers.g.dart
Normal file
@@ -0,0 +1,151 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'storage_providers.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$secureStorageHash() => r'9cd02a4033a37568df4c1778f34709abb5853782';
|
||||
|
||||
/// Secure storage provider
|
||||
///
|
||||
/// Copied from [secureStorage].
|
||||
@ProviderFor(secureStorage)
|
||||
final secureStorageProvider =
|
||||
AutoDisposeProvider<FlutterSecureStorage>.internal(
|
||||
secureStorage,
|
||||
name: r'secureStorageProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$secureStorageHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef SecureStorageRef = AutoDisposeProviderRef<FlutterSecureStorage>;
|
||||
String _$appSettingsBoxHash() => r'9e348c0084f7f23850f09adb2e6496fdbf8f2bdf';
|
||||
|
||||
/// Hive storage providers
|
||||
///
|
||||
/// Copied from [appSettingsBox].
|
||||
@ProviderFor(appSettingsBox)
|
||||
final appSettingsBoxProvider = AutoDisposeProvider<Box<AppSettings>>.internal(
|
||||
appSettingsBox,
|
||||
name: r'appSettingsBoxProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appSettingsBoxHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef AppSettingsBoxRef = AutoDisposeProviderRef<Box<AppSettings>>;
|
||||
String _$cacheBoxHash() => r'949b55a2b7423b7fa7182b8e45adf02367ab8c7c';
|
||||
|
||||
/// See also [cacheBox].
|
||||
@ProviderFor(cacheBox)
|
||||
final cacheBoxProvider = AutoDisposeProvider<Box<CacheItem>>.internal(
|
||||
cacheBox,
|
||||
name: r'cacheBoxProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$cacheBoxHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef CacheBoxRef = AutoDisposeProviderRef<Box<CacheItem>>;
|
||||
String _$userPreferencesBoxHash() =>
|
||||
r'38e2eab12afb00cca5ad2f48bf1f9ec76cc962c8';
|
||||
|
||||
/// See also [userPreferencesBox].
|
||||
@ProviderFor(userPreferencesBox)
|
||||
final userPreferencesBoxProvider =
|
||||
AutoDisposeProvider<Box<UserPreferences>>.internal(
|
||||
userPreferencesBox,
|
||||
name: r'userPreferencesBoxProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$userPreferencesBoxHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef UserPreferencesBoxRef = AutoDisposeProviderRef<Box<UserPreferences>>;
|
||||
String _$secureStorageNotifierHash() =>
|
||||
r'08d6cb392865d7483027fde37192c07cb944c45f';
|
||||
|
||||
/// Secure storage notifier for managing secure data
|
||||
///
|
||||
/// Copied from [SecureStorageNotifier].
|
||||
@ProviderFor(SecureStorageNotifier)
|
||||
final secureStorageNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||
SecureStorageNotifier, Map<String, String>>.internal(
|
||||
SecureStorageNotifier.new,
|
||||
name: r'secureStorageNotifierProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$secureStorageNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$SecureStorageNotifier = AutoDisposeAsyncNotifier<Map<String, String>>;
|
||||
String _$hiveStorageNotifierHash() =>
|
||||
r'5d91bf162282fcfbef13aa7296255bb87640af51';
|
||||
|
||||
/// Hive storage notifier for managing Hive data
|
||||
///
|
||||
/// Copied from [HiveStorageNotifier].
|
||||
@ProviderFor(HiveStorageNotifier)
|
||||
final hiveStorageNotifierProvider = AutoDisposeNotifierProvider<
|
||||
HiveStorageNotifier, Map<String, dynamic>>.internal(
|
||||
HiveStorageNotifier.new,
|
||||
name: r'hiveStorageNotifierProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$hiveStorageNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$HiveStorageNotifier = AutoDisposeNotifier<Map<String, dynamic>>;
|
||||
String _$storageHealthMonitorHash() =>
|
||||
r'1d52e331a84bd59a36055f5e8963eaa996f9c235';
|
||||
|
||||
/// Storage health monitor
|
||||
///
|
||||
/// Copied from [StorageHealthMonitor].
|
||||
@ProviderFor(StorageHealthMonitor)
|
||||
final storageHealthMonitorProvider = AutoDisposeNotifierProvider<
|
||||
StorageHealthMonitor, Map<String, dynamic>>.internal(
|
||||
StorageHealthMonitor.new,
|
||||
name: r'storageHealthMonitorProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$storageHealthMonitorHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$StorageHealthMonitor = AutoDisposeNotifier<Map<String, dynamic>>;
|
||||
String _$storageManagerHash() => r'8e017d34c8c574dd2777d6478af3cd921448b080';
|
||||
|
||||
/// Unified storage manager
|
||||
///
|
||||
/// Copied from [StorageManager].
|
||||
@ProviderFor(StorageManager)
|
||||
final storageManagerProvider =
|
||||
AutoDisposeNotifierProvider<StorageManager, Map<String, dynamic>>.internal(
|
||||
StorageManager.new,
|
||||
name: r'storageManagerProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$storageManagerHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$StorageManager = AutoDisposeNotifier<Map<String, dynamic>>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
231
lib/core/providers/theme_providers.dart
Normal file
231
lib/core/providers/theme_providers.dart
Normal file
@@ -0,0 +1,231 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../database/models/app_settings.dart';
|
||||
import '../database/repositories/settings_repository.dart';
|
||||
|
||||
part 'theme_providers.g.dart';
|
||||
|
||||
/// Theme mode enumeration
|
||||
enum AppThemeMode {
|
||||
light('light'),
|
||||
dark('dark'),
|
||||
system('system');
|
||||
|
||||
const AppThemeMode(this.value);
|
||||
final String value;
|
||||
|
||||
static AppThemeMode fromString(String value) {
|
||||
return AppThemeMode.values.firstWhere(
|
||||
(mode) => mode.value == value,
|
||||
orElse: () => AppThemeMode.system,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings repository provider
|
||||
@riverpod
|
||||
SettingsRepository settingsRepository(SettingsRepositoryRef ref) {
|
||||
return SettingsRepository();
|
||||
}
|
||||
|
||||
/// Current app settings provider
|
||||
@riverpod
|
||||
class AppSettingsNotifier extends _$AppSettingsNotifier {
|
||||
late SettingsRepository _repository;
|
||||
|
||||
@override
|
||||
Future<AppSettings> build() async {
|
||||
_repository = ref.read(settingsRepositoryProvider);
|
||||
return _repository.getSettings();
|
||||
}
|
||||
|
||||
/// Update theme mode
|
||||
Future<void> updateThemeMode(AppThemeMode mode) async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
await _repository.updateThemeMode(mode.value);
|
||||
final updatedSettings = _repository.getSettings();
|
||||
state = AsyncValue.data(updatedSettings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update locale
|
||||
Future<void> updateLocale(String locale) async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
await _repository.updateLocale(locale);
|
||||
final updatedSettings = _repository.getSettings();
|
||||
state = AsyncValue.data(updatedSettings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update notifications enabled
|
||||
Future<void> updateNotificationsEnabled(bool enabled) async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
await _repository.updateNotificationsEnabled(enabled);
|
||||
final updatedSettings = _repository.getSettings();
|
||||
state = AsyncValue.data(updatedSettings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update analytics enabled
|
||||
Future<void> updateAnalyticsEnabled(bool enabled) async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
await _repository.updateAnalyticsEnabled(enabled);
|
||||
final updatedSettings = _repository.getSettings();
|
||||
state = AsyncValue.data(updatedSettings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set custom setting
|
||||
Future<void> setCustomSetting(String key, dynamic value) async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
await _repository.setCustomSetting(key, value);
|
||||
final updatedSettings = _repository.getSettings();
|
||||
state = AsyncValue.data(updatedSettings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset to default settings
|
||||
Future<void> resetToDefault() async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
await _repository.resetToDefault();
|
||||
final updatedSettings = _repository.getSettings();
|
||||
state = AsyncValue.data(updatedSettings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// Refresh settings from storage
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
final settings = _repository.getSettings();
|
||||
state = AsyncValue.data(settings);
|
||||
} catch (error, stackTrace) {
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Current theme mode provider
|
||||
@riverpod
|
||||
AppThemeMode currentThemeMode(CurrentThemeModeRef ref) {
|
||||
final settingsAsync = ref.watch(appSettingsNotifierProvider);
|
||||
|
||||
return settingsAsync.when(
|
||||
data: (settings) => AppThemeMode.fromString(settings.themeMode),
|
||||
loading: () => AppThemeMode.system,
|
||||
error: (_, __) => AppThemeMode.system,
|
||||
);
|
||||
}
|
||||
|
||||
/// Effective theme mode provider (resolves system theme)
|
||||
@riverpod
|
||||
ThemeMode effectiveThemeMode(EffectiveThemeModeRef ref) {
|
||||
final themeMode = ref.watch(currentThemeModeProvider);
|
||||
|
||||
switch (themeMode) {
|
||||
case AppThemeMode.light:
|
||||
return ThemeMode.light;
|
||||
case AppThemeMode.dark:
|
||||
return ThemeMode.dark;
|
||||
case AppThemeMode.system:
|
||||
return ThemeMode.system;
|
||||
}
|
||||
}
|
||||
|
||||
/// Is dark mode active provider
|
||||
@riverpod
|
||||
bool isDarkMode(IsDarkModeRef ref) {
|
||||
final themeMode = ref.watch(currentThemeModeProvider);
|
||||
|
||||
switch (themeMode) {
|
||||
case AppThemeMode.light:
|
||||
return false;
|
||||
case AppThemeMode.dark:
|
||||
return true;
|
||||
case AppThemeMode.system:
|
||||
// Get platform brightness from MediaQuery
|
||||
// This will be handled at the widget level
|
||||
return false; // Default fallback
|
||||
}
|
||||
}
|
||||
|
||||
/// Current locale provider
|
||||
@riverpod
|
||||
Locale currentLocale(CurrentLocaleRef ref) {
|
||||
final settingsAsync = ref.watch(appSettingsNotifierProvider);
|
||||
|
||||
return settingsAsync.when(
|
||||
data: (settings) => Locale(settings.locale),
|
||||
loading: () => const Locale('en'),
|
||||
error: (_, __) => const Locale('en'),
|
||||
);
|
||||
}
|
||||
|
||||
/// Settings stream provider for reactive updates
|
||||
@riverpod
|
||||
class SettingsStreamNotifier extends _$SettingsStreamNotifier {
|
||||
@override
|
||||
Stream<AppSettings> build() {
|
||||
final repository = ref.read(settingsRepositoryProvider);
|
||||
return repository.watchSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/// Theme preferences provider for quick access
|
||||
@riverpod
|
||||
class ThemePreferences extends _$ThemePreferences {
|
||||
@override
|
||||
Map<String, dynamic> build() {
|
||||
final settingsAsync = ref.watch(appSettingsNotifierProvider);
|
||||
|
||||
return settingsAsync.when(
|
||||
data: (settings) => {
|
||||
'themeMode': settings.themeMode,
|
||||
'isDarkMode': AppThemeMode.fromString(settings.themeMode) == AppThemeMode.dark,
|
||||
'locale': settings.locale,
|
||||
'analyticsEnabled': settings.analyticsEnabled,
|
||||
'notificationsEnabled': settings.notificationsEnabled,
|
||||
},
|
||||
loading: () => {
|
||||
'themeMode': 'system',
|
||||
'isDarkMode': false,
|
||||
'locale': 'en',
|
||||
'analyticsEnabled': false,
|
||||
'notificationsEnabled': true,
|
||||
},
|
||||
error: (_, __) => {
|
||||
'themeMode': 'system',
|
||||
'isDarkMode': false,
|
||||
'locale': 'en',
|
||||
'analyticsEnabled': false,
|
||||
'notificationsEnabled': true,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
153
lib/core/providers/theme_providers.g.dart
Normal file
153
lib/core/providers/theme_providers.g.dart
Normal file
@@ -0,0 +1,153 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'theme_providers.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$settingsRepositoryHash() =>
|
||||
r'0203e31bb994214ce864bf95a7afa14a8a14b812';
|
||||
|
||||
/// Settings repository provider
|
||||
///
|
||||
/// Copied from [settingsRepository].
|
||||
@ProviderFor(settingsRepository)
|
||||
final settingsRepositoryProvider =
|
||||
AutoDisposeProvider<SettingsRepository>.internal(
|
||||
settingsRepository,
|
||||
name: r'settingsRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$settingsRepositoryHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef SettingsRepositoryRef = AutoDisposeProviderRef<SettingsRepository>;
|
||||
String _$currentThemeModeHash() => r'6cd4101e1d0f6cbd7851f117872cd49253fe0564';
|
||||
|
||||
/// Current theme mode provider
|
||||
///
|
||||
/// Copied from [currentThemeMode].
|
||||
@ProviderFor(currentThemeMode)
|
||||
final currentThemeModeProvider = AutoDisposeProvider<AppThemeMode>.internal(
|
||||
currentThemeMode,
|
||||
name: r'currentThemeModeProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$currentThemeModeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef CurrentThemeModeRef = AutoDisposeProviderRef<AppThemeMode>;
|
||||
String _$effectiveThemeModeHash() =>
|
||||
r'd747fdd8489857c595ae766ee6a9497c4ad360c0';
|
||||
|
||||
/// Effective theme mode provider (resolves system theme)
|
||||
///
|
||||
/// Copied from [effectiveThemeMode].
|
||||
@ProviderFor(effectiveThemeMode)
|
||||
final effectiveThemeModeProvider = AutoDisposeProvider<ThemeMode>.internal(
|
||||
effectiveThemeMode,
|
||||
name: r'effectiveThemeModeProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$effectiveThemeModeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef EffectiveThemeModeRef = AutoDisposeProviderRef<ThemeMode>;
|
||||
String _$isDarkModeHash() => r'e76c5818694a33e63bd0a8ba0b7494d7ee12cff5';
|
||||
|
||||
/// Is dark mode active provider
|
||||
///
|
||||
/// Copied from [isDarkMode].
|
||||
@ProviderFor(isDarkMode)
|
||||
final isDarkModeProvider = AutoDisposeProvider<bool>.internal(
|
||||
isDarkMode,
|
||||
name: r'isDarkModeProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$isDarkModeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef IsDarkModeRef = AutoDisposeProviderRef<bool>;
|
||||
String _$currentLocaleHash() => r'c3cb4000a5eefa748ca41e50818b27323e61605a';
|
||||
|
||||
/// Current locale provider
|
||||
///
|
||||
/// Copied from [currentLocale].
|
||||
@ProviderFor(currentLocale)
|
||||
final currentLocaleProvider = AutoDisposeProvider<Locale>.internal(
|
||||
currentLocale,
|
||||
name: r'currentLocaleProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$currentLocaleHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef CurrentLocaleRef = AutoDisposeProviderRef<Locale>;
|
||||
String _$appSettingsNotifierHash() =>
|
||||
r'3a66de82c9b8f75bf34ffc7755b145a6d1e9c21e';
|
||||
|
||||
/// Current app settings provider
|
||||
///
|
||||
/// Copied from [AppSettingsNotifier].
|
||||
@ProviderFor(AppSettingsNotifier)
|
||||
final appSettingsNotifierProvider =
|
||||
AutoDisposeAsyncNotifierProvider<AppSettingsNotifier, AppSettings>.internal(
|
||||
AppSettingsNotifier.new,
|
||||
name: r'appSettingsNotifierProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appSettingsNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppSettingsNotifier = AutoDisposeAsyncNotifier<AppSettings>;
|
||||
String _$settingsStreamNotifierHash() =>
|
||||
r'1c1e31439ee63edc3217a20c0198bbb2aff6e033';
|
||||
|
||||
/// Settings stream provider for reactive updates
|
||||
///
|
||||
/// Copied from [SettingsStreamNotifier].
|
||||
@ProviderFor(SettingsStreamNotifier)
|
||||
final settingsStreamNotifierProvider = AutoDisposeStreamNotifierProvider<
|
||||
SettingsStreamNotifier, AppSettings>.internal(
|
||||
SettingsStreamNotifier.new,
|
||||
name: r'settingsStreamNotifierProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$settingsStreamNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$SettingsStreamNotifier = AutoDisposeStreamNotifier<AppSettings>;
|
||||
String _$themePreferencesHash() => r'71778e4afc614e1566d4a15131e2ab5d2302e57b';
|
||||
|
||||
/// Theme preferences provider for quick access
|
||||
///
|
||||
/// Copied from [ThemePreferences].
|
||||
@ProviderFor(ThemePreferences)
|
||||
final themePreferencesProvider = AutoDisposeNotifierProvider<ThemePreferences,
|
||||
Map<String, dynamic>>.internal(
|
||||
ThemePreferences.new,
|
||||
name: r'themePreferencesProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$themePreferencesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ThemePreferences = AutoDisposeNotifier<Map<String, dynamic>>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
Reference in New Issue
Block a user