From 72b2ea4bc50cb081ca857b80304f8c447a004787 Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Sun, 19 Apr 2026 18:31:28 +0800 Subject: [PATCH] flutter-3.19.6 --- .fvmrc | 3 + .gitignore | 5 +- lib/ui/app_update/new_version_dialog.dart | 3 +- lib/ui/component/qrcode/qr_scan_view.dart | 68 ++++++++----------- .../search/highlight_text_document.dart | 7 +- lib/ui/desktop/request/request.dart | 1 + lib/ui/desktop/setting/script.dart | 3 +- lib/ui/mobile/request/history.dart | 2 +- lib/ui/mobile/request/request.dart | 1 + lib/ui/mobile/setting/script.dart | 10 +-- lib/utils/flutter_compat.dart | 13 ++++ linux/flutter/generated_plugins.cmake | 1 - macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 4 +- windows/flutter/generated_plugins.cmake | 1 - 15 files changed, 65 insertions(+), 59 deletions(-) create mode 100644 .fvmrc diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..03c3fb3 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.19.6" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index f6f5e2c..9374407 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,7 @@ app.*.map.json l10n_errors.txt pubspec.lock -/dist/ \ No newline at end of file +/dist/ + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/lib/ui/app_update/new_version_dialog.dart b/lib/ui/app_update/new_version_dialog.dart index 5def3e8..641936c 100644 --- a/lib/ui/app_update/new_version_dialog.dart +++ b/lib/ui/app_update/new_version_dialog.dart @@ -73,7 +73,8 @@ class NewVersionDialog extends StatelessWidget { if (canIgnore) TextButton( onPressed: () async { - SharedPreferencesAsync().setString(Constants.ignoreReleaseVersionKey, newVersion.version); + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(Constants.ignoreReleaseVersionKey, newVersion.version); logger.i("ignored release [${newVersion.version}]"); if (context.mounted) Navigator.pop(context); }, diff --git a/lib/ui/component/qrcode/qr_scan_view.dart b/lib/ui/component/qrcode/qr_scan_view.dart index 4342454..1b305bf 100644 --- a/lib/ui/component/qrcode/qr_scan_view.dart +++ b/lib/ui/component/qrcode/qr_scan_view.dart @@ -1,47 +1,37 @@ -import 'package:file_picker/file_picker.dart'; +import 'package:easy_permission/easy_permission.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_qr_reader/flutter_qr_reader.dart'; +import 'package:image_pickers/image_pickers.dart'; import 'package:proxypin/l10n/app_localizations.dart'; -import 'package:flutter_qr_reader_plus/flutter_qr_reader.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:proxypin/network/util/logger.dart'; ///@Author: Hongen Wang /// qr code scanner class QrCodeScanner { static Future scan(BuildContext context) async { - var status = await Permission.camera.status; + var status = await EasyPermission.getSinglePermissionStatus(PermissionType.CAMERA); - if (!status.isGranted) { - status = await Permission.camera.request(); + if (status == PermissionStatus.DENY) { + EasyPermission.openSettings(); + return Future.value(null); + } else if (status == PermissionStatus.ALLOW) { + status = await EasyPermission.requestSinglePermission(PermissionType.CAMERA); } - if (!status.isGranted) { + if (status != PermissionStatus.ALLOW) { if (!context.mounted) return Future.value(null); AppLocalizations localizations = AppLocalizations.of(context)!; bool isCN = localizations.localeName == 'zh'; - await showDialog( + showDialog( context: context, builder: (context) => AlertDialog( - content: Text(isCN ? "请授予相机权限" : "Please grant camera permission"), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text(localizations.cancel), - ), - TextButton( - onPressed: () async { - if (!context.mounted) return Future.value(null); - Navigator.of(context).pop(); - final PermissionStatus newStatus = await Permission.camera.request(); - // Flutter权限处理有bug url: https://github.com/Baseflow/flutter-permission-handler/issues/1206 - if (newStatus.isRestricted || newStatus.isPermanentlyDenied) { - openAppSettings(); - } - }, - child: Text(localizations.confirm), - ), - ], - )); + content: Text(isCN ? "请授予相机权限" : "Please grant camera permission"), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(localizations.confirm), + ), + ], + )); return Future.value(null); } @@ -86,7 +76,6 @@ class _QrReaderViewState extends State with TickerProviderStateM isScan = true; _controller?.startCamera((data, _) async { - logger.d("scan qrCode data handle: $data"); await handle(data); }); @@ -95,6 +84,7 @@ class _QrReaderViewState extends State with TickerProviderStateM handle(String data) async { if (!isScan) return; + _controller?.stopCamera(); stop(); if (mounted) await Navigator.of(context, rootNavigator: true).maybePop(data); } @@ -130,7 +120,6 @@ class _QrReaderViewState extends State with TickerProviderStateM isScan = false; _controller?.stopCamera(); - _controller = null; if (_animationController != null) { _animationController?.stop(); _animationController?.dispose(); @@ -154,7 +143,7 @@ class _QrReaderViewState extends State with TickerProviderStateM FlutterQrReader.imgScan(path).then((value) { stop(); if (mounted) { - Navigator.of(context, rootNavigator: true).pop(value ?? "-1"); + Navigator.of(context, rootNavigator: true).pop(value.isEmpty ? "-1" : value); } }); } @@ -198,15 +187,12 @@ class _QrReaderViewState extends State with TickerProviderStateM mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ IconButton( - onPressed: () async { - final result = await FilePicker.platform.pickFiles( - type: FileType.image, - allowMultiple: false, - ); - if (result == null || result.files.isEmpty) return; - final path = result.files.first.path; - if (path == null) return; - scanImage(path); + onPressed: () { + ImagePickers.pickerPaths(showCamera: true).then((value) { + if (value.isNotEmpty) { + scanImage(value[0].path!); + } + }); }, icon: Icon(Icons.photo_library, color: Colors.white, size: 35), ), diff --git a/lib/ui/component/search/highlight_text_document.dart b/lib/ui/component/search/highlight_text_document.dart index 6a7bf9e..1bcdef7 100644 --- a/lib/ui/component/search/highlight_text_document.dart +++ b/lib/ui/component/search/highlight_text_document.dart @@ -4,6 +4,7 @@ import 'package:flutter_highlight/themes/atom-one-light.dart'; import 'package:highlight/highlight.dart' show Node, highlight; import 'search_controller.dart'; +import 'package:proxypin/utils/flutter_compat.dart'; class HighlightTextDocument { final String text; @@ -81,7 +82,7 @@ class HighlightTextDocument { } final spans = []; - final colorScheme = ColorScheme.of(context); + final colorScheme = context.colorScheme; var matchIndex = 0; var consumed = 0; @@ -142,7 +143,7 @@ class HighlightTextDocument { } TextStyle highlightRootStyle(BuildContext context, [TextStyle? style]) { - final theme = Theme.brightnessOf(context) == Brightness.light ? atomOneLightTheme : atomOneDarkTheme; + final theme = Theme.of(context).brightness == Brightness.light ? atomOneLightTheme : atomOneDarkTheme; return _stripBackground((theme['root'] ?? const TextStyle(fontFamily: 'monospace', fontSize: 14.5)).merge(style)) ?? const TextStyle(fontFamily: 'monospace', fontSize: 14.5); } @@ -159,7 +160,7 @@ List buildHighlightBaseSegments( try { final parsed = highlight.parse(text, language: language).nodes ?? const []; - final theme = Theme.brightnessOf(context) == Brightness.light ? atomOneLightTheme : atomOneDarkTheme; + final theme = Theme.of(context).brightness == Brightness.light ? atomOneLightTheme : atomOneDarkTheme; List convert(List nodes, [TextStyle? inheritedStyle]) { final spans = []; diff --git a/lib/ui/desktop/request/request.dart b/lib/ui/desktop/request/request.dart index afe489c..c8525d6 100644 --- a/lib/ui/desktop/request/request.dart +++ b/lib/ui/desktop/request/request.dart @@ -42,6 +42,7 @@ import 'package:proxypin/ui/desktop/widgets/highlight.dart'; import 'package:proxypin/utils/curl.dart'; import 'package:proxypin/utils/keyword_highlight.dart'; import 'package:proxypin/utils/lang.dart'; +import 'package:proxypin/utils/flutter_compat.dart'; import 'package:proxypin/utils/python.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:window_manager/window_manager.dart'; diff --git a/lib/ui/desktop/setting/script.dart b/lib/ui/desktop/setting/script.dart index f29ce9b..b58a172 100644 --- a/lib/ui/desktop/setting/script.dart +++ b/lib/ui/desktop/setting/script.dart @@ -35,6 +35,7 @@ import 'package:proxypin/ui/component/multi_window.dart'; import 'package:proxypin/ui/component/utils.dart'; import 'package:proxypin/ui/component/widgets.dart'; import 'package:proxypin/utils/lang.dart'; +import 'package:proxypin/utils/flutter_compat.dart'; bool _refresh = false; @@ -585,7 +586,7 @@ class _ScriptEditState extends State { width: 155, height: 34, child: DropdownButtonFormField( - initialValue: _useRemote, + value: _useRemote, items: [ DropdownMenuItem(value: false, child: Text(localizations.local)), DropdownMenuItem(value: true, child: Text(localizations.remoteUrl)), diff --git a/lib/ui/mobile/request/history.dart b/lib/ui/mobile/request/history.dart index 4c11be3..eb33ba2 100644 --- a/lib/ui/mobile/request/history.dart +++ b/lib/ui/mobile/request/history.dart @@ -310,7 +310,7 @@ class _MobileHistoryState extends State { rect = Rect.fromCenter(center: offset, width: 1, height: 1); } - SharePlus.instance.share(ShareParams(files: [file], fileNameOverrides: [fileName], sharePositionOrigin: rect)); + await Share.shareXFiles([file], fileNameOverrides: [fileName], sharePositionOrigin: rect); Future.delayed(const Duration(seconds: 30), () => item.requests = null); } diff --git a/lib/ui/mobile/request/request.dart b/lib/ui/mobile/request/request.dart index e82a50c..80721f5 100644 --- a/lib/ui/mobile/request/request.dart +++ b/lib/ui/mobile/request/request.dart @@ -42,6 +42,7 @@ import 'package:proxypin/utils/keyword_highlight.dart'; import 'package:proxypin/utils/lang.dart'; import 'package:proxypin/utils/navigator.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:proxypin/utils/flutter_compat.dart'; ///请求行 class RequestRow extends StatefulWidget { diff --git a/lib/ui/mobile/setting/script.dart b/lib/ui/mobile/setting/script.dart index 31afa2d..fd349d2 100644 --- a/lib/ui/mobile/setting/script.dart +++ b/lib/ui/mobile/setting/script.dart @@ -33,6 +33,7 @@ import 'package:proxypin/ui/component/widgets.dart'; import 'package:proxypin/ui/mobile/widgets/floating_window.dart'; import 'package:proxypin/utils/lang.dart'; import 'package:proxypin/utils/platform.dart'; +import 'package:proxypin/utils/flutter_compat.dart'; import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -626,7 +627,7 @@ class _ScriptEditState extends State { SizedBox(width: 55, child: Text('${localizations.type}:')), Expanded( child: DropdownButtonFormField( - initialValue: _useRemote, + value: _useRemote, items: [ DropdownMenuItem(value: false, child: Text(localizations.local)), DropdownMenuItem(value: true, child: Text(localizations.remoteUrl)), @@ -1066,12 +1067,7 @@ class _ScriptListState extends State { } final XFile file = XFile.fromData(utf8.encode(jsonEncode(json)), mimeType: 'json'); - final shareParams = ShareParams( - files: [file], - fileNameOverrides: [fileName], - sharePositionOrigin: box?.paintBounds, - ); - SharePlus.instance.share(shareParams); + await Share.shareXFiles([file], fileNameOverrides: [fileName], sharePositionOrigin: box?.paintBounds); } void enableStatus(bool enable) { diff --git a/lib/utils/flutter_compat.dart b/lib/utils/flutter_compat.dart index 01b58d3..d099221 100644 --- a/lib/utils/flutter_compat.dart +++ b/lib/utils/flutter_compat.dart @@ -7,6 +7,19 @@ import 'package:flutter/material.dart'; /// to emulate older/newer helper methods used in the codebase. /// - BuildContext.colorScheme getter as a convenience. +/// Provide a small set of ColorScheme getters that may be referenced in code +/// compiled against newer Flutter SDKs. These return reasonable fallbacks so +/// code can compile against older SDKs as well. +extension ColorSchemeCompat on ColorScheme { + /// A lightweight surface color variant used throughout the app. If the + /// newer `surfaceContainerLow` semantic is available in the SDK it would be + /// preferred; here we emulate it with a slightly transparent surface color. + Color get surfaceContainerLow => surface.withOpacity(0.05); + + /// A mild outline-like color. Emulated from onSurface with low opacity. + Color get outlineVariant => onSurface.withOpacity(0.12); +} + extension ColorWithValues on Color { /// If [alpha] is provided, return this color with that opacity. /// If [values] is provided, return a MaterialColor constructed from this color value. diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index f62bb08..740dc77 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -13,7 +13,6 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ca6c8bf..1808fce 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import desktop_multi_window import device_info_plus import flutter_desktop_context_menu import flutter_js +import path_provider_foundation import proxy_manager import screen_retriever_macos import share_plus @@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FlutterDesktopContextMenuPlugin.register(with: registry.registrar(forPlugin: "FlutterDesktopContextMenuPlugin")) FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ProxyManagerPlugin.register(with: registry.registrar(forPlugin: "ProxyManagerPlugin")) ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/pubspec.yaml b/pubspec.yaml index 594d7ed..5ccfc09 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: flutter_localizations: sdk: flutter intl: any - cupertino_icons: ^1.0.9 + cupertino_icons: ^1.0.8 pointycastle: ^4.0.0 logger: ^2.5.0 date_format: ^2.0.9 @@ -46,7 +46,7 @@ dependencies: brotli: ^0.6.0 html: ^0.15.6 - xml: ^6.6.1 + xml: ^6.5.0 # macos_window_utils: 1.6.1 win32audio: ^1.3.1 vclibs: ^0.1.3 diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index ed0273f..260f62b 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -16,7 +16,6 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - jni ) set(PLUGIN_BUNDLED_LIBRARIES)