From 8e27ddeded9afd1999c7c9bc5f335e917db481b2 Mon Sep 17 00:00:00 2001 From: speakeloudest Date: Mon, 1 Dec 2025 22:34:48 -0800 Subject: [PATCH] =?UTF-8?q?Revert=20"feat:=20=E5=A2=9E=E5=8A=A0=E5=8A=A8?= =?UTF-8?q?=E7=94=BB=E5=8F=98=E9=87=8F=EF=BC=8C=E5=A4=84=E7=90=86=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E5=8F=98=E9=87=8F=E6=8E=A7=E5=88=B6=E5=8A=A8=E7=94=BB?= =?UTF-8?q?=E9=80=BB=E8=BE=91"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit aa0fd94cb29a197f2c48d39412e6eb0e8bfb6f5e. --- .../controllers/kr_home_controller.dart | 132 +++--------- .../views/hi_animated_connect_button.dart | 188 +++++++++++------- 2 files changed, 144 insertions(+), 176 deletions(-) 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 b738dad..5f6a2f3 100755 --- a/lib/app/modules/kr_home/controllers/kr_home_controller.dart +++ b/lib/app/modules/kr_home/controllers/kr_home_controller.dart @@ -90,8 +90,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 是否显示延迟 final kr_isLatency = false.obs; - final kr_isPositionAnimating = false.obs; - /// 默认 var kr_cutTag = ''.obs; var kr_cutSeletedTag = ''.obs; @@ -219,17 +217,14 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { await _kr_prepareCountrySelectionBeforeStart(); final selectedAfter = - await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); - KRLogUtil.kr_i( - '准备后 SELECTED_NODE_TAG_autoConnect: ${selectedAfter ?? ''}', + await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); + KRLogUtil.kr_i('准备后 SELECTED_NODE_TAG_autoConnect: ${selectedAfter ?? ''}', tag: 'HomeController'); - KRLogUtil.kr_i( - '准备后 kr_currentNodeName_autoConnect: ${kr_currentNodeName.value}', + KRLogUtil.kr_i('准备后 kr_currentNodeName_autoConnect: ${kr_currentNodeName.value}', tag: 'HomeController'); KRLogUtil.kr_i('准备后 kr_cutTag_autoConnect: ${kr_cutTag.value}', tag: 'HomeController'); - KRLogUtil.kr_i( - '准备后 kr_cutSeletedTag_autoConnect: ${kr_cutSeletedTag.value}', + KRLogUtil.kr_i('准备后 kr_cutSeletedTag_autoConnect: ${kr_cutSeletedTag.value}', tag: 'HomeController'); await kr_performNodeSwitch(selectedAfter!); await KRSingBoxImp.instance.kr_start(); @@ -320,42 +315,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { }); // 🔧 Android 15 新增:5秒后再次强制更新高度,兜底保护 - // Future.delayed(const Duration(seconds: 5), () { - // if (kr_bottomPanelHeight.value < 100) { - // KRLogUtil.kr_w('检测到底部面板高度异常(${kr_bottomPanelHeight.value}),强制修正', - // tag: 'HomeController'); - // kr_updateBottomPanelHeight(); - // } - // }); - - _initShowAnimationWatcher(); - } - - bool _lastIsShow = false; - - void _initShowAnimationWatcher() { - _lastIsShow = kr_currentNodeLatency.value == -1 || kr_isConnected.value; - everAll([kr_currentNodeLatency, kr_isConnected], (_) { - final v = kr_currentNodeLatency.value == -1 || kr_isConnected.value; - if (v != _lastIsShow) { - _triggerPositionAnim(); - _lastIsShow = v; - } - }); - } - - void kr_onPositionAnimationEnd() { - kr_isPositionAnimating.value = false; - } - - int _positionAnimToken = 0; - void _triggerPositionAnim() { - _positionAnimToken++; - final token = _positionAnimToken; - kr_isPositionAnimating.value = true; - Future.delayed(const Duration(milliseconds: 450), () { - if (_positionAnimToken == token) { - kr_isPositionAnimating.value = false; + Future.delayed(const Duration(seconds: 5), () { + if (kr_bottomPanelHeight.value < 100) { + KRLogUtil.kr_w('检测到底部面板高度异常(${kr_bottomPanelHeight.value}),强制修正', + tag: 'HomeController'); + kr_updateBottomPanelHeight(); } }); } @@ -974,26 +938,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { if (kDebugMode) {} } - DateTime? _lastConnectClickAt; - static const Duration _connectClickCooldown = Duration(milliseconds: 600); - - bool kr_canProcessConnectClick() { - final now = DateTime.now(); - if (kr_isPositionAnimating.value) { - return false; - } - final st = KRSingBoxImp.instance.kr_status.value; - if (st is SingboxStarting || st is SingboxStopping) { - return false; - } - if (_lastConnectClickAt != null && - now.difference(_lastConnectClickAt!) < _connectClickCooldown) { - return false; - } - _lastConnectClickAt = now; - return true; - } - /// 🔧 等待状态达到预期值 Future _waitForStatus(Type expectedType, {int maxSeconds = 3}) async { if (kDebugMode) {} @@ -1204,9 +1148,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { /// 处理手动模式 void _kr_handleManualMode(dynamic element) { try { - KRLogUtil.kr_d( - '处理手动模式 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}', - tag: 'HomeController'); + KRLogUtil.kr_d('处理手动模式 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}', tag: 'HomeController'); // 🔧 关键修复:不要用内核返回的 selected 覆盖用户手动选择 // 只有当内核返回的节点与用户选择一致时,才更新相关状态 @@ -1218,15 +1160,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 只有当内核确认切换成功时,才更新 UI 状态 if (element.selected == kr_cutTag.value) { kr_cutSeletedTag.value = element.selected; - kr_currentNodeName.value = - kr_truncateText(element.selected, maxLength: 25); + kr_currentNodeName.value = kr_truncateText(element.selected, maxLength: 25); // kr_moveToSelectedNode(); - KRLogUtil.kr_d('✅ 内核确认节点切换成功: ${element.selected}', - tag: 'HomeController'); + KRLogUtil.kr_d('✅ 内核确认节点切换成功: ${element.selected}', tag: 'HomeController'); } else { // 内核返回的节点与用户选择不一致,保持用户选择的显示 - KRLogUtil.kr_d('⏳ 等待内核切换到用户选择的节点: ${kr_cutTag.value}', - tag: 'HomeController'); + KRLogUtil.kr_d('⏳ 等待内核切换到用户选择的节点: ${kr_cutTag.value}', tag: 'HomeController'); } } catch (e) { KRLogUtil.kr_e('处理手动模式出错: $e', tag: 'HomeController'); @@ -1425,12 +1364,9 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 🔒 节流控制:2秒内的重复切换请求直接忽略 final now = DateTime.now(); - if (_lastSwitchTime != null && - now.difference(_lastSwitchTime!) < _switchThrottleDuration) { - final remainingTime = _switchThrottleDuration.inMilliseconds - - now.difference(_lastSwitchTime!).inMilliseconds; - KRLogUtil.kr_w('⚠️ 切换过于频繁,请等待 ${remainingTime}ms', - tag: 'HomeController'); + if (_lastSwitchTime != null && now.difference(_lastSwitchTime!) < _switchThrottleDuration) { + final remainingTime = _switchThrottleDuration.inMilliseconds - now.difference(_lastSwitchTime!).inMilliseconds; + KRLogUtil.kr_w('⚠️ 切换过于频繁,请等待 ${remainingTime}ms', tag: 'HomeController'); KRCommonUtil.kr_showToast('切换过于频繁,请稍后再试'); return false; } @@ -1457,9 +1393,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 🔧 修复:保存节点选择以便VPN启动时应用 KRLogUtil.kr_i('💾 保存节点选择以便稍后应用: $tag', tag: 'HomeController'); - KRSecureStorage() - .kr_saveData(key: 'SELECTED_NODE_TAG', value: tag) - .then((_) { + KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: tag).then((_) { KRLogUtil.kr_i('✅ 节点选择已保存: $tag', tag: 'HomeController'); }).catchError((e) { KRLogUtil.kr_e('❌ 保存节点选择失败: $e', tag: 'HomeController'); @@ -1478,13 +1412,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 🔧 保存新节点选择 KRLogUtil.kr_i('💾 保存新节点选择: $tag', tag: 'HomeController'); - await KRSecureStorage() - .kr_saveData(key: 'SELECTED_NODE_TAG', value: tag); + await KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: tag); // 🚀 核心改进:使用 selectOutbound 进行热切换(参考 hiddify-app) // 优势:不重启VPN,保持连接状态,切换瞬间完成,VPN开关不闪烁 - KRLogUtil.kr_i('🔄 [热切换] 调用 selectOutbound 切换节点...', - tag: 'HomeController'); + KRLogUtil.kr_i('🔄 [热切换] 调用 selectOutbound 切换节点...', tag: 'HomeController'); // 🔧 关键修复:确定正确的 selector 组 tag // selectOutbound(groupTag, outboundTag) - 第一个参数是组的tag,不是节点的tag @@ -1495,14 +1427,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { for (var group in activeGroups) { if (group.type == ProxyType.selector) { selectorGroupTag = group.tag; - KRLogUtil.kr_i('🔍 找到 selector 组: $selectorGroupTag', - tag: 'HomeController'); + KRLogUtil.kr_i('🔍 找到 selector 组: $selectorGroupTag', tag: 'HomeController'); break; } } - KRLogUtil.kr_i('📡 调用 selectOutbound("$selectorGroupTag", "$tag")', - tag: 'HomeController'); + KRLogUtil.kr_i('📡 调用 selectOutbound("$selectorGroupTag", "$tag")', tag: 'HomeController'); // 调用 sing-box 的 selectOutbound API final result = await KRSingBoxImp.instance.kr_singBox @@ -1511,13 +1441,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 处理切换结果 result.fold( - (error) { + (error) { // 切换失败 - KRLogUtil.kr_e('❌ selectOutbound 调用失败: $error', - tag: 'HomeController'); + KRLogUtil.kr_e('❌ selectOutbound 调用失败: $error', tag: 'HomeController'); throw Exception('节点切换失败: $error'); }, - (_) { + (_) { // 切换成功 KRLogUtil.kr_i('✅ selectOutbound 调用成功', tag: 'HomeController'); }, @@ -1537,18 +1466,15 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { try { final updatedGroups = KRSingBoxImp.instance.kr_activeGroups; final selectGroup = updatedGroups.firstWhere( - (group) => group.type == ProxyType.selector, + (group) => group.type == ProxyType.selector, orElse: () => throw Exception('未找到 selector 组'), ); - KRLogUtil.kr_i( - '📊 [验证] ${selectGroup.tag}组当前选中: ${selectGroup.selected}', - tag: 'HomeController'); + KRLogUtil.kr_i('📊 [验证] ${selectGroup.tag}组当前选中: ${selectGroup.selected}', tag: 'HomeController'); KRLogUtil.kr_i('📊 [验证] 目标节点: $tag', tag: 'HomeController'); if (selectGroup.selected != tag) { - KRLogUtil.kr_w('⚠️ [验证] 节点选择验证失败,实际选中: ${selectGroup.selected}', - tag: 'HomeController'); + KRLogUtil.kr_w('⚠️ [验证] 节点选择验证失败,实际选中: ${selectGroup.selected}', tag: 'HomeController'); // 不抛出异常,但记录警告 } else { KRLogUtil.kr_i('✅ [验证] 节点选择验证成功!', tag: 'HomeController'); @@ -1562,6 +1488,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { KRLogUtil.kr_i('✅ 节点热切换成功,VPN保持连接: $tag', tag: 'HomeController'); return true; + } catch (switchError) { // 后台切换失败,恢复到原节点 KRLogUtil.kr_e('❌ 后台节点切换失败: $switchError', tag: 'HomeController'); @@ -1573,8 +1500,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 恢复原节点选择 try { - await KRSecureStorage() - .kr_saveData(key: 'SELECTED_NODE_TAG', value: originalTag); + await KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: originalTag); } catch (e) { KRLogUtil.kr_e('❌ 恢复节点选择失败: $e', tag: 'HomeController'); } diff --git a/lib/app/modules/kr_home/views/hi_animated_connect_button.dart b/lib/app/modules/kr_home/views/hi_animated_connect_button.dart index f398659..b12e2a8 100644 --- a/lib/app/modules/kr_home/views/hi_animated_connect_button.dart +++ b/lib/app/modules/kr_home/views/hi_animated_connect_button.dart @@ -22,96 +22,113 @@ class HIAnimatedConnectButton extends GetView { @override Widget build(BuildContext context) { return Obx(() { - final _ = KRSingBoxImp.instance.kr_status.value; + final delay = controller.kr_currentNodeLatency.value; + // 🔧 关键: 强制读取两个 observable 确保追踪 + final _ = KRSingBoxImp.instance.kr_status.value; // 强制追踪 + final isConnected = controller.kr_isConnected.value; // 使用 controller 的状态 + + // 再次读取状态用于判断 + final status = KRSingBoxImp.instance.kr_status.value; + final isSwitching = status is SingboxStarting || status is SingboxStopping; + + print('🔵 Switch UI 更新: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching'); + + final isShow = delay == -1 || isConnected; + + final Color buttonColor = Theme.of(context).primaryColor; final double screenWidth = Get.width; final double screenHeight = Get.height; final double buttonSize = 340; - final double currentButtonSize = 270; + final double currentButtonSize = isShow ? 270 : buttonSize; return Stack( children: [ AnimatedPositioned( duration: const Duration(milliseconds: 400), curve: Curves.linear, - left: (screenWidth - currentButtonSize) / 2, - bottom: screenHeight * 0.2, - onEnd: () { - controller.kr_onPositionAnimationEnd(); - }, + left: isShow ? (screenWidth - currentButtonSize) / 2 : -60, + bottom: isShow ? screenHeight * 0.2 : -40, child: Stack( alignment: Alignment.center, clipBehavior: Clip.none, children: [ - if (controller.kr_isPositionAnimating.value) + /// ✅ 呼吸同心圆背景 + if (isShow) Positioned.fill( child: OverflowBox( maxWidth: buttonSize * 3, maxHeight: buttonSize * 3, child: ContinuousRippleEffect( - color: Theme.of(context).primaryColor), + color: Theme.of(context).primaryColor + ), ), ), + + if (!isShow) + Positioned( + top: -20, // 距离顶部 10.w + left: 115, // 距离右侧 10.w + child: KrLocalImage( + imageName: "hi-home-slogan", + imageType: ImageType.svg, + ), + ), + /// ✅ 按钮主体 Material( shape: const CircleBorder(), - color: Colors.transparent, + color: isShow ? Colors.transparent : Theme.of(context).primaryColor, clipBehavior: Clip.antiAlias, - child: AbsorbPointer( - absorbing: controller.kr_isPositionAnimating.value, - child: InkWell( - onTap: () { - if (!controller.kr_canProcessConnectClick()) { - return; + child: InkWell( + onTap: () { + if(isSwitching) { + print('🔵 Switch UI 正在更新,切换中点击了按钮: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching'); + return; + } + final current = controller.kr_subscribeService.kr_currentSubscribe.value; + bool hasValidSubscription = false; + if (current != null) { + DateTime? expire; + try { + expire = DateTime.parse(current.expireTime); + } catch (_) { + expire = null; } - final current = controller - .kr_subscribeService.kr_currentSubscribe.value; - bool hasValidSubscription = false; - if (current != null) { - DateTime? expire; - try { - expire = DateTime.parse(current.expireTime); - } catch (_) { - expire = null; - } - hasValidSubscription = - expire != null && expire.isAfter(DateTime.now()); - } - if (hasValidSubscription) { - controller.kr_toggleSwitch( - !controller.kr_isConnected.value); - } else { - HIDialog.show( - customMessageWidget: Padding( - padding: EdgeInsets.only(top: 16.w), - child: Text( - '尚未购买套餐,请前往购买页面下单后即可开始极速网络体验', - textAlign: TextAlign.left, - style: KrAppTextStyle( - color: Theme.of(context) - .textTheme - .bodyMedium - ?.color, - fontSize: 14.sp, - fontWeight: FontWeight.w600, - ), + hasValidSubscription = expire != null && expire.isAfter(DateTime.now()); + } + if (hasValidSubscription) { + controller.kr_toggleSwitch(!controller.kr_isConnected.value); + } else { + HIDialog.show( + customMessageWidget: Padding( + padding: EdgeInsets.only(top: 16.w), + child: Text( + '尚未购买套餐,请前往购买页面下单后即可开始极速网络体验', + textAlign: TextAlign.left, + style: KrAppTextStyle( + color: Theme.of(context).textTheme.bodyMedium?.color, + fontSize: 14.sp, + fontWeight: FontWeight.w600, ), ), - cancelText: '取消', - confirmText: '前往', - onConfirm: () { - GlobalOverlayService.instance - .triggerSubscriptionAnimation(); - }, - ); - } - }, - child: SizedBox( - width: currentButtonSize, - height: currentButtonSize, - child: Stack( - alignment: Alignment.center, - children: [ + ), + cancelText: '取消', + confirmText: '前往', + onConfirm: () { + GlobalOverlayService.instance.triggerSubscriptionAnimation(); + }, + ); + } + }, + child: SizedBox( + width: currentButtonSize, + height: currentButtonSize, + child: Stack( + alignment: Alignment.center, + children: [ + if (isShow) + /// ✅ isShow = true,图片居中,文字贴近底部 Stack( alignment: Alignment.center, children: [ @@ -131,25 +148,52 @@ class HIAnimatedConnectButton extends GetView { controller.kr_connectText.value == '已连接' ? '已连接\n点击断开' : controller.kr_connectText.value, - key: ValueKey( - controller.kr_connectText.value), + key: ValueKey(controller.kr_connectText.value), textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( color: Colors.black, fontSize: 18, fontWeight: FontWeight.bold, - height: 1, + height: 1 ), ), ), ], + ) + else + /// ✅ isShow = false,恢复原本布局 + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + KrLocalImage( + imageName: "hi-home-logo", + width: 104, + imageType: ImageType.svg, + ), + SizedBox(height: 2.h), + Text( + controller.kr_connectText.value == '已连接' + ? '已连接\n点击断开' + : controller.kr_connectText.value, + key: ValueKey(controller.kr_connectText.value), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 18.sp, + fontWeight: FontWeight.bold, + ), + ), + ], ), - ], - ), + ], ), ), + + ), ), + ], ), ), @@ -158,7 +202,6 @@ class HIAnimatedConnectButton extends GetView { }); } } - /// ✅ 呼吸式同心圆动画(最小圆固定270,其他圆在270~479间波动,初始直径差10%) /// ✅ 呼吸式同心圆动画(所有圆在270~470之间运动,起点相同但周期不同) class ContinuousRippleEffect extends StatefulWidget { @@ -189,11 +232,10 @@ class _ContinuousRippleEffectState extends State // 动画周期差异:外层慢,内层快 final duration = Duration(milliseconds: 3000 + i * 1200); - final controller = AnimationController(vsync: this, duration: duration) - ..repeat(reverse: true); + final controller = + AnimationController(vsync: this, duration: duration)..repeat(reverse: true); - final curved = - CurvedAnimation(parent: controller, curve: Curves.easeInOutSine); + final curved = CurvedAnimation(parent: controller, curve: Curves.easeInOutSine); _controllers.add(controller); _animations.add(curved); }