This commit is contained in:
2025-09-26 18:48:14 +07:00
parent 382a0e7909
commit 30ed6b39b5
85 changed files with 20722 additions and 112 deletions

View File

@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../../../core/constants/storage_constants.dart';
import '../../../core/network/dio_client.dart';
import '../../../core/providers/network_providers.dart';
/// Secure storage provider
final secureStorageProvider = Provider<FlutterSecureStorage>(
(ref) => const FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
iOptions: IOSOptions(),
),
);
/// HTTP client provider
final httpClientProvider = Provider<DioClient>(
(ref) {
final networkInfo = ref.watch(networkInfoProvider);
final secureStorage = ref.watch(secureStorageProvider);
return DioClient(
networkInfo: networkInfo,
secureStorage: secureStorage,
);
},
);
/// App settings Hive box provider
final appSettingsBoxProvider = Provider<Box>(
(ref) => Hive.box(StorageConstants.appSettingsBox),
);
/// Cache Hive box provider
final cacheBoxProvider = Provider<Box>(
(ref) => Hive.box(StorageConstants.cacheBox),
);
/// User data Hive box provider
final userDataBoxProvider = Provider<Box>(
(ref) => Hive.box(StorageConstants.userDataBox),
);
/// Theme mode provider
final themeModeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
(ref) => ThemeModeNotifier(ref.watch(appSettingsBoxProvider)),
);
/// Theme mode notifier
class ThemeModeNotifier extends StateNotifier<ThemeMode> {
final Box _box;
ThemeModeNotifier(this._box) : super(ThemeMode.system) {
_loadThemeMode();
}
void _loadThemeMode() {
final isDarkMode = _box.get(StorageConstants.isDarkModeKey, defaultValue: null);
if (isDarkMode == null) {
state = ThemeMode.system;
} else {
state = isDarkMode ? ThemeMode.dark : ThemeMode.light;
}
}
Future<void> setThemeMode(ThemeMode mode) async {
state = mode;
switch (mode) {
case ThemeMode.system:
await _box.delete(StorageConstants.isDarkModeKey);
break;
case ThemeMode.light:
await _box.put(StorageConstants.isDarkModeKey, false);
break;
case ThemeMode.dark:
await _box.put(StorageConstants.isDarkModeKey, true);
break;
}
}
Future<void> toggleTheme() async {
switch (state) {
case ThemeMode.system:
case ThemeMode.light:
await setThemeMode(ThemeMode.dark);
break;
case ThemeMode.dark:
await setThemeMode(ThemeMode.light);
break;
}
}
}

View File

@@ -0,0 +1,345 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter/foundation.dart';
part 'connectivity_providers.g.dart';
/// Network connection type
enum NetworkConnectionType {
wifi,
mobile,
ethernet,
bluetooth,
vpn,
other,
none,
}
/// Network status data class
class NetworkStatus {
final bool isConnected;
final NetworkConnectionType connectionType;
final DateTime lastUpdated;
final String? errorMessage;
const NetworkStatus({
required this.isConnected,
required this.connectionType,
required this.lastUpdated,
this.errorMessage,
});
NetworkStatus copyWith({
bool? isConnected,
NetworkConnectionType? connectionType,
DateTime? lastUpdated,
String? errorMessage,
}) {
return NetworkStatus(
isConnected: isConnected ?? this.isConnected,
connectionType: connectionType ?? this.connectionType,
lastUpdated: lastUpdated ?? this.lastUpdated,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
String toString() {
return 'NetworkStatus{isConnected: $isConnected, connectionType: $connectionType, lastUpdated: $lastUpdated}';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is NetworkStatus &&
other.isConnected == isConnected &&
other.connectionType == connectionType;
}
@override
int get hashCode {
return isConnected.hashCode ^ connectionType.hashCode;
}
}
/// Convert ConnectivityResult to NetworkConnectionType
NetworkConnectionType _getConnectionType(List<ConnectivityResult> results) {
if (results.isEmpty || results.contains(ConnectivityResult.none)) {
return NetworkConnectionType.none;
}
if (results.contains(ConnectivityResult.wifi)) {
return NetworkConnectionType.wifi;
}
if (results.contains(ConnectivityResult.mobile)) {
return NetworkConnectionType.mobile;
}
if (results.contains(ConnectivityResult.ethernet)) {
return NetworkConnectionType.ethernet;
}
if (results.contains(ConnectivityResult.bluetooth)) {
return NetworkConnectionType.bluetooth;
}
if (results.contains(ConnectivityResult.vpn)) {
return NetworkConnectionType.vpn;
}
if (results.contains(ConnectivityResult.other)) {
return NetworkConnectionType.other;
}
return NetworkConnectionType.none;
}
/// Connectivity instance provider
@riverpod
Connectivity connectivity(ConnectivityRef ref) {
return Connectivity();
}
/// Network connectivity stream provider
@riverpod
Stream<NetworkStatus> networkConnectivityStream(NetworkConnectivityStreamRef ref) {
final connectivity = ref.watch(connectivityProvider);
return connectivity.onConnectivityChanged.map((results) {
final connectionType = _getConnectionType(results);
final isConnected = connectionType != NetworkConnectionType.none;
return NetworkStatus(
isConnected: isConnected,
connectionType: connectionType,
lastUpdated: DateTime.now(),
);
}).handleError((error) {
debugPrint('❌ Connectivity stream error: $error');
return NetworkStatus(
isConnected: false,
connectionType: NetworkConnectionType.none,
lastUpdated: DateTime.now(),
errorMessage: error.toString(),
);
});
}
/// Current network status provider
@riverpod
class NetworkStatusNotifier extends _$NetworkStatusNotifier {
@override
NetworkStatus build() {
// Start listening to connectivity changes
final streamValue = ref.watch(networkConnectivityStreamProvider);
streamValue.whenData((status) {
state = status;
_logConnectionChange(status);
});
// Get initial connectivity state
_checkInitialConnectivity();
// Return initial state
return NetworkStatus(
isConnected: false,
connectionType: NetworkConnectionType.none,
lastUpdated: DateTime.now(),
);
}
Future<void> _checkInitialConnectivity() async {
try {
final connectivity = ref.read(connectivityProvider);
final result = await connectivity.checkConnectivity();
final connectionType = _getConnectionType(result);
final isConnected = connectionType != NetworkConnectionType.none;
state = NetworkStatus(
isConnected: isConnected,
connectionType: connectionType,
lastUpdated: DateTime.now(),
);
debugPrint('📡 Initial connectivity: ${isConnected ? '✅ Connected' : '❌ Disconnected'} ($connectionType)');
} catch (error) {
debugPrint('❌ Error checking initial connectivity: $error');
state = NetworkStatus(
isConnected: false,
connectionType: NetworkConnectionType.none,
lastUpdated: DateTime.now(),
errorMessage: error.toString(),
);
}
}
void _logConnectionChange(NetworkStatus status) {
final icon = status.isConnected ? '📶' : '📵';
final statusText = status.isConnected ? 'Connected' : 'Disconnected';
debugPrint('$icon Network status changed: $statusText (${status.connectionType})');
}
/// Force refresh network status
Future<void> refresh() async {
await _checkInitialConnectivity();
}
/// Get connection strength (mock implementation)
double getConnectionStrength() {
switch (state.connectionType) {
case NetworkConnectionType.wifi:
return 1.0; // Assume strong Wi-Fi
case NetworkConnectionType.ethernet:
return 1.0; // Assume strong ethernet
case NetworkConnectionType.mobile:
return 0.7; // Assume moderate mobile
case NetworkConnectionType.bluetooth:
return 0.5; // Assume weak bluetooth
case NetworkConnectionType.vpn:
return 0.8; // Assume good VPN
case NetworkConnectionType.other:
return 0.6; // Assume moderate other
case NetworkConnectionType.none:
return 0.0; // No connection
}
}
}
/// Simple connectivity status provider
@riverpod
bool isConnected(IsConnectedRef ref) {
final networkStatus = ref.watch(networkStatusNotifierProvider);
return networkStatus.isConnected;
}
/// Connection type provider
@riverpod
NetworkConnectionType connectionType(ConnectionTypeRef ref) {
final networkStatus = ref.watch(networkStatusNotifierProvider);
return networkStatus.connectionType;
}
/// Is Wi-Fi connected provider
@riverpod
bool isWifiConnected(IsWifiConnectedRef ref) {
final networkStatus = ref.watch(networkStatusNotifierProvider);
return networkStatus.isConnected && networkStatus.connectionType == NetworkConnectionType.wifi;
}
/// Is mobile data connected provider
@riverpod
bool isMobileConnected(IsMobileConnectedRef ref) {
final networkStatus = ref.watch(networkStatusNotifierProvider);
return networkStatus.isConnected && networkStatus.connectionType == NetworkConnectionType.mobile;
}
/// Network quality indicator provider
@riverpod
String networkQuality(NetworkQualityRef ref) {
final networkStatus = ref.watch(networkStatusNotifierProvider);
if (!networkStatus.isConnected) {
return 'No Connection';
}
switch (networkStatus.connectionType) {
case NetworkConnectionType.wifi:
return 'Excellent';
case NetworkConnectionType.ethernet:
return 'Excellent';
case NetworkConnectionType.mobile:
return 'Good';
case NetworkConnectionType.vpn:
return 'Good';
case NetworkConnectionType.other:
return 'Fair';
case NetworkConnectionType.bluetooth:
return 'Poor';
case NetworkConnectionType.none:
return 'No Connection';
}
}
/// Network history provider for tracking connection changes
@riverpod
class NetworkHistoryNotifier extends _$NetworkHistoryNotifier {
static const int _maxHistorySize = 50;
@override
List<NetworkStatus> build() {
// Listen to network status changes and add to history
ref.listen(networkStatusNotifierProvider, (previous, next) {
if (previous != next) {
_addToHistory(next);
}
});
return [];
}
void _addToHistory(NetworkStatus status) {
final newHistory = [...state, status];
// Keep only the last _maxHistorySize entries
if (newHistory.length > _maxHistorySize) {
newHistory.removeRange(0, newHistory.length - _maxHistorySize);
}
state = newHistory;
}
/// Get recent connection changes
List<NetworkStatus> getRecentChanges({int count = 10}) {
return state.reversed.take(count).toList();
}
/// Get connection uptime percentage
double getUptimePercentage({Duration? period}) {
if (state.isEmpty) return 0.0;
final now = DateTime.now();
final startTime = period != null ? now.subtract(period) : state.first.lastUpdated;
final relevantEntries = state.where((status) => status.lastUpdated.isAfter(startTime)).toList();
if (relevantEntries.isEmpty) return 0.0;
final connectedCount = relevantEntries.where((status) => status.isConnected).length;
return connectedCount / relevantEntries.length;
}
/// Clear history
void clearHistory() {
state = [];
}
/// Get connection statistics
Map<String, dynamic> getConnectionStats() {
if (state.isEmpty) {
return {
'totalChanges': 0,
'uptimePercentage': 0.0,
'mostCommonConnection': 'Unknown',
'connectionTypes': <String, int>{},
};
}
final connectionTypeCounts = <NetworkConnectionType, int>{};
for (final status in state) {
connectionTypeCounts[status.connectionType] = (connectionTypeCounts[status.connectionType] ?? 0) + 1;
}
final mostCommonType = connectionTypeCounts.entries.reduce((a, b) => a.value > b.value ? a : b).key;
return {
'totalChanges': state.length,
'uptimePercentage': getUptimePercentage(),
'mostCommonConnection': mostCommonType.toString().split('.').last,
'connectionTypes': connectionTypeCounts.map((key, value) => MapEntry(key.toString().split('.').last, value)),
};
}
}

View File

@@ -0,0 +1,169 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'connectivity_providers.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$connectivityHash() => r'da8080dfc40288eff97ff9cb96e9d9577714a9a0';
/// Connectivity instance provider
///
/// Copied from [connectivity].
@ProviderFor(connectivity)
final connectivityProvider = AutoDisposeProvider<Connectivity>.internal(
connectivity,
name: r'connectivityProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$connectivityHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef ConnectivityRef = AutoDisposeProviderRef<Connectivity>;
String _$networkConnectivityStreamHash() =>
r'0850402a3f1ed68481cfa9b8a3a371c804c358f3';
/// Network connectivity stream provider
///
/// Copied from [networkConnectivityStream].
@ProviderFor(networkConnectivityStream)
final networkConnectivityStreamProvider =
AutoDisposeStreamProvider<NetworkStatus>.internal(
networkConnectivityStream,
name: r'networkConnectivityStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$networkConnectivityStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef NetworkConnectivityStreamRef
= AutoDisposeStreamProviderRef<NetworkStatus>;
String _$isConnectedHash() => r'89efbfc9ecb21e2ff1a7f6eea736457e35bed181';
/// Simple connectivity status provider
///
/// Copied from [isConnected].
@ProviderFor(isConnected)
final isConnectedProvider = AutoDisposeProvider<bool>.internal(
isConnected,
name: r'isConnectedProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$isConnectedHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef IsConnectedRef = AutoDisposeProviderRef<bool>;
String _$connectionTypeHash() => r'fd1d65f0ae9afe2b04b358755ed4347e27a0515f';
/// Connection type provider
///
/// Copied from [connectionType].
@ProviderFor(connectionType)
final connectionTypeProvider =
AutoDisposeProvider<NetworkConnectionType>.internal(
connectionType,
name: r'connectionTypeProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$connectionTypeHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef ConnectionTypeRef = AutoDisposeProviderRef<NetworkConnectionType>;
String _$isWifiConnectedHash() => r'6ab4a8f83d5073544d77620bea093f4b34d61e05';
/// Is Wi-Fi connected provider
///
/// Copied from [isWifiConnected].
@ProviderFor(isWifiConnected)
final isWifiConnectedProvider = AutoDisposeProvider<bool>.internal(
isWifiConnected,
name: r'isWifiConnectedProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$isWifiConnectedHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef IsWifiConnectedRef = AutoDisposeProviderRef<bool>;
String _$isMobileConnectedHash() => r'1e03a490b5a59ac598fe75b45c42b353cec26129';
/// Is mobile data connected provider
///
/// Copied from [isMobileConnected].
@ProviderFor(isMobileConnected)
final isMobileConnectedProvider = AutoDisposeProvider<bool>.internal(
isMobileConnected,
name: r'isMobileConnectedProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$isMobileConnectedHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef IsMobileConnectedRef = AutoDisposeProviderRef<bool>;
String _$networkQualityHash() => r'b72cb19e0b8537514827d11fbe2f46bba4e94ac2';
/// Network quality indicator provider
///
/// Copied from [networkQuality].
@ProviderFor(networkQuality)
final networkQualityProvider = AutoDisposeProvider<String>.internal(
networkQuality,
name: r'networkQualityProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$networkQualityHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef NetworkQualityRef = AutoDisposeProviderRef<String>;
String _$networkStatusNotifierHash() =>
r'adebb286dce36d8cb54504f04a67dd4c00dceade';
/// Current network status provider
///
/// Copied from [NetworkStatusNotifier].
@ProviderFor(NetworkStatusNotifier)
final networkStatusNotifierProvider =
AutoDisposeNotifierProvider<NetworkStatusNotifier, NetworkStatus>.internal(
NetworkStatusNotifier.new,
name: r'networkStatusNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$networkStatusNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$NetworkStatusNotifier = AutoDisposeNotifier<NetworkStatus>;
String _$networkHistoryNotifierHash() =>
r'6498139c6e6e8472c81cb3f1789bcabfc4779943';
/// Network history provider for tracking connection changes
///
/// Copied from [NetworkHistoryNotifier].
@ProviderFor(NetworkHistoryNotifier)
final networkHistoryNotifierProvider = AutoDisposeNotifierProvider<
NetworkHistoryNotifier, List<NetworkStatus>>.internal(
NetworkHistoryNotifier.new,
name: r'networkHistoryNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$networkHistoryNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$NetworkHistoryNotifier = AutoDisposeNotifier<List<NetworkStatus>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member