diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a709117 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": ["master"] +} diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..bf8d421 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,6 +7,15 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** include: package:flutter_lints/flutter.yaml linter: diff --git a/lib/main.dart b/lib/main.dart index 8e4956d..918600f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; + import 'providers/telegram_proxy_provider.dart'; import 'providers/v2ray_provider.dart'; import 'providers/language_provider.dart'; @@ -10,6 +11,7 @@ import 'screens/main_navigation_screen.dart'; import 'screens/privacy_welcome_screen.dart'; import 'services/update_service.dart'; import 'theme/app_theme.dart'; + import 'dart:async'; void main() async { @@ -129,4 +131,4 @@ class _MyAppState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/providers/language_provider.dart b/lib/providers/language_provider.dart index 120339e..a5b721c 100644 --- a/lib/providers/language_provider.dart +++ b/lib/providers/language_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import '../models/app_language.dart'; import '../services/language_service.dart'; diff --git a/lib/providers/telegram_proxy_provider.dart b/lib/providers/telegram_proxy_provider.dart index 706b54b..8a3f971 100644 --- a/lib/providers/telegram_proxy_provider.dart +++ b/lib/providers/telegram_proxy_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; + import '../models/telegram_proxy.dart'; import '../services/telegram_proxy_service.dart'; diff --git a/lib/providers/v2ray_provider.dart b/lib/providers/v2ray_provider.dart index 6de3233..795e496 100644 --- a/lib/providers/v2ray_provider.dart +++ b/lib/providers/v2ray_provider.dart @@ -1,7 +1,10 @@ +// ignore_for_file: unused_element, avoid_print, unused_local_variable + import 'package:flutter/widgets.dart'; import 'package:flutter_v2ray_client/flutter_v2ray.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; + import '../models/v2ray_config.dart'; import '../models/subscription.dart'; import '../services/v2ray_service.dart'; @@ -20,7 +23,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { bool _isLoadingServers = false; bool _isProxyMode = false; bool _isInitializing = true; - bool _isUpdatingSubscriptions = false; // Track when updates are in progress + bool _isUpdatingSubscriptions = false; // Track when updates are in progress // Method channel for VPN control static const platform = MethodChannel('com.cloud.pira/vpn_control'); @@ -36,7 +39,8 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { String get errorMessage => _errorMessage; V2RayService get v2rayService => _v2rayService; bool get isProxyMode => _isProxyMode; - bool get isUpdatingSubscriptions => _isUpdatingSubscriptions; // Getter for update state + bool get isUpdatingSubscriptions => + _isUpdatingSubscriptions; // Getter for update state // Expose V2Ray status for real-time traffic monitoring V2RayStatus? get currentStatus => _v2rayService.currentStatus; @@ -101,7 +105,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { // Load subscriptions await loadSubscriptions(); - debugPrint('Loaded ${_subscriptions.length} subscriptions during initialization'); + debugPrint( + 'Loaded ${_subscriptions.length} subscriptions during initialization', + ); // Load proxy mode setting from SharedPreferences final prefs = await SharedPreferences.getInstance(); @@ -330,15 +336,15 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { _setLoading(true); try { _subscriptions = await _v2rayService.loadSubscriptions(); - + // Debug information debugPrint('Loaded ${_subscriptions.length} subscriptions'); for (var sub in _subscriptions) { - debugPrint(' Subscription: ${sub.name} with ${sub.configIds.length} configs'); + debugPrint( + ' Subscription: ${sub.name} with ${sub.configIds.length} configs', + ); } - - // Ensure configs are loaded and match subscription config IDs if (_configs.isEmpty) { _configs = await _v2rayService.loadConfigs(); @@ -350,8 +356,10 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { for (var subscription in _subscriptions) { final configIds = subscription.configIds; final existingConfigIds = _configs.map((c) => c.id).toSet(); - - debugPrint('Subscription "${subscription.name}" has ${configIds.length} config IDs, ${existingConfigIds.length} existing configs'); + + debugPrint( + 'Subscription "${subscription.name}" has ${configIds.length} config IDs, ${existingConfigIds.length} existing configs', + ); // Check if any config IDs in the subscription are missing from the configs list final missingConfigIds = configIds @@ -373,7 +381,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { _subscriptions[index] = subscription.copyWith( configIds: updatedConfigIds, ); - debugPrint('Updated subscription "${subscription.name}" to have ${updatedConfigIds.length} config IDs'); + debugPrint( + 'Updated subscription "${subscription.name}" to have ${updatedConfigIds.length} config IDs', + ); } } } @@ -389,7 +399,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { Future addConfig(V2RayConfig config) async { // Add config and display it immediately _configs.add(config); - debugPrint('Added config: ${config.remark} (${config.id}) - Total configs: ${_configs.length}'); + debugPrint( + 'Added config: ${config.remark} (${config.id}) - Total configs: ${_configs.length}', + ); // Save the configuration immediately to display it await _v2rayService.saveConfigs(_configs); @@ -493,9 +505,11 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { _configs.addAll(configs); final newConfigIds = configs.map((c) => c.id).toList(); - + // Debug information - debugPrint('Adding subscription "$name" with ${configs.length} configs and IDs: $newConfigIds'); + debugPrint( + 'Adding subscription "$name" with ${configs.length} configs and IDs: $newConfigIds', + ); // Create subscription final subscription = Subscription( @@ -511,7 +525,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { // Save both configs and subscription await _v2rayService.saveConfigs(_configs); await _v2rayService.saveSubscriptions(_subscriptions); - + // Debug information debugPrint('Subscription "$name" added successfully'); @@ -655,7 +669,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { _setLoading(true); _errorMessage = ''; _isLoadingServers = true; - _isUpdatingSubscriptions = true; // Set update state + _isUpdatingSubscriptions = true; // Set update state notifyListeners(); // Clear all ping cache before updating subscriptions @@ -735,8 +749,8 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { } finally { _setLoading(false); _isLoadingServers = false; - _isUpdatingSubscriptions = false; // Clear update state - notifyListeners(); // Notify listeners that update state has changed + _isUpdatingSubscriptions = false; // Clear update state + notifyListeners(); // Notify listeners that update state has changed } } @@ -752,7 +766,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { notifyListeners(); } - Future connectToServer(V2RayConfig config, bool _isProxyMode) async { + Future connectToServer(V2RayConfig config, bool isProxyMode) async { _isConnecting = true; _errorMessage = ''; notifyListeners(); @@ -789,7 +803,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { // Connect to server with timeout success = await _v2rayService - .connect(config, _isProxyMode) + .connect(config, isProxyMode) .timeout( const Duration(seconds: 30), // Timeout for connection onTimeout: () { @@ -851,14 +865,13 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { for (int i = 0; i < _configs.length; i++) { if (_configs[i].id == config.id) { _configs[i].isConnected = true; - _configs[i].isProxyMode = - _isProxyMode; // Update proxy mode status + _configs[i].isProxyMode = isProxyMode; // Update proxy mode status } else { _configs[i].isConnected = false; } } _selectedConfig = config; - _isProxyMode = _isProxyMode; // Update provider's proxy mode state + isProxyMode = isProxyMode; // Update provider's proxy mode state // Persist the changes with error handling try { @@ -1116,8 +1129,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver { final subscription = Subscription( id: DateTime.now().millisecondsSinceEpoch.toString(), name: name, - url: - 'file://subscription', // Special indicator for file-based subscriptions + url: 'file://subscription', // Special indicator for file-based subscriptions lastUpdated: DateTime.now(), configIds: newConfigIds, ); diff --git a/lib/screens/about_screen.dart b/lib/screens/about_screen.dart index 4563c1e..2354e95 100644 --- a/lib/screens/about_screen.dart +++ b/lib/screens/about_screen.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; + import '../providers/language_provider.dart'; import '../utils/app_localizations.dart'; class AboutScreen extends StatelessWidget { - const AboutScreen({Key? key}) : super(key: key); + const AboutScreen({super.key}); Future _launchUrl(String url) async { final Uri uri = Uri.parse(url); @@ -104,9 +105,8 @@ class AboutScreen extends StatelessWidget { color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(12), border: Border.all( - color: Theme.of( - context, - ).colorScheme.outline.withValues(alpha: 0.2), + color: Theme.of(context).colorScheme.outline + .withValues(alpha: 0.2), ), ), child: Text( diff --git a/lib/screens/backup_restore_screen.dart b/lib/screens/backup_restore_screen.dart index 2a9058e..abf7dcf 100644 --- a/lib/screens/backup_restore_screen.dart +++ b/lib/screens/backup_restore_screen.dart @@ -1,10 +1,14 @@ +// ignore_for_file: unnecessary_null_comparison + import 'dart:convert'; import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:path_provider/path_provider.dart'; import 'package:file_picker/file_picker.dart'; + import '../providers/language_provider.dart'; import '../utils/app_localizations.dart'; import '../theme/app_theme.dart'; @@ -85,12 +89,12 @@ class _BackupRestoreScreenState extends State { }); try { - final result = await FilePicker.platform.pickFiles( + final result = await FilePicker.pickFiles( type: FileType.custom, allowedExtensions: ['json'], ); - if (result == null || result.files.isEmpty) { + if (result == null || result.isEmpty) { setState(() { _statusMessage = context.tr( TranslationKeys.backupRestoreNoFileSelected, @@ -99,7 +103,7 @@ class _BackupRestoreScreenState extends State { return; } - final file = File(result.files.single.path!); + final file = File(result.single.path!); final jsonString = await file.readAsString(); final data = jsonDecode(jsonString) as Map; diff --git a/lib/screens/battery_settings_screen.dart b/lib/screens/battery_settings_screen.dart index 6346860..5e257de 100644 --- a/lib/screens/battery_settings_screen.dart +++ b/lib/screens/battery_settings_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; + import '../theme/app_theme.dart'; import '../utils/app_localizations.dart'; import '../providers/language_provider.dart'; diff --git a/lib/screens/blocked_apps_screen.dart b/lib/screens/blocked_apps_screen.dart index 7b28169..b224e5b 100644 --- a/lib/screens/blocked_apps_screen.dart +++ b/lib/screens/blocked_apps_screen.dart @@ -1,13 +1,17 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:provider/provider.dart'; + import '../theme/app_theme.dart'; import '../services/v2ray_service.dart'; import '../utils/app_localizations.dart'; import '../providers/language_provider.dart'; + import 'package:flutter/foundation.dart'; + import 'dart:convert'; -import 'dart:typed_data'; class BlockedAppsScreen extends StatefulWidget { const BlockedAppsScreen({super.key}); diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 9686f85..8dbdca9 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,7 +1,10 @@ +// ignore_for_file: body_might_complete_normally_catch_error, use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:http/http.dart' as http; + import '../providers/v2ray_provider.dart'; import '../providers/language_provider.dart'; import '../utils/app_localizations.dart'; @@ -16,7 +19,7 @@ import '../utils/auto_select_util.dart'; import 'subscription_management_screen.dart'; class HomeScreen extends StatefulWidget { - const HomeScreen({Key? key}) : super(key: key); + const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); @@ -264,13 +267,17 @@ class _HomeScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(AppTheme.primaryGreen), + valueColor: AlwaysStoppedAnimation( + AppTheme.primaryGreen, + ), ), const SizedBox(height: 16), Text(context.tr('server_selection.testing_servers')), const SizedBox(height: 8), StreamBuilder( - stream: Stream.periodic(const Duration(milliseconds: 500), (count) { + stream: Stream.periodic(const Duration(milliseconds: 500), ( + count, + ) { final messages = [ 'Testing servers for fastest connection...', 'Analyzing server response times...', @@ -281,7 +288,8 @@ class _HomeScreenState extends State { }), builder: (context, snapshot) { return Text( - snapshot.data ?? 'Testing servers for fastest connection...', + snapshot.data ?? + 'Testing servers for fastest connection...', style: const TextStyle(fontSize: 12, color: Colors.grey), ); }, @@ -327,17 +335,15 @@ class _HomeScreenState extends State { result.selectedConfig!, provider.isProxyMode, ); - final success = provider.errorMessage.isEmpty; // Check if connection was successful + final success = + provider.errorMessage.isEmpty; // Check if connection was successful if (mounted) { if (success) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( - '${context.tr('server_selection.lowest_ping', parameters: { - 'server': result.selectedConfig!.remark, - 'ping': result.bestPing.toString(), - })} - Connected!', + '${context.tr('server_selection.lowest_ping', parameters: {'server': result.selectedConfig!.remark, 'ping': result.bestPing.toString()})} - Connected!', ), backgroundColor: AppTheme.connectedGreen, duration: const Duration(seconds: 3), @@ -347,10 +353,13 @@ class _HomeScreenState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( - context.tr('server_selection.connect_failed', parameters: { - 'server': result.selectedConfig!.remark, - 'error': 'Connection failed', - }), + context.tr( + 'server_selection.connect_failed', + parameters: { + 'server': result.selectedConfig!.remark, + 'error': 'Connection failed', + }, + ), ), backgroundColor: Colors.red, duration: const Duration(seconds: 3), @@ -377,7 +386,9 @@ class _HomeScreenState extends State { Navigator.of(context).pop(); // Close dialog ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('${context.tr('server_selection.error_updating')}: $e'), + content: Text( + '${context.tr('server_selection.error_updating')}: $e', + ), backgroundColor: Colors.red, duration: const Duration(seconds: 3), ), @@ -422,41 +433,48 @@ class _HomeScreenState extends State { builder: (context, provider, _) { return IconButton( icon: const Icon(Icons.refresh), - onPressed: provider.isUpdatingSubscriptions ? null : () async { - final v2rayProvider = Provider.of( - context, - listen: false, - ); - - // Show loading indicator - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr('home.updating_subscriptions'), - ), - ), - ); - - // Update all subscriptions instead of just fetching servers - await v2rayProvider.updateAllSubscriptions(); - v2rayProvider.fetchNotificationStatus(); - - // Show success message - if (v2rayProvider.errorMessage.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr('home.subscriptions_updated'), - ), - ), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(v2rayProvider.errorMessage)), - ); - v2rayProvider.clearError(); - } - }, + onPressed: provider.isUpdatingSubscriptions + ? null + : () async { + final v2rayProvider = + Provider.of( + context, + listen: false, + ); + + // Show loading indicator + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr('home.updating_subscriptions'), + ), + ), + ); + + // Update all subscriptions instead of just fetching servers + await v2rayProvider.updateAllSubscriptions(); + v2rayProvider.fetchNotificationStatus(); + + // Show success message + if (v2rayProvider.errorMessage.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr( + 'home.subscriptions_updated', + ), + ), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(v2rayProvider.errorMessage), + ), + ); + v2rayProvider.clearError(); + } + }, tooltip: context.tr(TranslationKeys.homeRefresh), ); }, @@ -585,12 +603,12 @@ class _HomeScreenState extends State { padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isGlassBackground - ? AppTheme.cardDark.withOpacity(0.7) + ? AppTheme.cardDark.withValues(alpha: 0.7) : AppTheme.cardDark, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Colors.black.withValues(alpha: 0.1), blurRadius: 10, offset: const Offset(0, 4), ), diff --git a/lib/screens/host_checker_screen.dart b/lib/screens/host_checker_screen.dart index f3bec30..a2153e9 100644 --- a/lib/screens/host_checker_screen.dart +++ b/lib/screens/host_checker_screen.dart @@ -1,11 +1,15 @@ +// ignore_for_file: unused_local_variable, use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; + import 'dart:async'; + import '../theme/app_theme.dart'; import '../utils/app_localizations.dart'; class HostCheckerScreen extends StatefulWidget { - const HostCheckerScreen({Key? key}) : super(key: key); + const HostCheckerScreen({super.key}); @override State createState() => _HostCheckerScreenState(); @@ -326,7 +330,7 @@ class _HostCheckerScreenState extends State { SizedBox( width: 120, child: DropdownButtonFormField( - value: _timeoutSeconds, + initialValue: _timeoutSeconds, decoration: InputDecoration( isDense: true, contentPadding: const EdgeInsets.symmetric( @@ -386,7 +390,7 @@ class _HostCheckerScreenState extends State { foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - disabledBackgroundColor: AppTheme.primaryBlue.withOpacity(0.5), + disabledBackgroundColor: AppTheme.primaryBlue.withValues(alpha: 0.5), ), child: _isLoading ? const SizedBox( @@ -506,8 +510,8 @@ class _HostCheckerScreenState extends State { padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isSuccess - ? Colors.green.withOpacity(0.1) - : Colors.red.withOpacity(0.1), + ? Colors.green.withValues(alpha: 0.1) + : Colors.red.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( diff --git a/lib/screens/ip_info_screen.dart b/lib/screens/ip_info_screen.dart index 0cd1006..f61792c 100644 --- a/lib/screens/ip_info_screen.dart +++ b/lib/screens/ip_info_screen.dart @@ -1,14 +1,18 @@ +// ignore_for_file: avoid_print + import 'package:flutter/material.dart'; + import 'dart:ui'; -import 'package:provider/provider.dart'; + import 'package:http/http.dart' as http; + import 'dart:convert'; -import '../providers/v2ray_provider.dart'; + import '../theme/app_theme.dart'; import '../utils/app_localizations.dart'; class IpInfoScreen extends StatefulWidget { - const IpInfoScreen({Key? key}) : super(key: key); + const IpInfoScreen({super.key}); @override State createState() => _IpInfoScreenState(); diff --git a/lib/screens/language_settings_screen.dart b/lib/screens/language_settings_screen.dart index 204d55e..31d9e75 100644 --- a/lib/screens/language_settings_screen.dart +++ b/lib/screens/language_settings_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:google_fonts/google_fonts.dart'; + import '../theme/app_theme.dart'; import '../models/app_language.dart'; import '../providers/language_provider.dart'; diff --git a/lib/screens/main_navigation_screen.dart b/lib/screens/main_navigation_screen.dart index 160ba5f..b0760c3 100644 --- a/lib/screens/main_navigation_screen.dart +++ b/lib/screens/main_navigation_screen.dart @@ -1,7 +1,9 @@ +// ignore_for_file: unused_element + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../providers/v2ray_provider.dart'; + import '../providers/language_provider.dart'; import '../utils/app_localizations.dart'; import '../theme/app_theme.dart'; @@ -12,7 +14,7 @@ import 'tools_screen.dart'; import 'store_screen.dart'; class MainNavigationScreen extends StatefulWidget { - const MainNavigationScreen({Key? key}) : super(key: key); + const MainNavigationScreen({super.key}); @override State createState() => _MainNavigationScreenState(); @@ -135,7 +137,7 @@ class _MainNavigationScreenState extends State { decoration: BoxDecoration( shape: BoxShape.circle, color: _currentIndex == 0 - ? AppTheme.primaryBlue.withOpacity(0.2) + ? AppTheme.primaryBlue.withValues(alpha: 0.2) : Colors.transparent, ), child: const Icon(Icons.vpn_key_rounded), @@ -148,7 +150,7 @@ class _MainNavigationScreenState extends State { decoration: BoxDecoration( shape: BoxShape.circle, color: _currentIndex == 1 - ? AppTheme.primaryBlue.withOpacity(0.2) + ? AppTheme.primaryBlue.withValues(alpha: 0.2) : Colors.transparent, ), child: const Icon(Icons.telegram_rounded), @@ -161,7 +163,7 @@ class _MainNavigationScreenState extends State { decoration: BoxDecoration( shape: BoxShape.circle, color: _currentIndex == 2 - ? AppTheme.primaryBlue.withOpacity(0.2) + ? AppTheme.primaryBlue.withValues(alpha: 0.2) : Colors.transparent, ), child: const Icon(Icons.storefront_rounded), @@ -174,7 +176,7 @@ class _MainNavigationScreenState extends State { decoration: BoxDecoration( shape: BoxShape.circle, color: _currentIndex == 3 - ? AppTheme.primaryBlue.withOpacity(0.2) + ? AppTheme.primaryBlue.withValues(alpha: 0.2) : Colors.transparent, ), child: const Icon(Icons.handyman_rounded), diff --git a/lib/screens/per_app_tunnel_screen.dart b/lib/screens/per_app_tunnel_screen.dart index eb173a5..b82aad9 100644 --- a/lib/screens/per_app_tunnel_screen.dart +++ b/lib/screens/per_app_tunnel_screen.dart @@ -1,11 +1,16 @@ +// ignore_for_file: unnecessary_underscores, use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; + import '../theme/app_theme.dart'; import '../services/v2ray_service.dart'; + import 'package:flutter/foundation.dart'; + import '../utils/app_localizations.dart'; + import 'dart:convert'; -import 'dart:typed_data'; class PerAppTunnelScreen extends StatefulWidget { const PerAppTunnelScreen({super.key}); diff --git a/lib/screens/privacy_welcome_screen.dart b/lib/screens/privacy_welcome_screen.dart index c258eab..bed69d5 100644 --- a/lib/screens/privacy_welcome_screen.dart +++ b/lib/screens/privacy_welcome_screen.dart @@ -1,9 +1,11 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:provider/provider.dart'; import 'package:google_fonts/google_fonts.dart'; + import '../theme/app_theme.dart'; import 'main_navigation_screen.dart'; import '../utils/app_localizations.dart'; @@ -12,7 +14,7 @@ import '../providers/language_provider.dart'; import '../widgets/error_snackbar.dart'; class PrivacyWelcomeScreen extends StatefulWidget { - const PrivacyWelcomeScreen({Key? key}) : super(key: key); + const PrivacyWelcomeScreen({super.key}); @override State createState() => _PrivacyWelcomeScreenState(); @@ -21,8 +23,7 @@ class PrivacyWelcomeScreen extends StatefulWidget { class _PrivacyWelcomeScreenState extends State { final PageController _pageController = PageController(); int _currentPage = 0; - final int _totalPages = - 7; // Reduced to remove background access page, now accommodating channels, sponsors, and Persian Gulf pages + final int _totalPages = 7; // Reduced to remove background access page, now accommodating channels, sponsors, and Persian Gulf pages bool _acceptedPrivacy = false; AppLanguage? _selectedLanguage; @@ -175,7 +176,7 @@ class _PrivacyWelcomeScreenState extends State { end: Alignment.bottomCenter, colors: [ AppTheme.primaryDark, - AppTheme.primaryDark.withOpacity(0.8), + AppTheme.primaryDark.withValues(alpha: 0.8), ], ), ), @@ -242,7 +243,7 @@ class _PrivacyWelcomeScreenState extends State { shape: BoxShape.circle, color: _currentPage == index ? AppTheme.primaryGreen - : Colors.grey.withOpacity(0.5), + : Colors.grey.withValues(alpha: 0.5), ), ), ), @@ -264,7 +265,7 @@ class _PrivacyWelcomeScreenState extends State { borderRadius: BorderRadius.circular(8), ), disabledBackgroundColor: AppTheme.primaryGreen - .withOpacity(0.3), + .withValues(alpha: 0.3), ), child: Text( _currentPage == _totalPages - 1 @@ -373,7 +374,7 @@ class _PrivacyWelcomeScreenState extends State { child: Container( decoration: BoxDecoration( color: isSelected - ? AppTheme.primaryGreen.withOpacity(0.3) + ? AppTheme.primaryGreen.withValues(alpha: 0.3) : AppTheme.cardDark, borderRadius: BorderRadius.circular(12), border: Border.all( diff --git a/lib/screens/server_selection_screen.dart b/lib/screens/server_selection_screen.dart index 2671118..c4c0807 100644 --- a/lib/screens/server_selection_screen.dart +++ b/lib/screens/server_selection_screen.dart @@ -1,5 +1,8 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:async'; import 'dart:convert'; + import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -22,12 +25,12 @@ class ServerSelectionScreen extends StatefulWidget { final Future Function(V2RayConfig) onConfigSelected; const ServerSelectionScreen({ - Key? key, + super.key, required this.configs, required this.selectedConfig, required this.isConnecting, required this.onConfigSelected, - }) : super(key: key); + }); @override State createState() => _ServerSelectionScreenState(); @@ -40,9 +43,6 @@ class _ServerSelectionScreenState extends State { final V2RayService _v2rayService = V2RayService(); final StreamController _autoConnectStatusStream = StreamController.broadcast(); - - // Add the missing _originalOrder field - List _originalOrder = []; /// Get ping batch size from shared preferences (increased default for faster testing) Future _getPingBatchSize() async { @@ -279,21 +279,6 @@ class _ServerSelectionScreenState extends State { super.dispose(); } - Map> _groupConfigsByHost( - List configs, - ) { - final Map> groupedConfigs = {}; - for (var config in configs) { - // Use config.id as the key to ensure each config is treated individually - final key = config.id; - if (!groupedConfigs.containsKey(key)) { - groupedConfigs[key] = []; - } - groupedConfigs[key]!.add(config); - } - return groupedConfigs; - } - Future _loadPingForConfig( V2RayConfig config, List relatedConfigs, @@ -359,29 +344,6 @@ class _ServerSelectionScreenState extends State { } } - Future _pingServer(V2RayConfig config) async { - try { - // Check if task was cancelled or widget unmounted - if (_cancelPingTasks[config.id] == true || !mounted) { - return -1; - } - - final delay = await _v2rayService - .getServerDelay(config) - .timeout( - const Duration(seconds: 8), // Reduced timeout for better UX - onTimeout: () { - debugPrint('Ping timeout for server ${config.remark}'); - return -1; // Return -1 on timeout - }, - ); - return delay ?? -1; // Handle null case by returning -1 - } catch (e) { - debugPrint('Error pinging server ${config.remark}: $e'); - return -1; // Return -1 on error - } - } - // Method to ping all servers in the current filter tab in batches of 5 Future _pingAllServersInBatches() async { if (_isPingingAllServers) return; @@ -464,7 +426,9 @@ class _ServerSelectionScreenState extends State { // Very small delay between batches to avoid overwhelming the system if (mounted && i + batchSize < configsToPing.length) { - await Future.delayed(const Duration(milliseconds: 50)); // Reduced delay + await Future.delayed( + const Duration(milliseconds: 50), + ); // Reduced delay } } @@ -659,109 +623,6 @@ class _ServerSelectionScreenState extends State { } } - // New helper method to ping a single server and return the result - Future _processBatchPingTask(V2RayConfig config) async { - // Early return if widget unmounted - if (!mounted || _cancelPingTasks[config.id] == true) { - return -1; - } - - try { - // Ping the server with timeout - int ping; - try { - final result = await _pingServer(config).timeout( - const Duration(seconds: 8), - onTimeout: () { - debugPrint('Ping task timeout for server ${config.remark}'); - return -1; // Return -1 on timeout - }, - ); - ping = result; - } catch (e) { - if (e.toString().contains('timeout')) { - debugPrint('Timeout in ping task for ${config.remark}: $e'); - } else { - debugPrint('Error pinging server in task ${config.remark}: $e'); - } - ping = -1; // Return -1 on error - } - - return ping; - } catch (e) { - debugPrint( - 'Unexpected error in _processBatchPingTask for ${config.remark}: $e', - ); - return -1; - } - } - - Future _pingAllConfigs() async { - setState(() { - _isPingingAllServers = true; - }); - - try { - // Clear existing pings when starting new test - setState(() { - _pings.clear(); - }); - - // Get the batch size from settings - final int batchSize = await _getPingBatchSize(); - debugPrint('Using ping batch size: $batchSize'); - - // Filter configs to only include non-connected configs - final configsToPing = widget.configs - .where((config) => config.id != widget.selectedConfig?.id) - .toList(); - - // Process configs in batches - for (int i = 0; i < configsToPing.length; i += batchSize) { - if (!mounted) break; - - final endIndex = (i + batchSize < configsToPing.length) - ? i + batchSize - : configsToPing.length; - final batch = configsToPing.sublist(i, endIndex); - - // Ping all configs in the batch in parallel - final futures = >[]; - for (final config in batch) { - if (!mounted) break; - futures.add(_loadPingForConfig(config, [config])); - } - - // Wait for all configs in the batch to complete - await Future.wait(futures); - - // Small delay between batches to avoid overwhelming the system - if (mounted && i + batchSize < configsToPing.length) { - await Future.delayed(const Duration(milliseconds: 100)); - } - } - - // Save all pings to storage after completing all batches - await _savePingsToStorage(); - } catch (e) { - debugPrint('Error in ping all operation: $e'); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Error testing all servers: $e'), - backgroundColor: Colors.red, - ), - ); - } - } finally { - if (mounted) { - setState(() { - _isPingingAllServers = false; - }); - } - } - } - void _cancelAllPingTasks() { _cancelPingTasks.updateAll((key, value) => true); } @@ -838,23 +699,25 @@ class _ServerSelectionScreenState extends State { final allSubscriptionConfigIds = subscriptions .expand((sub) => sub.configIds) .toSet(); - + // Debug print to see what's happening - debugPrint('Local tab filtering: Total configs: ${configs.length}, Subscription config IDs: ${allSubscriptionConfigIds.length}'); + debugPrint( + 'Local tab filtering: Total configs: ${configs.length}, Subscription config IDs: ${allSubscriptionConfigIds.length}', + ); debugPrint('Subscription config IDs: $allSubscriptionConfigIds'); - - filteredConfigs = configs - .where((config) { - final isLocal = !allSubscriptionConfigIds.contains(config.id); - if (!isLocal) { - debugPrint('Config ${config.remark} (${config.id}) is NOT local (belongs to subscription)'); - } else { - debugPrint('Config ${config.remark} (${config.id}) IS local'); - } - return isLocal; - }) - .toList(); - + + filteredConfigs = configs.where((config) { + final isLocal = !allSubscriptionConfigIds.contains(config.id); + if (!isLocal) { + debugPrint( + 'Config ${config.remark} (${config.id}) is NOT local (belongs to subscription)', + ); + } else { + debugPrint('Config ${config.remark} (${config.id}) IS local'); + } + return isLocal; + }).toList(); + debugPrint('Local tab showing ${filteredConfigs.length} configs'); } else { final subscription = subscriptions.firstWhere( @@ -870,13 +733,13 @@ class _ServerSelectionScreenState extends State { filteredConfigs = configs .where((config) => subscription.configIds.contains(config.id)) .toList(); - debugPrint('Subscription tab "${_selectedFilter}": showing ${filteredConfigs.length} configs'); + debugPrint( + 'Subscription tab "$_selectedFilter": showing ${filteredConfigs.length} configs', + ); } // Store original order when not sorting - if (!_sortByPing) { - _originalOrder = List.from(filteredConfigs); - } + if (!_sortByPing) {} // Sort configs by ping if enabled if (_sortByPing) { @@ -924,82 +787,89 @@ class _ServerSelectionScreenState extends State { builder: (context, provider, _) { return IconButton( icon: const Icon(Icons.refresh), - onPressed: provider.isUpdatingSubscriptions ? null : () async { - try { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr( - TranslationKeys.serverSelectionUpdatingServers, - ), - ), - duration: const Duration(seconds: 1), - ), - ); - - // Clear saved pings when updating subscriptions - await _clearSavedPings(); - _pings.clear(); - if (mounted) { - setState(() {}); - } - - if (_selectedFilter == 'All') { - await provider.updateAllSubscriptions(); - } else if (_selectedFilter != 'Default') { - final subscription = subscriptions.firstWhere( - (sub) => sub.name == _selectedFilter, - orElse: () => Subscription( - id: '', - name: '', - url: '', - lastUpdated: DateTime.now(), - configIds: [], - ), - ); - if (subscription.id.isNotEmpty) { - await provider.updateSubscription(subscription); - } - } - - setState(() {}); - // Ping all servers in current tab after refresh - await _pingAllServersInBatches(); - - if (provider.errorMessage.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(provider.errorMessage), - backgroundColor: Colors.red.shade700, - duration: const Duration(seconds: 3), - ), - ); - provider.clearError(); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr( - TranslationKeys.serverSelectionServersUpdated, + onPressed: provider.isUpdatingSubscriptions + ? null + : () async { + try { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr( + TranslationKeys + .serverSelectionUpdatingServers, + ), + ), + duration: const Duration(seconds: 1), ), - ), - ), - ); - } - } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr( - TranslationKeys.serverSelectionErrorUpdating, - parameters: {'error': e.toString()}, - ), - ), - ), - ); - } - }, - tooltip: context.tr(TranslationKeys.serverSelectionUpdateServers), + ); + + // Clear saved pings when updating subscriptions + await _clearSavedPings(); + _pings.clear(); + if (mounted) { + setState(() {}); + } + + if (_selectedFilter == 'All') { + await provider.updateAllSubscriptions(); + } else if (_selectedFilter != 'Default') { + final subscription = subscriptions.firstWhere( + (sub) => sub.name == _selectedFilter, + orElse: () => Subscription( + id: '', + name: '', + url: '', + lastUpdated: DateTime.now(), + configIds: [], + ), + ); + if (subscription.id.isNotEmpty) { + await provider.updateSubscription(subscription); + } + } + + setState(() {}); + // Ping all servers in current tab after refresh + await _pingAllServersInBatches(); + + if (provider.errorMessage.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(provider.errorMessage), + backgroundColor: Colors.red.shade700, + duration: const Duration(seconds: 3), + ), + ); + provider.clearError(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr( + TranslationKeys + .serverSelectionServersUpdated, + ), + ), + ), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr( + TranslationKeys + .serverSelectionErrorUpdating, + parameters: {'error': e.toString()}, + ), + ), + ), + ); + } + }, + tooltip: context.tr( + TranslationKeys.serverSelectionUpdateServers, + ), ); }, ), @@ -1050,11 +920,7 @@ class _ServerSelectionScreenState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.cloud_off, - size: 48, - color: Colors.grey, - ), + Icon(Icons.cloud_off, size: 48, color: Colors.grey), const SizedBox(height: 16), Text( context.tr( @@ -1072,12 +938,12 @@ class _ServerSelectionScreenState extends State { ), child: Text( context.tr( - TranslationKeys.serverSelectionImportFromClipboard, + TranslationKeys + .serverSelectionImportFromClipboard, ), style: const TextStyle(color: Colors.white), ), ), - ], ), ) @@ -1474,8 +1340,9 @@ class _ServerSelectionScreenState extends State { vertical: 2, ), decoration: BoxDecoration( - color: Colors.blueGrey - .withValues(alpha: 0.2), + color: Colors.blueGrey.withValues( + alpha: 0.2, + ), borderRadius: BorderRadius.circular(4), ), diff --git a/lib/screens/speedtest_screen.dart b/lib/screens/speedtest_screen.dart index 9f890ae..6d25b1d 100644 --- a/lib/screens/speedtest_screen.dart +++ b/lib/screens/speedtest_screen.dart @@ -1,10 +1,13 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; + import '../theme/app_theme.dart'; class SpeedtestScreen extends StatefulWidget { - const SpeedtestScreen({Key? key}) : super(key: key); + const SpeedtestScreen({super.key}); @override State createState() => _SpeedtestScreenState(); @@ -65,9 +68,8 @@ class _SpeedtestScreenState extends State { final Uri uri = Uri.parse(url); if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { if (context.mounted) { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text('Could not launch $url'))); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text('Could not launch $url'))); } } } diff --git a/lib/screens/store_screen.dart b/lib/screens/store_screen.dart index dc5fac0..8248413 100644 --- a/lib/screens/store_screen.dart +++ b/lib/screens/store_screen.dart @@ -1,16 +1,20 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; + import '../providers/v2ray_provider.dart'; import '../theme/app_theme.dart'; import '../widgets/error_snackbar.dart'; import '../utils/app_localizations.dart'; class StoreScreen extends StatefulWidget { - const StoreScreen({Key? key}) : super(key: key); + const StoreScreen({super.key}); @override State createState() => _StoreScreenState(); @@ -69,7 +73,7 @@ class _StoreScreenState extends State { } catch (e) { setState(() { _errorMessage = - context.tr(TranslationKeys.errorNetwork) + ': ${e.toString()}'; + '${context.tr(TranslationKeys.errorNetwork)}: ${e.toString()}'; _isLoading = false; }); } @@ -141,7 +145,7 @@ class _StoreScreenState extends State { } catch (e) { ErrorSnackbar.show( context, - context.tr(TranslationKeys.errorUnknown) + ': ${e.toString()}', + '${context.tr(TranslationKeys.errorUnknown)}: ${e.toString()}', ); } } @@ -189,7 +193,7 @@ class _StoreScreenState extends State { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: AppTheme.primaryBlue.withOpacity(0.2), + color: AppTheme.primaryBlue.withValues(alpha: 0.2), shape: BoxShape.circle, ), child: const Icon( @@ -222,15 +226,15 @@ class _StoreScreenState extends State { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - AppTheme.primaryBlue.withOpacity(0.3), - AppTheme.primaryBlue.withOpacity(0.1), + AppTheme.primaryBlue.withValues(alpha: 0.3), + AppTheme.primaryBlue.withValues(alpha: 0.1), ], begin: Alignment.centerLeft, end: Alignment.centerRight, ), borderRadius: BorderRadius.circular(16), border: Border.all( - color: AppTheme.primaryBlue.withOpacity(0.5), + color: AppTheme.primaryBlue.withValues(alpha: 0.5), width: 1, ), ), @@ -283,7 +287,7 @@ class _StoreScreenState extends State { decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( - color: Colors.grey.withOpacity(0.5), + color: Colors.grey.withValues(alpha: 0.5), width: 1, ), ), @@ -426,8 +430,8 @@ class _StoreScreenState extends State { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - AppTheme.cardDark.withOpacity(0.9), - AppTheme.cardDark.withOpacity(0.7), + AppTheme.cardDark.withValues(alpha: 0.9), + AppTheme.cardDark.withValues(alpha: 0.7), ], begin: Alignment.topLeft, end: Alignment.bottomRight, @@ -435,7 +439,7 @@ class _StoreScreenState extends State { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.3), + color: Colors.black.withValues(alpha: 0.3), blurRadius: 10, offset: const Offset(0, 4), ), @@ -450,10 +454,10 @@ class _StoreScreenState extends State { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppTheme.primaryBlue.withOpacity(0.1), + color: AppTheme.primaryBlue.withValues(alpha: 0.1), border: Border( bottom: BorderSide( - color: AppTheme.primaryBlue.withOpacity(0.2), + color: AppTheme.primaryBlue.withValues(alpha: 0.2), width: 1, ), ), @@ -580,7 +584,7 @@ class _StoreScreenState extends State { gradient: LinearGradient( colors: [ AppTheme.primaryBlue, - AppTheme.primaryBlue.withOpacity(0.8), + AppTheme.primaryBlue.withValues(alpha: 0.8), ], begin: Alignment.centerLeft, end: Alignment.centerRight, @@ -588,7 +592,7 @@ class _StoreScreenState extends State { borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: AppTheme.primaryBlue.withOpacity(0.3), + color: AppTheme.primaryBlue.withValues(alpha: 0.3), blurRadius: 8, offset: const Offset(0, 2), ), diff --git a/lib/screens/subscription_management_screen.dart b/lib/screens/subscription_management_screen.dart index e098d79..954a6f4 100644 --- a/lib/screens/subscription_management_screen.dart +++ b/lib/screens/subscription_management_screen.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unnecessary_null_comparison, use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:file_picker/file_picker.dart'; @@ -9,7 +11,7 @@ import '../widgets/error_snackbar.dart'; import '../utils/app_localizations.dart'; class SubscriptionManagementScreen extends StatefulWidget { - const SubscriptionManagementScreen({Key? key}) : super(key: key); + const SubscriptionManagementScreen({super.key}); @override State createState() => @@ -32,16 +34,16 @@ class _SubscriptionManagementScreenState Future _importFromFile() async { try { - final result = await FilePicker.platform.pickFiles( + final result = await FilePicker.pickFiles( type: FileType.custom, allowedExtensions: ['txt', 'json', 'yaml', 'yml'], ); - if (result == null || result.files.isEmpty) { + if (result == null || result.isEmpty) { return; } - final file = File(result.files.single.path!); + final file = File(result.single.path!); final content = await file.readAsString(); // Parse the content as subscription configs @@ -400,8 +402,6 @@ class _SubscriptionManagementScreenState } } - - Future _updateAllSubscriptions(BuildContext context) async { final provider = Provider.of(context, listen: false); @@ -464,7 +464,9 @@ class _SubscriptionManagementScreenState builder: (context, provider, _) { return IconButton( icon: const Icon(Icons.refresh_rounded), - onPressed: provider.isUpdatingSubscriptions ? null : () => _updateAllSubscriptions(context), + onPressed: provider.isUpdatingSubscriptions + ? null + : () => _updateAllSubscriptions(context), tooltip: context.tr( TranslationKeys.subscriptionManagementUpdateAll, ), @@ -671,14 +673,11 @@ class _SubscriptionManagementScreenState ), IconButton( - icon: Icon( - Icons.delete, - color: Colors.red, - ), + icon: Icon(Icons.delete, color: Colors.red), onPressed: () => _deleteSubscription( - context, - subscription, - ), + context, + subscription, + ), tooltip: context.tr( TranslationKeys .subscriptionManagementDelete, diff --git a/lib/screens/telegram_proxy_screen.dart b/lib/screens/telegram_proxy_screen.dart index 0154914..54e8e1d 100644 --- a/lib/screens/telegram_proxy_screen.dart +++ b/lib/screens/telegram_proxy_screen.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; @@ -11,7 +13,7 @@ import '../utils/app_localizations.dart'; import '../services/wallpaper_service.dart'; class TelegramProxyScreen extends StatefulWidget { - const TelegramProxyScreen({Key? key}) : super(key: key); + const TelegramProxyScreen({super.key}); @override State createState() => _TelegramProxyScreenState(); @@ -122,7 +124,7 @@ class _TelegramProxyScreenState extends State { return Card( margin: const EdgeInsets.only(bottom: 16), color: isGlassBackground - ? AppTheme.cardDark.withOpacity(0.7) + ? AppTheme.cardDark.withValues(alpha: 0.7) : AppTheme.cardDark, elevation: 4, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), @@ -151,7 +153,7 @@ class _TelegramProxyScreenState extends State { vertical: 6, ), decoration: BoxDecoration( - color: AppTheme.primaryBlue.withOpacity(0.2), + color: AppTheme.primaryBlue.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), border: Border.all(color: AppTheme.primaryBlue, width: 1), ), @@ -177,7 +179,7 @@ class _TelegramProxyScreenState extends State { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.1), + color: Colors.blue.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Row( @@ -203,7 +205,7 @@ class _TelegramProxyScreenState extends State { child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.amber.withOpacity(0.1), + color: Colors.amber.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Row( @@ -240,7 +242,7 @@ class _TelegramProxyScreenState extends State { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.grey.withOpacity(0.1), + color: Colors.grey.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Row( @@ -265,7 +267,7 @@ class _TelegramProxyScreenState extends State { borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: proxy.uptime / 100, - backgroundColor: Colors.grey.withOpacity(0.3), + backgroundColor: Colors.grey.withValues(alpha: 0.3), valueColor: AlwaysStoppedAnimation( proxy.uptime > 80 ? Colors.green diff --git a/lib/screens/tools_screen.dart b/lib/screens/tools_screen.dart index ced2ba8..c07829a 100644 --- a/lib/screens/tools_screen.dart +++ b/lib/screens/tools_screen.dart @@ -298,8 +298,9 @@ class _ToolsScreenState extends State { final backgroundColor = isExitButton ? Colors.red.withValues(alpha: 0.1) : AppTheme.primaryBlue.withValues(alpha: 0.1); - final borderColor = - isExitButton ? Colors.red.withValues(alpha: 0.3) : Colors.transparent; + final borderColor = isExitButton + ? Colors.red.withValues(alpha: 0.3) + : Colors.transparent; return Card( margin: const EdgeInsets.only(bottom: 16.0), @@ -307,10 +308,7 @@ class _ToolsScreenState extends State { elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), - side: BorderSide( - color: borderColor, - width: isExitButton ? 1 : 0, - ), + side: BorderSide(color: borderColor, width: isExitButton ? 1 : 0), ), child: InkWell( onTap: onTap, @@ -345,17 +343,15 @@ class _ToolsScreenState extends State { description, style: TextStyle( fontSize: 14, - color: isExitButton ? Colors.redAccent : Colors.grey[400], + color: isExitButton + ? Colors.redAccent + : Colors.grey[400], ), ), ], ), ), - Icon( - Icons.arrow_forward_ios, - color: iconColor, - size: 16, - ), + Icon(Icons.arrow_forward_ios, color: iconColor, size: 16), ], ), ), @@ -428,7 +424,9 @@ class _ToolsScreenState extends State { const Spacer(), ElevatedButton( onPressed: () => _launchUrl(update.url.trim()), - style: ElevatedButton.styleFrom(backgroundColor: AppTheme.primaryBlue), + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.primaryBlue, + ), child: Text(context.tr('tools.update_now')), ), ], diff --git a/lib/screens/vpn_settings_screen.dart b/lib/screens/vpn_settings_screen.dart index b7455b9..a90cf53 100644 --- a/lib/screens/vpn_settings_screen.dart +++ b/lib/screens/vpn_settings_screen.dart @@ -10,7 +10,7 @@ import '../providers/v2ray_provider.dart'; const String _pingBatchSizeKey = 'ping_batch_size'; class VpnSettingsScreen extends StatefulWidget { - const VpnSettingsScreen({Key? key}) : super(key: key); + const VpnSettingsScreen({super.key}); @override State createState() => _VpnSettingsScreenState(); @@ -51,8 +51,7 @@ class _VpnSettingsScreenState extends State { prefs.getBool('bypass_subnets_enabled') ?? false; final bool savedDnsEnabled = prefs.getBool('custom_dns_enabled') ?? false; final String savedDnsServers = - prefs.getString('custom_dns_servers') ?? - '8.8.8.8\n8.8.4.4'; + prefs.getString('custom_dns_servers') ?? '8.8.8.8\n8.8.4.4'; final int savedPingBatchSize = prefs.getInt(_pingBatchSizeKey) ?? 5; // Default to 5 final bool savedProxyModeEnabled = @@ -247,7 +246,7 @@ class _VpnSettingsScreenState extends State { }); }, // Updated deprecated activeThumbColor to activeColor - activeColor: AppTheme.primaryGreen, + activeThumbColor: AppTheme.primaryGreen, ), ], ), @@ -365,7 +364,7 @@ class _VpnSettingsScreenState extends State { // Update the provider's proxy mode state v2rayProvider.toggleProxyMode(value); }, - activeColor: AppTheme.primaryGreen, + activeThumbColor: AppTheme.primaryGreen, ), ], ), @@ -466,7 +465,7 @@ class _VpnSettingsScreenState extends State { }); }, // Updated deprecated activeThumbColor to activeColor - activeColor: AppTheme.primaryGreen, + activeThumbColor: AppTheme.primaryGreen, ), ], ), diff --git a/lib/screens/wallpaper_settings_screen.dart b/lib/screens/wallpaper_settings_screen.dart index 88aed63..4b9d64d 100644 --- a/lib/screens/wallpaper_settings_screen.dart +++ b/lib/screens/wallpaper_settings_screen.dart @@ -187,8 +187,8 @@ class _WallpaperSettingsScreenState extends State { color: _statusMessage!.contains('Error') || _statusMessage!.contains('failed') - ? Colors.red.withOpacity(0.1) - : AppTheme.primaryBlue.withOpacity(0.1), + ? Colors.red.withValues(alpha: 0.1) + : AppTheme.primaryBlue.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all( color: @@ -264,7 +264,7 @@ class _WallpaperSettingsScreenState extends State { _toggleGlassBackground(value); }); }, - activeColor: AppTheme.primaryBlue, + activeThumbColor: AppTheme.primaryBlue, ), ], ), @@ -552,8 +552,7 @@ class _WallpaperSettingsScreenState extends State { class FullScreenWallpaperViewer extends StatelessWidget { final String imagePath; - const FullScreenWallpaperViewer({Key? key, required this.imagePath}) - : super(key: key); + const FullScreenWallpaperViewer({super.key, required this.imagePath}); @override Widget build(BuildContext context) { diff --git a/lib/screens/wallpaper_store_screen.dart b/lib/screens/wallpaper_store_screen.dart index 9bfdc27..af247e1 100644 --- a/lib/screens/wallpaper_store_screen.dart +++ b/lib/screens/wallpaper_store_screen.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:io'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -11,7 +13,7 @@ import '../theme/app_theme.dart'; import '../utils/app_localizations.dart'; class WallpaperStoreScreen extends StatefulWidget { - const WallpaperStoreScreen({Key? key}) : super(key: key); + const WallpaperStoreScreen({super.key}); @override State createState() => _WallpaperStoreScreenState(); @@ -86,7 +88,7 @@ class _WallpaperStoreScreenState extends State { final String fileName = url.split('/').last; // Invoke the Android Download Manager - final String downloadId = await platform.invokeMethod('downloadFile', { + await platform.invokeMethod('downloadFile', { 'url': url, 'fileName': fileName, }); @@ -381,16 +383,13 @@ class _WallpaperItem extends StatelessWidget { class FullScreenImageViewer extends StatefulWidget { final String imageUrl; - const FullScreenImageViewer({Key? key, required this.imageUrl}) - : super(key: key); + const FullScreenImageViewer({super.key, required this.imageUrl}); @override State createState() => _FullScreenImageViewerState(); } class _FullScreenImageViewerState extends State { - late Future _downloadFuture; - @override Widget build(BuildContext context) { return Scaffold( @@ -448,7 +447,7 @@ class _FullScreenImageViewerState extends State { final String fileName = url.split('/').last; // Invoke the Android Download Manager - final String downloadId = await platform.invokeMethod('downloadFile', { + await platform.invokeMethod('downloadFile', { 'url': url, 'fileName': fileName, }); diff --git a/lib/services/language_service.dart b/lib/services/language_service.dart index 4c7a841..f78d7df 100644 --- a/lib/services/language_service.dart +++ b/lib/services/language_service.dart @@ -1,7 +1,9 @@ import 'dart:convert'; + import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:shared_preferences/shared_preferences.dart'; + import '../models/app_language.dart'; class LanguageService { diff --git a/lib/services/ping_service.dart b/lib/services/ping_service.dart index aef8bb0..60a9023 100644 --- a/lib/services/ping_service.dart +++ b/lib/services/ping_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; diff --git a/lib/services/server_service.dart b/lib/services/server_service.dart index 9d6886f..7c9dcff 100644 --- a/lib/services/server_service.dart +++ b/lib/services/server_service.dart @@ -1,18 +1,24 @@ import 'dart:convert'; + import 'package:http/http.dart' as http; + import '../models/v2ray_config.dart'; + import 'package:flutter/foundation.dart'; import 'package:flutter_v2ray_client/flutter_v2ray.dart'; class ServerService { - Future> fetchServers({required String customUrl}) async { try { final url = customUrl; - final response = await http.get(Uri.parse(url)).timeout( + final response = await http + .get(Uri.parse(url)) + .timeout( const Duration(seconds: 60), onTimeout: () { - throw Exception('Network timeout: Check your internet connection'); + throw Exception( + 'Network timeout: Check your internet connection', + ); }, ); @@ -149,4 +155,4 @@ class ServerService { return null; } } -} \ No newline at end of file +} diff --git a/lib/services/telegram_proxy_service.dart b/lib/services/telegram_proxy_service.dart index f80b598..55746fe 100644 --- a/lib/services/telegram_proxy_service.dart +++ b/lib/services/telegram_proxy_service.dart @@ -1,4 +1,5 @@ import 'package:http/http.dart' as http; + import '../models/telegram_proxy.dart'; class TelegramProxyService { @@ -14,10 +15,14 @@ class TelegramProxyService { Future> fetchProxies() async { try { - final response = await http.get(Uri.parse(proxyUrl)).timeout( + final response = await http + .get(Uri.parse(proxyUrl)) + .timeout( const Duration(seconds: 60), onTimeout: () { - throw Exception('Network timeout: Check your internet connection'); + throw Exception( + 'Network timeout: Check your internet connection', + ); }, ); diff --git a/lib/services/update_service.dart b/lib/services/update_service.dart index 799dd28..2718faa 100644 --- a/lib/services/update_service.dart +++ b/lib/services/update_service.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:url_launcher/url_launcher.dart'; import 'package:proxycloud/models/app_update.dart'; + import '../utils/app_localizations.dart'; class UpdateService { @@ -73,7 +74,7 @@ class UpdateService { } // Launch URL - Future _launchUrl(String url, [BuildContext? context]) async { + Future _launchUrl(String url) async { final Uri uri = Uri.parse(url); if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { debugPrint('Could not launch $url'); diff --git a/lib/services/v2ray_service.dart b/lib/services/v2ray_service.dart index 614fd7d..20ad112 100644 --- a/lib/services/v2ray_service.dart +++ b/lib/services/v2ray_service.dart @@ -1,6 +1,9 @@ +// ignore_for_file: avoid_print + import 'dart:convert'; import 'dart:async'; import 'dart:math'; + import 'package:flutter_v2ray_client/flutter_v2ray.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; @@ -173,10 +176,6 @@ class V2RayService extends ChangeNotifier { // No need to initialize native ping service since we're using V2Ray's built-in method } - Future _initializeNativePing() async { - // No need to initialize native ping service since we're using V2Ray's built-in method - } - void _handleStatusChange(V2RayStatus status) { // Handle disconnection from notification // Check for common disconnected status values using string matching @@ -1124,9 +1123,8 @@ class V2RayService extends ChangeNotifier { for (final config in configs) { futures.add( - _pingSingleServerFast( - config, - ).then((delay) => MapEntry(config.id, delay)), + _pingSingleServerFast(config) + .then((delay) => MapEntry(config.id, delay)), ); } diff --git a/lib/services/wallpaper_service.dart b/lib/services/wallpaper_service.dart index 5b48904..bfa8a7f 100644 --- a/lib/services/wallpaper_service.dart +++ b/lib/services/wallpaper_service.dart @@ -1,10 +1,10 @@ import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:path_provider/path_provider.dart'; import 'package:image_picker/image_picker.dart'; import 'package:http/http.dart' as http; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; class WallpaperService extends ChangeNotifier { @@ -101,7 +101,7 @@ class WallpaperService extends ChangeNotifier { } await _saveWallpaperSettings(null, false); - + // Clean up any remaining old wallpapers await cleanupAllOldWallpapers(); } @@ -192,7 +192,7 @@ class WallpaperService extends ChangeNotifier { try { // Clear cached network images to free up space await clearCachedImages(); - + final Directory appDir = await getApplicationDocumentsDirectory(); final String wallpapersDir = '${appDir.path}/wallpapers'; final Directory wallpaperDirectory = Directory(wallpapersDir); @@ -214,15 +214,19 @@ class WallpaperService extends ChangeNotifier { await entity.delete(recursive: true); } } - + // Also clean up any loose wallpaper files in the app directory (legacy) - final List appDirContents = await appDir.list().toList(); + final List appDirContents = await appDir + .list() + .toList(); for (final FileSystemEntity entity in appDirContents) { if (entity is File) { final String fileName = entity.path.split('/').last; - if (fileName.startsWith('wallpaper_') && - (fileName.endsWith('.jpg') || fileName.endsWith('.png') || - fileName.endsWith('.jpeg') || fileName.endsWith('.gif'))) { + if (fileName.startsWith('wallpaper_') && + (fileName.endsWith('.jpg') || + fileName.endsWith('.png') || + fileName.endsWith('.jpeg') || + fileName.endsWith('.gif'))) { // Only delete if it's not the current wallpaper if (_wallpaperPath == null || entity.path != _wallpaperPath) { await entity.delete(); @@ -242,7 +246,7 @@ class WallpaperService extends ChangeNotifier { try { // Clean up old wallpapers before setting new one await cleanupAllOldWallpapers(); - + final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { @@ -281,4 +285,4 @@ class WallpaperService extends ChangeNotifier { return false; } } -} \ No newline at end of file +} diff --git a/lib/utils/app_localizations.dart b/lib/utils/app_localizations.dart index c398b0c..12c5c05 100644 --- a/lib/utils/app_localizations.dart +++ b/lib/utils/app_localizations.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../providers/language_provider.dart'; /// Helper class for easy translation access throughout the app diff --git a/lib/utils/auto_select_util.dart b/lib/utils/auto_select_util.dart index eb2cd84..e9da407 100644 --- a/lib/utils/auto_select_util.dart +++ b/lib/utils/auto_select_util.dart @@ -1,4 +1,5 @@ import 'dart:math'; + import 'package:proxycloud/models/v2ray_config.dart'; import 'package:proxycloud/services/v2ray_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -54,7 +55,9 @@ class AutoSelectUtil { debugPrint('Using auto-select batch size: $batchSize'); // Notify about starting - onStatusUpdate?.call('Testing ${configs.length} servers in batches of $batchSize...'); + onStatusUpdate?.call( + 'Testing ${configs.length} servers in batches of $batchSize...', + ); // Variables to track the best server found so far V2RayConfig? selectedConfig; @@ -70,8 +73,11 @@ class AutoSelectUtil { // Determine how many servers to test in this batch final int serversToTest = min(batchSize, configs.length - testedOffset); - final int actualServersToTest = max(1, serversToTest); // Ensure at least 1 - + final int actualServersToTest = max( + 1, + serversToTest, + ); // Ensure at least 1 + // Get the configs for this batch final List batchConfigs = configs.sublist( testedOffset, @@ -79,7 +85,9 @@ class AutoSelectUtil { ); // Notify about current batch - onStatusUpdate?.call('Testing batch ${testedOffset ~/ batchSize + 1}: ${batchConfigs.length} servers...'); + onStatusUpdate?.call( + 'Testing batch ${testedOffset ~/ batchSize + 1}: ${batchConfigs.length} servers...', + ); // Ping all configs in the batch in parallel with optimized settings final futures = >>[]; @@ -88,7 +96,7 @@ class AutoSelectUtil { if (cancellationToken?.isCancelled == true) { return AutoSelectResult(errorMessage: 'Auto-select cancelled'); } - + futures.add( v2rayService .getServerDelay(config, cancellationToken: cancellationToken) @@ -97,16 +105,15 @@ class AutoSelectUtil { } // Wait for all configs in the batch to complete with shorter timeout - final List> results = await Future.wait( - futures, - ).timeout( - const Duration(seconds: 6), // Reduced timeout for faster response - onTimeout: () { - // Handle timeout case - debugPrint('Auto-select batch timeout'); - return []; // Return empty list on timeout - }, - ); + final List> results = + await Future.wait(futures).timeout( + const Duration(seconds: 6), // Reduced timeout for faster response + onTimeout: () { + // Handle timeout case + debugPrint('Auto-select batch timeout'); + return []; // Return empty list on timeout + }, + ); // Process results and find the best server in this batch for (final result in results) { @@ -121,15 +128,17 @@ class AutoSelectUtil { // Update status with current result if (delay != null && delay >= 0) { onStatusUpdate?.call('✓ ${config.remark}: ${delay}ms'); - + // Check if this is the best server so far if (delay < (bestPing ?? 10000)) { selectedConfig = config; bestPing = delay; - + // If we found a very fast server (< 100ms), we can stop early for maximum speed if (delay < 100) { - onStatusUpdate?.call('Found very fast server (${delay}ms), stopping early...'); + onStatusUpdate?.call( + 'Found very fast server (${delay}ms), stopping early...', + ); break; } } @@ -140,7 +149,9 @@ class AutoSelectUtil { // Early exit if we found a good server if (selectedConfig != null && (bestPing ?? 10000) < 200) { - onStatusUpdate?.call('Found good server (${bestPing}ms), stopping batch testing...'); + onStatusUpdate?.call( + 'Found good server (${bestPing}ms), stopping batch testing...', + ); break; } diff --git a/lib/widgets/background_gradient.dart b/lib/widgets/background_gradient.dart index da3d22f..52f50a9 100644 --- a/lib/widgets/background_gradient.dart +++ b/lib/widgets/background_gradient.dart @@ -1,13 +1,15 @@ import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../theme/app_theme.dart'; import '../services/wallpaper_service.dart'; class BackgroundGradient extends StatelessWidget { final Widget child; - const BackgroundGradient({Key? key, required this.child}) : super(key: key); + const BackgroundGradient({super.key, required this.child}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/connection_button.dart b/lib/widgets/connection_button.dart index 29ee185..9ac54b9 100644 --- a/lib/widgets/connection_button.dart +++ b/lib/widgets/connection_button.dart @@ -1,14 +1,18 @@ +// ignore_for_file: use_build_context_synchronously, unused_element + import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:provider/provider.dart'; + import 'dart:async'; + import '../providers/v2ray_provider.dart'; import '../theme/app_theme.dart'; import '../utils/auto_select_util.dart'; import '../utils/app_localizations.dart'; class ConnectionButton extends StatefulWidget { - const ConnectionButton({Key? key}) : super(key: key); + const ConnectionButton({super.key}); @override State createState() => _ConnectionButtonState(); @@ -168,7 +172,7 @@ class _ConnectionButtonState extends State { color: AppTheme.cardDark, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.3), + color: Colors.black.withValues(alpha: 0.3), blurRadius: 15, offset: const Offset(0, 5), ), @@ -350,7 +354,7 @@ class _ConnectionButtonState extends State { shape: BoxShape.circle, gradient: RadialGradient( colors: [ - Colors.white.withOpacity(0.2), + Colors.white.withValues(alpha: 0.2), Colors.transparent, ], ), @@ -413,7 +417,7 @@ class _ConnectionButtonState extends State { if (isConnecting) { return [ AppTheme.connectingBlue, - AppTheme.connectingBlue.withOpacity(0.7), + AppTheme.connectingBlue.withValues(alpha: 0.7), ]; } else if (isConnected) { return [ diff --git a/lib/widgets/connection_status.dart b/lib/widgets/connection_status.dart index ce80b96..501a88d 100644 --- a/lib/widgets/connection_status.dart +++ b/lib/widgets/connection_status.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_animate/flutter_animate.dart'; + import '../providers/v2ray_provider.dart'; import '../theme/app_theme.dart'; import '../services/wallpaper_service.dart'; class ConnectionStatus extends StatelessWidget { - const ConnectionStatus({Key? key}) : super(key: key); + const ConnectionStatus({super.key}); @override Widget build(BuildContext context) { @@ -88,8 +89,8 @@ class ConnectionStatus extends StatelessWidget { ), decoration: BoxDecoration( color: isGlassBackground - ? Colors.black.withOpacity(0.2) - : Colors.black.withOpacity(0.2), + ? Colors.black.withValues(alpha: 0.2) + : Colors.black.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), ), child: Row( @@ -153,7 +154,10 @@ class ConnectionStatus extends StatelessWidget { ]; } if (isConnected) { - return [AppTheme.connectedGreen, AppTheme.connectedGreen.withValues(alpha: 0.8)]; + return [ + AppTheme.connectedGreen, + AppTheme.connectedGreen.withValues(alpha: 0.8), + ]; } return [ AppTheme.disconnectedRed, @@ -172,4 +176,4 @@ class ConnectionStatus extends StatelessWidget { } return activeConfig != null ? 'Connected' : 'Disconnected'; } -} \ No newline at end of file +} diff --git a/lib/widgets/error_snackbar.dart b/lib/widgets/error_snackbar.dart index a2c07ec..1f8e7f3 100644 --- a/lib/widgets/error_snackbar.dart +++ b/lib/widgets/error_snackbar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import '../theme/app_theme.dart'; import '../utils/app_localizations.dart'; diff --git a/lib/widgets/server_bottom_sheet.dart b/lib/widgets/server_bottom_sheet.dart index ac17daa..d1bec66 100644 --- a/lib/widgets/server_bottom_sheet.dart +++ b/lib/widgets/server_bottom_sheet.dart @@ -1,5 +1,8 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../models/v2ray_config.dart'; import '../providers/v2ray_provider.dart'; import '../providers/language_provider.dart'; @@ -14,12 +17,12 @@ class ServerBottomSheet extends StatefulWidget { final Future Function(V2RayConfig) onConfigSelected; const ServerBottomSheet({ - Key? key, + super.key, required this.configs, required this.selectedConfig, required this.isConnecting, required this.onConfigSelected, - }) : super(key: key); + }); @override State createState() => _ServerBottomSheetState(); diff --git a/lib/widgets/server_list_item.dart b/lib/widgets/server_list_item.dart index 4b2b6ba..4206174 100644 --- a/lib/widgets/server_list_item.dart +++ b/lib/widgets/server_list_item.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../models/v2ray_config.dart'; import '../providers/v2ray_provider.dart'; import '../providers/language_provider.dart'; @@ -8,7 +9,7 @@ import '../utils/app_localizations.dart'; class ServerListItem extends StatefulWidget { final V2RayConfig config; - const ServerListItem({Key? key, required this.config}) : super(key: key); + const ServerListItem({super.key, required this.config}); @override State createState() => _ServerListItemState(); @@ -168,7 +169,7 @@ class _ServerListItemState extends State { vertical: 4, ), decoration: BoxDecoration( - color: Colors.blueGrey.withOpacity(0.2), + color: Colors.blueGrey.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4), ), child: Text( diff --git a/lib/widgets/server_selector.dart b/lib/widgets/server_selector.dart index b6e126b..b8b9b04 100644 --- a/lib/widgets/server_selector.dart +++ b/lib/widgets/server_selector.dart @@ -1,5 +1,8 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../models/v2ray_config.dart'; import '../providers/v2ray_provider.dart'; import '../providers/language_provider.dart'; @@ -7,10 +10,9 @@ import '../utils/app_localizations.dart'; import '../theme/app_theme.dart'; import '../screens/server_selection_screen.dart'; import '../services/wallpaper_service.dart'; -import 'error_snackbar.dart'; class ServerSelector extends StatelessWidget { - const ServerSelector({Key? key}) : super(key: key); + const ServerSelector({super.key}); @override Widget build(BuildContext context) { @@ -47,7 +49,7 @@ class ServerSelector extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), color: isGlassBackground - ? AppTheme.surfaceCard.withOpacity(0.7) + ? AppTheme.surfaceCard.withValues(alpha: 0.7) : AppTheme.cardDark, child: Padding( padding: const EdgeInsets.all(16), @@ -117,7 +119,7 @@ class ServerSelector extends StatelessWidget { ), decoration: BoxDecoration( color: isGlassBackground - ? AppTheme.surfaceContainer.withOpacity(0.7) + ? AppTheme.surfaceContainer.withValues(alpha: 0.7) : AppTheme.surfaceContainer, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppTheme.surfaceCard), @@ -198,7 +200,7 @@ class _LoadingServerCard extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), color: isGlassBackground - ? AppTheme.surfaceCard.withOpacity(0.7) + ? AppTheme.surfaceCard.withValues(alpha: 0.7) : AppTheme.cardDark, child: Padding( padding: const EdgeInsets.all(24), @@ -213,50 +215,54 @@ class _LoadingServerCard extends StatelessWidget { Consumer( builder: (context, provider, _) { return TextButton( - onPressed: provider.isUpdatingSubscriptions ? null : () async { - final v2rayProvider = Provider.of( - context, - listen: false, - ); - try { - // Show loading indicator - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr('home.updating_subscriptions'), - ), - ), - ); + onPressed: provider.isUpdatingSubscriptions + ? null + : () async { + final v2rayProvider = Provider.of( + context, + listen: false, + ); + try { + // Show loading indicator + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr('home.updating_subscriptions'), + ), + ), + ); - // Update all subscriptions instead of just fetching servers - await v2rayProvider.updateAllSubscriptions(); - v2rayProvider.fetchNotificationStatus(); + // Update all subscriptions instead of just fetching servers + await v2rayProvider.updateAllSubscriptions(); + v2rayProvider.fetchNotificationStatus(); - // Show success message - if (v2rayProvider.errorMessage.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.tr('home.subscriptions_updated'), - ), - ), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(v2rayProvider.errorMessage)), - ); - v2rayProvider.clearError(); - } - } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - '${context.tr(TranslationKeys.serverSelectorErrorRefreshing)}: ${e.toString()}', - ), - ), - ); - } - }, + // Show success message + if (v2rayProvider.errorMessage.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.tr('home.subscriptions_updated'), + ), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(v2rayProvider.errorMessage), + ), + ); + v2rayProvider.clearError(); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '${context.tr(TranslationKeys.serverSelectorErrorRefreshing)}: ${e.toString()}', + ), + ), + ); + } + }, child: Text(context.tr(TranslationKeys.commonRefresh)), ); }, @@ -280,7 +286,7 @@ class _EmptyServerCard extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), color: isGlassBackground - ? AppTheme.surfaceCard.withOpacity(0.7) + ? AppTheme.surfaceCard.withValues(alpha: 0.7) : AppTheme.cardDark, child: Padding( padding: const EdgeInsets.all(24), @@ -319,7 +325,9 @@ class _EmptyServerCard extends StatelessWidget { backgroundColor: AppTheme.primaryGreen, ), child: Text( - context.tr(TranslationKeys.serverSelectionImportFromClipboard), + context.tr( + TranslationKeys.serverSelectionImportFromClipboard, + ), style: const TextStyle(color: Colors.white), ), ), diff --git a/local_packages/flutter_v2ray_client/analysis_options.yaml b/local_packages/flutter_v2ray_client/analysis_options.yaml index 96be936..643adcb 100644 --- a/local_packages/flutter_v2ray_client/analysis_options.yaml +++ b/local_packages/flutter_v2ray_client/analysis_options.yaml @@ -1,12 +1,18 @@ include: package:flutter_lints/flutter.yaml analyzer: + errors: + avoid_print: ignore exclude: - build/** - android/** - ios/** - example/ios/Flutter/App.framework/** - example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java + - web/** + - windows/** + - macos/** + - linux/** linter: rules: @@ -44,7 +50,7 @@ linter: - prefer_const_declarations - prefer_constructors_over_static_methods - prefer_contains - - prefer_equal_for_default_values + # - prefer_equal_for_default_values - prefer_final_fields - prefer_final_in_for_each - prefer_final_locals diff --git a/local_packages/flutter_v2ray_client/lib/url/vless.dart b/local_packages/flutter_v2ray_client/lib/url/vless.dart index e80d5af..47b3c20 100644 --- a/local_packages/flutter_v2ray_client/lib/url/vless.dart +++ b/local_packages/flutter_v2ray_client/lib/url/vless.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_print + import 'dart:convert'; import 'package:flutter_v2ray_client/url/url.dart'; @@ -41,7 +43,7 @@ class VlessURL extends V2RayURL { shortId: uri.queryParameters['sid'] ?? '', spiderX: uri.queryParameters['spx'] ?? '', ); - + // Handle xhttp specific settings _populateXhttpSettings(); } @@ -67,7 +69,7 @@ class VlessURL extends V2RayURL { if (transport == 'xhttp') { final extraParam = uri.queryParameters['extra']; Map? extraSettings; - + if (extraParam != null) { try { // Decode and parse the extra parameter which contains JSON @@ -78,19 +80,19 @@ class VlessURL extends V2RayURL { print('Failed to parse xhttp extra settings: $e'); } } - + // Create xhttpSettings object final xhttpSettings = { 'host': uri.queryParameters['host'] ?? '', 'path': uri.queryParameters['path'] ?? '/', 'mode': uri.queryParameters['mode'] ?? 'auto', }; - + // Add extra settings if available if (extraSettings != null) { xhttpSettings['extra'] = extraSettings; } - + // Add xhttpSettings to streamSetting streamSetting['xhttpSettings'] = xhttpSettings; } @@ -138,4 +140,4 @@ class VlessURL extends V2RayURL { 'concurrency': 8, }, }; -} \ No newline at end of file +} diff --git a/local_packages/flutter_v2ray_client/pubspec.yaml b/local_packages/flutter_v2ray_client/pubspec.yaml index 66ba10a..e582ff5 100644 --- a/local_packages/flutter_v2ray_client/pubspec.yaml +++ b/local_packages/flutter_v2ray_client/pubspec.yaml @@ -22,28 +22,14 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.2 + plugin_platform_interface: any dev_dependencies: - flutter_lints: ^5.0.0 + flutter_lints: ^6.0.0 flutter_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) - # which should be registered in the plugin registry. This is required for - # using method channels. - # The Android 'package' specifies package in which the registered class is. - # This is required for using method channels on Android. - # The 'ffiPlugin' specifies that native code should be built and bundled. - # This is required for using `dart:ffi`. - # All these are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: android: @@ -52,22 +38,10 @@ flutter: ios: pluginClass: FlutterV2rayPlugin - # To add assets to your plugin package, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: # fonts: # - family: Schyler # fonts: @@ -79,6 +53,3 @@ flutter: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index d7cc0b9..d7db058 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,14 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + android_file_picker: + dependency: transitive + description: + name: android_file_picker + sha256: "24ccc79cdc9612614703dda2d6fd6c67e8b7b16529b54ab983578488ef2d721c" + url: "https://pub.dev" + source: hosted + version: "1.0.0" archive: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.9" args: dependency: transitive description: @@ -61,18 +69,18 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" clipboard: dependency: "direct main" description: name: clipboard - sha256: "2ec38f0e59878008ceca0ab122e4bfde98847f88ef0f83331362ba4521f565a9" + sha256: c0ba5e7214035b824549473fa134870bc544fff0d7b89170d4756c0ac2c0239d url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "3.0.14" clock: dependency: transitive description: @@ -93,10 +101,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51" url: "https://pub.dev" source: hosted - version: "0.3.4+2" + version: "0.3.5+4" crypto: dependency: transitive description: @@ -109,18 +117,18 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" dbus: dependency: transitive description: name: dbus - sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.14" easy_localization: dependency: "direct main" description: @@ -149,10 +157,18 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" + ffi_leak_tracker: + dependency: transitive + description: + name: ffi_leak_tracker + sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97" + url: "https://pub.dev" + source: hosted + version: "0.1.2" file: dependency: transitive description: @@ -165,10 +181,42 @@ packages: dependency: "direct main" description: name: file_picker - sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22 + sha256: afbaa8015d9efabd224f41084ed9fdeddfa65389ebd7cd3a9eb1476aca66b46d url: "https://pub.dev" source: hosted - version: "10.3.2" + version: "12.0.0" + file_picker_darwin: + dependency: transitive + description: + name: file_picker_darwin + sha256: "27be6ab5d48771f036b273c4424d83919226d7ceda04c7389cf8d632d9332303" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + file_picker_linux: + dependency: transitive + description: + name: file_picker_linux + sha256: "5f8e1de3d032b6d0b2e840d30481abe41ead3ab28c9753a0187f3465d6797bf1" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + file_picker_platform_interface: + dependency: transitive + description: + name: file_picker_platform_interface + sha256: bfa1bfc65e92d521063ebebe6ffea4e7b0c0be87bbae52d9e69aab08cd2f94f4 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + file_picker_web: + dependency: transitive + description: + name: file_picker_web + sha256: "3c0d736648b92d488b4ce8de4c88a35998c98287eebd50e9cb3a60bc8a953d96" + url: "https://pub.dev" + source: hosted + version: "3.0.0" file_selector_linux: dependency: transitive description: @@ -226,18 +274,18 @@ packages: dependency: "direct main" description: name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + sha256: "1de7849213b4c73c85aca7e0ac687a9a5d82ccdb594366b9dcc26cb6a2189cd2" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.4.2" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -271,10 +319,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: cd57f7969b4679317c17af6fd16ee233c1e60a82ed209d8a475c54fd6fd6f845 + sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" flutter_test: dependency: "direct dev" description: flutter @@ -296,18 +344,18 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: df9763500dadba0155373e9cb44e202ce21bd9ed5de6bdbd05c5854e86839cb8 + sha256: e3cb3ee6b47fd2472c23de6da5744796a4da195137759ddb3fbcc9467b7b3c7d url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "8.2.1" http: dependency: "direct main" description: name: http - sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" http_parser: dependency: transitive description: @@ -320,10 +368,10 @@ packages: dependency: "direct main" description: name: image_picker - sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041" + sha256: d8402284df184bc05f4a2210c6c23983b0720f4cd87cbd05c5390a78af602667 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.3" image_picker_android: dependency: transitive description: @@ -384,10 +432,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" leak_tracker: dependency: transitive description: @@ -416,42 +464,42 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "6.1.0" lottie: dependency: "direct main" description: name: lottie - sha256: a93542cc2d60a7057255405f62252533f8e8956e7e06754955669fd32fb4b216 + sha256: "58dda9eee3f1b1fb9d490b0161635cffe93022b3b8ca0687e3df33a53620ead2" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "3.5.1" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.20" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.19.0" mime: dependency: transitive description: @@ -496,10 +544,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.6" path_provider_android: dependency: transitive description: @@ -544,58 +592,58 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 + sha256: e7317eb2eb611d1bf0386b6b974be9c50449f975f19a90a7b8ea013550302b30 url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "13.0.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + sha256: d7676c6fcf2f0b92537ec41476a6ead45a00b0d8bbb852395a6f9f33f49d6242 url: "https://pub.dev" source: hosted - version: "13.0.1" + version: "14.0.0" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + sha256: f49cb15a064ea9d974fc7fbb302099353b7b170d07284e86e264561579e5bcf8 url: "https://pub.dev" source: hosted - version: "9.4.7" + version: "9.6.1" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + sha256: "6ea98b3f17f60d3b527f2647ed2ab4dc0f6bfe25b22cb1c363f5d8f62252f6ac" url: "https://pub.dev" source: hosted - version: "0.1.3+5" + version: "0.1.4+1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + sha256: a5c8a97ecf5616112a5b16d4b8e9ec0e5ae90ef63ac69c0d7b8ae240be760b23 url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.4.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + sha256: caeae01858a0a7d2df67a445ac98e1ad95e55a0e77c73044f4e9b1c8c2289cbd url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.2.2" petitparser: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.2" platform: dependency: transitive description: @@ -612,14 +660,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.dev" + source: hosted + version: "6.5.0" provider: dependency: "direct main" description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.5+1" rxdart: dependency: transitive description: @@ -632,10 +688,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.5" shared_preferences_android: dependency: transitive description: @@ -697,14 +753,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" sqflite: dependency: transitive description: @@ -789,10 +837,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.12" typed_data: dependency: transitive description: @@ -869,18 +917,18 @@ packages: dependency: transitive description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.6.0" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.2.2" vector_graphics_codec: dependency: transitive description: @@ -893,18 +941,18 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: "4dca4feb77dc3ec7f6e27e49c53241eb8217f55e4f9b12599a27f8903bca5682" url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: @@ -925,42 +973,50 @@ packages: dependency: "direct main" description: name: webview_flutter - sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba + sha256: d53e1ccf5516f25017e3c9d44c39034db352d20fa34fe200674270242c2c5111 url: "https://pub.dev" source: hosted - version: "4.13.0" + version: "4.14.1" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: "414174cacd339046a1a6dbf93cac62422194c676fed11119ccf228b81640e887" + sha256: a97db7a44f8e71af2f3971c45550a08cce1fb60059c1b8e534251e6cfb753490 url: "https://pub.dev" source: hosted - version: "4.9.1" + version: "4.13.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0" + sha256: "1221c1b12f5278791042f2ec2841743784cf25c5a644e23d6680e5d718824f04" url: "https://pub.dev" source: hosted - version: "2.14.0" + version: "2.15.1" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f + sha256: c879dd64b87c452aa84381b244d5469da57ba7e8cca6884c7b1e0d406372c12d url: "https://pub.dev" source: hosted - version: "3.23.0" + version: "3.26.0" win32: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: a0b93865d5644f11cf6a8c3f6db909f1ec168958b5805f6cc684adea957cd63d + url: "https://pub.dev" + source: hosted + version: "6.4.0" + windows_file_picker: + dependency: transitive + description: + name: windows_file_picker + sha256: bb25e1b0098c316cc5bcfa04e858f36644c68598884dcd254fbedbbd62f68406 url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "1.0.0" xdg_directories: dependency: transitive description: @@ -973,10 +1029,18 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" sdks: - dart: ">=3.9.2 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.13.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 367582e..e3656d3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,101 +1,53 @@ name: proxycloud description: "A modern, feature-rich VPN client that's fast, unlimited, secure, and completely free." -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. +publish_to: "none" + version: 3.9.0+55 environment: - sdk: ^3.9.2 + sdk: ^3.13.0 -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.8 - easy_localization: ^3.0.7 + cupertino_icons: ^1.0.9 + easy_localization: ^3.0.8 flutter_v2ray_client: path: local_packages/flutter_v2ray_client - http: ^1.2.0 - provider: ^6.1.1 - shared_preferences: ^2.2.2 - flutter_svg: ^2.0.9 + http: ^1.6.0 + provider: ^6.1.5+1 + shared_preferences: ^2.5.5 + flutter_svg: ^2.3.0 url_launcher: ^6.3.2 - flutter_spinkit: ^5.2.0 - clipboard: ^0.1.3 - path_provider: ^2.1.1 - google_fonts: ^6.1.0 - lottie: ^2.7.0 - flutter_animate: ^4.5.0 - cached_network_image: ^3.3.1 - flutter_cache_manager: ^3.3.1 - intl: ^0.20.2 - webview_flutter: ^4.13.0 - file_picker: ^10.3.2 - permission_handler: ^12.0.1 - image_picker: ^1.1.2 + flutter_spinkit: ^5.2.2 + clipboard: ^3.0.14 + path_provider: ^2.1.6 + google_fonts: ^8.2.1 + lottie: ^3.5.1 + flutter_animate: ^4.5.2 + cached_network_image: ^3.4.1 + flutter_cache_manager: ^3.4.2 + intl: any + webview_flutter: ^4.14.1 + file_picker: ^12.0.0 + permission_handler: ^13.0.1 + image_picker: ^1.2.3 dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^6.0.0 - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^5.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - # To add assets to your application, add an assets section, like this: assets: - assets/images/ - assets/languages/ - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/to/asset-from-package - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: # fonts: # - family: Schyler # fonts: @@ -107,6 +59,3 @@ flutter: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package \ No newline at end of file