From eb4fad64cbfb39f458f6e5df11ab368c1e0294fc Mon Sep 17 00:00:00 2001 From: speakeloudest Date: Thu, 8 Jan 2026 03:49:59 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E8=AF=B8=E5=A4=9Abug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hiddify/hiddify/bg/BoxService.kt | 5 + .../com/hiddify/hiddify/bg/VPNService.kt | 5 + .../controllers/hi_help_controller.dart | 3 + .../modules/hi_help/views/hi_help_view.dart | 7 +- .../controllers/kr_home_controller.dart | 7 +- .../controllers/kr_message_controller.dart | 2 + .../kr_message/views/kr_message_view.dart | 7 +- .../kr_order_status_controller.dart | 23 +++- lib/app/widgets/hi_collapsible_list.dart | 16 +++ lib/app/widgets/hi_fixed_scrollbar.dart | 130 ++++++++++-------- macos/Podfile.lock | 7 - 11 files changed, 133 insertions(+), 79 deletions(-) diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/bg/BoxService.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/bg/BoxService.kt index f69654e..e0c3921 100755 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/bg/BoxService.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/bg/BoxService.kt @@ -387,6 +387,11 @@ class BoxService( stopService() } + fun onTaskRemoved(intent: Intent?) { + Log.d(TAG, "📦 onTaskRemoved 被调用, 准备停止 VPN 服务") + stopService() + } + fun writeLog(message: String) { binder.broadcast { it.onServiceWriteLog(message) diff --git a/android/app/src/main/kotlin/com/hiddify/hiddify/bg/VPNService.kt b/android/app/src/main/kotlin/com/hiddify/hiddify/bg/VPNService.kt index fd3df14..eacf2b5 100755 --- a/android/app/src/main/kotlin/com/hiddify/hiddify/bg/VPNService.kt +++ b/android/app/src/main/kotlin/com/hiddify/hiddify/bg/VPNService.kt @@ -38,6 +38,11 @@ class VPNService : VpnService(), PlatformInterfaceWrapper { service.onDestroy() } + override fun onTaskRemoved(rootIntent: Intent?) { + super.onTaskRemoved(rootIntent) + service.onTaskRemoved(rootIntent) + } + override fun onRevoke() { runBlocking { withContext(Dispatchers.Main) { diff --git a/lib/app/modules/hi_help/controllers/hi_help_controller.dart b/lib/app/modules/hi_help/controllers/hi_help_controller.dart index 2a5659a..1860288 100755 --- a/lib/app/modules/hi_help/controllers/hi_help_controller.dart +++ b/lib/app/modules/hi_help/controllers/hi_help_controller.dart @@ -1,5 +1,6 @@ import 'package:get/get.dart'; import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/widgets.dart'; import '../../../services/api_service/kr_api.user.dart'; import '../../../utils/kr_common_util.dart'; @@ -85,6 +86,7 @@ class HIHelpController extends GetxController { int kr_page = 1; final int kr_size = 10; final EasyRefreshController refreshController = EasyRefreshController(); + final ScrollController kr_scrollController = ScrollController(); @override void onInit() { @@ -134,6 +136,7 @@ class HIHelpController extends GetxController { @override void onClose() { refreshController.dispose(); + kr_scrollController.dispose(); super.onClose(); } } diff --git a/lib/app/modules/hi_help/views/hi_help_view.dart b/lib/app/modules/hi_help/views/hi_help_view.dart index 6f91269..bb82ee9 100755 --- a/lib/app/modules/hi_help/views/hi_help_view.dart +++ b/lib/app/modules/hi_help/views/hi_help_view.dart @@ -25,11 +25,10 @@ class HIHelpView extends GetView implements HasSwipeConfig { @override Widget build(BuildContext context) { - final ScrollController scrollController = ScrollController(); return HIBaseScaffold( child: Padding( - padding: const EdgeInsets.only(left: 20), + padding: const EdgeInsets.only(left: 0), child: Column( children: [ Expanded( @@ -51,9 +50,9 @@ class HIHelpView extends GetView implements HasSwipeConfig { child: Padding( padding: EdgeInsets.only(right: 0.w), // 滚动条与内容间距 child: HiFixedScrollbar( - controller: scrollController, + controller: controller.kr_scrollController, child: ListView.builder( - controller: scrollController, + controller: controller.kr_scrollController, padding: EdgeInsets.only(left: 40.w, right: 40.w), itemCount: controller.kr_messages.length, itemBuilder: (context, index) { diff --git a/lib/app/modules/kr_home/controllers/kr_home_controller.dart b/lib/app/modules/kr_home/controllers/kr_home_controller.dart index 190f764..f272112 100755 --- a/lib/app/modules/kr_home/controllers/kr_home_controller.dart +++ b/lib/app/modules/kr_home/controllers/kr_home_controller.dart @@ -200,7 +200,9 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 检查当前是否已连接 if (kr_isConnected.value) { - KRLogUtil.kr_i('当前已连接,跳过自动连接', tag: 'QuickConnect'); + KRLogUtil.kr_i('当前已连接,同步线路信息', tag: 'QuickConnect'); + await _restoreSelectedNode(); + kr_updateConnectionInfo(); return; } @@ -1609,7 +1611,8 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { String actualTag; - if (kr_selectedCountryTag.value == 'auto') { + if (kr_selectedCountryTag.value == 'auto' && + kr_cutSeletedTag.value.isNotEmpty) { actualTag = kr_cutSeletedTag.value; } // 🔧 优先策略: diff --git a/lib/app/modules/kr_message/controllers/kr_message_controller.dart b/lib/app/modules/kr_message/controllers/kr_message_controller.dart index 8a17e18..f19c27f 100755 --- a/lib/app/modules/kr_message/controllers/kr_message_controller.dart +++ b/lib/app/modules/kr_message/controllers/kr_message_controller.dart @@ -21,6 +21,7 @@ class KRMessageController extends GetxController { controlFinishRefresh: true, // ⭐ 必须设置为 true controlFinishLoad: true, // 加载更多通常也需要手动控制 ); + final ScrollController kr_scrollController = ScrollController(); @override void onInit() { @@ -86,6 +87,7 @@ class KRMessageController extends GetxController { @override void onClose() { refreshController.dispose(); + kr_scrollController.dispose(); super.onClose(); } } diff --git a/lib/app/modules/kr_message/views/kr_message_view.dart b/lib/app/modules/kr_message/views/kr_message_view.dart index ec85e91..39746d9 100755 --- a/lib/app/modules/kr_message/views/kr_message_view.dart +++ b/lib/app/modules/kr_message/views/kr_message_view.dart @@ -26,11 +26,10 @@ class KRMessageView extends GetView @override Widget build(BuildContext context) { - final ScrollController scrollController = ScrollController(); return HIBaseScaffold( child: Padding( - padding: const EdgeInsets.only(left: 20), + padding: const EdgeInsets.only(left: 0), child: Stack( children: [ // 主要内容区域 @@ -80,9 +79,9 @@ class KRMessageView extends GetView child: Padding( padding: EdgeInsets.only(right: 0.w), child: HiFixedScrollbar( - controller: scrollController, + controller: controller.kr_scrollController, child: ListView.builder( - controller: scrollController, + controller: controller.kr_scrollController, padding: EdgeInsets.only(left: 40.w, right: 40.w), itemCount: controller.kr_messages.length, diff --git a/lib/app/modules/kr_order_status/controllers/kr_order_status_controller.dart b/lib/app/modules/kr_order_status/controllers/kr_order_status_controller.dart index 2ee8faa..6635062 100755 --- a/lib/app/modules/kr_order_status/controllers/kr_order_status_controller.dart +++ b/lib/app/modules/kr_order_status/controllers/kr_order_status_controller.dart @@ -11,10 +11,10 @@ import '../../../utils/kr_common_util.dart'; import '../../../localization/app_translations.dart'; import '../../../utils/kr_log_util.dart'; import 'package:flutter/foundation.dart'; -import ''; +import 'package:flutter/widgets.dart'; /// 订单状态控制器(参考 Tauri 项目实现) -class KROrderStatusController extends GetxController { +class KROrderStatusController extends GetxController with WidgetsBindingObserver { /// API服务 final KRSubscribeApi kr_subscribeApi = KRSubscribeApi(); final KRSubscribeService kr_subscribeService = KRSubscribeService(); @@ -99,6 +99,9 @@ class KROrderStatusController extends GetxController { kr_checkPaymentStatus(); // 启动轮询和倒计时 kr_startCheckingPaymentStatus(); + + // 添加生命周期监听 + WidgetsBinding.instance.addObserver(this); } @override @@ -114,9 +117,25 @@ class KROrderStatusController extends GetxController { } kr_timer?.cancel(); kr_countdownTimer?.cancel(); + // 移除生命周期监听 + WidgetsBinding.instance.removeObserver(this); super.onClose(); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + if (kDebugMode) { + print('📱 App 恢复前台,立即更新倒计时和订单状态'); + } + // 立即触发一次倒计时更新 + kr_updateCountdown(); + // 立即检查一次订单状态 + kr_checkPaymentStatus(); + } + } + /// 开始检查支付状态(参考 Tauri 项目:5秒轮询一次) void kr_startCheckingPaymentStatus() { if (kDebugMode) { diff --git a/lib/app/widgets/hi_collapsible_list.dart b/lib/app/widgets/hi_collapsible_list.dart index d8d2d71..496567e 100644 --- a/lib/app/widgets/hi_collapsible_list.dart +++ b/lib/app/widgets/hi_collapsible_list.dart @@ -103,6 +103,22 @@ class _HICollapsibleItemWidgetState extends State { setState(() { _isExpanded = !_isExpanded; }); + + if (_isExpanded) { + // 在下一帧之后,等待动画接近完成时滚动到可视区域 + WidgetsBinding.instance.addPostFrameCallback((_) { + Future.delayed(const Duration(milliseconds: 200), () { + if (mounted && _isExpanded) { + Scrollable.ensureVisible( + context, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + alignment: 0.1, // 距离顶部留出一点间距 + ); + } + }); + }); + } }, behavior: HitTestBehavior.translucent, child: Padding( diff --git a/lib/app/widgets/hi_fixed_scrollbar.dart b/lib/app/widgets/hi_fixed_scrollbar.dart index c52c5bf..fdc3cb1 100644 --- a/lib/app/widgets/hi_fixed_scrollbar.dart +++ b/lib/app/widgets/hi_fixed_scrollbar.dart @@ -79,13 +79,19 @@ class _HiFixedScrollbarState extends State bool _onScrollNotification(ScrollNotification notification) { if (!mounted) return false; + if (_fadeTimer != null && _fadeTimer!.isActive) { _fadeTimer?.cancel(); } + if (notification is ScrollStartNotification || notification is ScrollUpdateNotification) { _fadeController.forward(); _updateThumbPosition(); + } else if (notification is ScrollMetricsNotification) { + // 仅在最大滚动范围变化时更新位置且保持可见 + // 避免不必要的闪烁 + _updateThumbPosition(); } else if (notification is ScrollEndNotification || (notification is UserScrollNotification && notification.direction == ScrollDirection.idle)) { @@ -119,14 +125,18 @@ class _HiFixedScrollbarState extends State return; } + // 轨道总高度 = 视口高度 - 拇指高度 final trackHeight = viewport - widget.thumbHeight.h; - final scrollRatio = offset / maxScrollExtent; - final newOffset = (trackHeight * scrollRatio).clamp(0.0, trackHeight); + // 滚动比例 + final scrollRatio = (offset / maxScrollExtent).clamp(0.0, 1.0); + final newOffset = trackHeight * scrollRatio; if (_thumbOffset != newOffset) { setState(() { _thumbOffset = newOffset; }); + + // 当位置发生变化时,保持显示一段时间 if (_fadeTimer != null && _fadeTimer!.isActive) { _fadeTimer?.cancel(); } @@ -134,8 +144,6 @@ class _HiFixedScrollbarState extends State _fadeTimer = Timer(widget.fadeDelay, () { if (mounted) { _fadeController.reverse(); - } else { - _fadeTimer?.cancel(); } }); } @@ -151,67 +159,69 @@ class _HiFixedScrollbarState extends State @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (_, constraints) { - final position = - widget.controller.hasClients ? widget.controller.position : null; + return NotificationListener( + onNotification: _onScrollNotification, + child: Stack( + children: [ + widget.child, + // 滚动条渲染逻辑移至内部,确保外部组件树(widget.child 的路径)稳定 + AnimatedBuilder( + animation: _fadeController, + builder: (context, child) { + final position = widget.controller.hasClients + ? widget.controller.position + : null; - if (position == null || !position.hasContentDimensions) { - return widget.child; - } + if (position == null || !position.hasContentDimensions) { + return const SizedBox.shrink(); + } - final maxScrollExtent = position.maxScrollExtent; - final canShowScrollbar = widget.isShowScrollbar && maxScrollExtent > 0; + final maxScrollExtent = position.maxScrollExtent; + final canShowScrollbar = + widget.isShowScrollbar && maxScrollExtent > 0; - return Stack( - children: [ - NotificationListener( - onNotification: _onScrollNotification, - child: widget.child, - ), - if (canShowScrollbar) - AnimatedBuilder( - animation: _fadeController, - builder: (context, child) { - return Opacity( - opacity: _fadeController.value, - child: Stack( - children: [ - // 滚动条轨道 (Track) - Positioned( - right: widget.right.w.toDouble(), - top: 0, - bottom: 0, - child: Container( - width: widget.thickness.w.toDouble(), - decoration: BoxDecoration( - color: widget.trackColor, - borderRadius: BorderRadius.circular(4), - ), - ), + if (!canShowScrollbar) { + return const SizedBox.shrink(); + } + + return Opacity( + opacity: _fadeController.value, + child: Stack( + children: [ + // 滚动条轨道 (Track) + Positioned( + right: widget.right.w.toDouble(), + top: 0, + bottom: 0, + child: Container( + width: widget.thickness.w.toDouble(), + decoration: BoxDecoration( + color: widget.trackColor, + borderRadius: BorderRadius.circular(4), ), - - // 滚动条拇指 (Thumb) - Positioned( - right: widget.right.w.toDouble(), - top: _thumbOffset, - child: Container( - width: widget.thickness.w.toDouble(), - height: widget.thumbHeight.h.toDouble(), - decoration: BoxDecoration( - color: widget.thumbColor, - borderRadius: BorderRadius.circular(4), - ), - ), - ), - ], + ), ), - ); - }, - ), - ], - ); - }, + + // 滚动条拇指 (Thumb) + Positioned( + right: widget.right.w.toDouble(), + top: _thumbOffset, + child: Container( + width: widget.thickness.w.toDouble(), + height: widget.thumbHeight.h.toDouble(), + decoration: BoxDecoration( + color: widget.thumbColor, + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ], + ), + ); + }, + ), + ], + ), ); } } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index e73a911..653b41a 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -30,9 +30,6 @@ PODS: - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - - webview_flutter_wkwebview (0.0.1): - - Flutter - - FlutterMacOS - window_manager (0.2.0): - FlutterMacOS @@ -49,7 +46,6 @@ DEPENDENCIES: - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - webview_flutter_wkwebview (from `Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) SPEC REPOS: @@ -83,8 +79,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - webview_flutter_wkwebview: - :path: Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin window_manager: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos @@ -104,7 +98,6 @@ SPEC CHECKSUMS: share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 - webview_flutter_wkwebview: a4af96a051138e28e29f60101d094683b9f82188 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 PODFILE CHECKSUM: 04e3af9980f29522a03273385f61d561da92c2fb