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 ee9e9c9..8952c0f 100755 --- a/lib/app/modules/kr_home/controllers/kr_home_controller.dart +++ b/lib/app/modules/kr_home/controllers/kr_home_controller.dart @@ -25,7 +25,7 @@ import '../models/kr_home_views_status.dart'; import 'package:kaer_with_panels/app/model/response/kr_user_available_subscribe.dart'; import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; -import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart'; +import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; class KRHomeController extends GetxController with WidgetsBindingObserver { @@ -654,7 +654,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { kr_connectText.value = AppTranslations.kr_home.disconnecting; break; } - + // 强制更新UI update(); }); @@ -1119,77 +1119,78 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { } } - // 选择节点 - void kr_selectNode(String tag) async { + /// 🔧 修复:统一的节点切换方法(包含UI同步和后台操作等待) + /// 执行节点切换,包括UI更新和后台操作的完整同步 + /// 返回 true 表示切换成功,false 表示失败 + Future kr_performNodeSwitch(String tag) async { try { + KRLogUtil.kr_i('🔄 开始切换节点: $tag', tag: 'HomeController'); + + // 1. 保存原节点,以备失败恢复 + final originalTag = kr_cutTag.value; + + // 2. 设置切换中状态 kr_cutTag.value = tag; - kr_currentNodeName.value = tag; + kr_currentNodeLatency.value = -1; // 切换中状态 + kr_isLatency.value = true; // 显示加载动画 - // 更新当前选中的标签 - kr_cutSeletedTag.value = tag; - - // 更新连接信息 - kr_updateConnectionInfo(); - - // 🔧 修复:只有在核心已启动时才选择节点,避免触发重启 - if (KRSingBoxImp.instance.kr_status.value == SingboxStarted()) { - print('🔵 节点已选择且VPN正在运行,切换到: $tag'); - // 🔧 关键修复:切换节点时设置为-1(切换中) - kr_currentNodeLatency.value = -1; - - // 🔧 关键修复:使用 await 等待节点切换完成 - try { - await KRSingBoxImp.instance.kr_selectOutbound(tag); - print('🔵 节点切换命令已执行完成: $tag'); - } catch (e) { - KRLogUtil.kr_e('❌ 节点切换失败: $e', tag: 'HomeController'); - print('🔵 节点切换失败: $e'); - } - - // 🔧 修复:选择节点后启动延迟值更新(带超时保护) - Future.delayed(const Duration(milliseconds: 500), () { - if (kr_currentNodeLatency.value == -1 && kr_isConnected.value) { - KRLogUtil.kr_w('⚠️ 选择节点后延迟值未更新,尝试手动更新', tag: 'HomeController'); - if (!_kr_tryUpdateDelayFromActiveGroups()) { - kr_currentNodeLatency.value = 0; - kr_currentNodeLatency.refresh(); - } - } - }); - } else { - // 🔧 关键修复:核心未启动时,根据连接状态设置延迟 - print('🔵 节点已选择但VPN未运行: $tag, 当前状态=${KRSingBoxImp.instance.kr_status.value}'); - if (kr_isConnected.value) { - // 如果显示已连接但实际未运行,重置状态 - kr_currentNodeLatency.value = -2; - } else { - // 正常未连接状态 - kr_currentNodeLatency.value = -2; - } - kr_currentNodeLatency.refresh(); - - // 🔧 修复:核心未启动时,仍需保存用户选择,以便启动VPN时应用 - KRLogUtil.kr_i('💾 核心未启动,保存节点选择以便稍后应用: $tag', tag: 'HomeController'); - KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: tag).then((_) { - KRLogUtil.kr_i('✅ 节点选择已保存: $tag', tag: 'HomeController'); - }).catchError((e) { - KRLogUtil.kr_e('❌ 保存节点选择失败: $e', tag: 'HomeController'); - }); + // 3. 如果VPN未连接,只更新UI变量即可 + if (!kr_isConnected.value) { + KRLogUtil.kr_i('📴 VPN未连接,只更新UI变量: $tag', tag: 'HomeController'); + kr_cutSeletedTag.value = tag; + kr_updateConnectionInfo(); + kr_moveToSelectedNode(); + KRLogUtil.kr_i('✅ 节点选择已保存: $tag', tag: 'HomeController'); + return true; } - // 移动到选中的节点 - // kr_moveToSelectedNode(); + // 4. VPN已连接,需要通知后台进行节点切换 + try { + KRLogUtil.kr_i('🔌 VPN已连接,开始切换后台节点: $tag', tag: 'HomeController'); + + // 等待后台节点切换完成(关键!) + await KRSingBoxImp.instance.kr_selectOutbound(tag); + + // 后台切换成功,更新UI + kr_cutSeletedTag.value = tag; + kr_updateConnectionInfo(); + kr_moveToSelectedNode(); + + KRLogUtil.kr_i('✅ 节点切换成功: $tag', tag: 'HomeController'); + return true; + + } catch (switchError) { + // 后台切换失败,恢复到原节点 + KRLogUtil.kr_e('❌ 后台节点切换失败: $switchError', tag: 'HomeController'); + + // 恢复原状态 + kr_cutTag.value = originalTag; + kr_currentNodeLatency.value = -2; // 恢复为未连接状态 + + // 显示错误提示给用户 + KRCommonUtil.kr_showToast('节点切换失败,已恢复为: $originalTag'); + + return false; + } } catch (e) { - KRLogUtil.kr_e('选择节点失败: $e', tag: 'HomeController'); - // 🔧 修复:选择节点失败时,根据连接状态设置合适的延迟值 - if (kr_isConnected.value) { - kr_currentNodeLatency.value = 0; - } else { - kr_currentNodeLatency.value = -2; - } + KRLogUtil.kr_e('❌ 节点切换异常: $e', tag: 'HomeController'); + kr_isLatency.value = false; + KRCommonUtil.kr_showToast('节点切换异常,请重试'); + return false; + } finally { + // 关闭加载状态 + kr_isLatency.value = false; + KRLogUtil.kr_i('🔄 节点切换流程完成', tag: 'HomeController'); } } + /// 🔧 修复:简化的 kr_selectNode 方法 + /// 现在只是委托给新的 kr_performNodeSwitch 方法 + /// 为了保持向后兼容,保留此方法但改为调用新方法 + Future kr_selectNode(String tag) async { + return await kr_performNodeSwitch(tag); + } + /// 获取当前节点国家 String kr_getCurrentNodeCountry() { if (kr_cutSeletedTag.isEmpty) return ''; diff --git a/lib/app/modules/kr_home/views/kr_home_node_list_view.dart b/lib/app/modules/kr_home/views/kr_home_node_list_view.dart index d401b86..bee56ea 100755 --- a/lib/app/modules/kr_home/views/kr_home_node_list_view.dart +++ b/lib/app/modules/kr_home/views/kr_home_node_list_view.dart @@ -278,14 +278,27 @@ class KRHomeNodeListView extends GetView { return Column( children: [ InkWell( - onTap: () { - print(server.tag); - KRSingBoxImp.instance - .kr_selectOutbound(server.tag); - controller.kr_selectNode(server.tag); - // 添加状态切换,回到默认状态 - controller.kr_currentListStatus.value = - KRHomeViewsListStatus.kr_none; + // 🔧 修复:改为 async,等待节点切换完成后再关闭列表 + onTap: () async { + try { + print('🔄 用户点击节点: ${server.tag}'); + // 使用统一的节点切换方法,等待完成 + final success = await controller + .kr_performNodeSwitch(server.tag); + + // 只有切换成功才关闭列表 + if (success) { + controller.kr_currentListStatus.value = + KRHomeViewsListStatus.kr_none; + print('✅ 节点切换成功,关闭列表'); + } else { + print('❌ 节点切换失败,列表保持打开'); + } + } catch (e) { + print('❌ 节点切换异常: $e'); + KRLogUtil.kr_e('节点切换异常: $e', + tag: 'NodeListView'); + } }, child: Container( padding: EdgeInsets.symmetric( @@ -362,13 +375,26 @@ class KRHomeNodeListView extends GetView { return Column( children: [ InkWell( - onTap: () { - KRLogUtil.kr_i(server.tag); - KRSingBoxImp.instance.kr_selectOutbound(server.tag); - controller.kr_selectNode(server.tag); - // 添加状态切换,回到默认状态 - controller.kr_currentListStatus.value = - KRHomeViewsListStatus.kr_none; + // 🔧 修复:改为 async,等待节点切换完成后再关闭列表 + onTap: () async { + try { + KRLogUtil.kr_i('🔄 用户点击节点: ${server.tag}'); + // 使用统一的节点切换方法,等待完成 + final success = await controller + .kr_performNodeSwitch(server.tag); + + // 只有切换成功才关闭列表 + if (success) { + controller.kr_currentListStatus.value = + KRHomeViewsListStatus.kr_none; + KRLogUtil.kr_i('✅ 节点切换成功,关闭列表'); + } else { + KRLogUtil.kr_w('❌ 节点切换失败,列表保持打开'); + } + } catch (e) { + KRLogUtil.kr_e('❌ 节点切换异常: $e', + tag: 'NodeListView'); + } }, child: Container( padding: EdgeInsets.symmetric(vertical: 4.w), @@ -735,10 +761,19 @@ class KRHomeNodeListView extends GetView { ), // Auto 选项 InkWell( - onTap: () { - controller.kr_selectNode('auto'); - controller.kr_currentListStatus.value = - KRHomeViewsListStatus.kr_none; + // 🔧 修复:改为 async,等待节点切换完成后再关闭列表 + onTap: () async { + try { + final success = + await controller.kr_performNodeSwitch('auto'); + if (success) { + controller.kr_currentListStatus.value = + KRHomeViewsListStatus.kr_none; + } + } catch (e) { + KRLogUtil.kr_e('Auto选项切换异常: $e', + tag: 'NodeListView'); + } }, child: Container( padding: EdgeInsets.symmetric(vertical: 8.w), @@ -877,12 +912,22 @@ class KRHomeNodeListView extends GetView { .map((node) => Column( children: [ InkWell( - onTap: () { - KRLogUtil.kr_i(node.tag); - KRSingBoxImp.instance.kr_selectOutbound(node.tag); - controller.kr_selectNode(node.tag); - controller.kr_currentListStatus.value = - KRHomeViewsListStatus.kr_none; + // 🔧 修复:改为 async,等待节点切换完成后再关闭列表 + onTap: () async { + try { + KRLogUtil.kr_i( + '🔄 用户点击节点: ${node.tag}'); + final success = await controller + .kr_performNodeSwitch(node.tag); + if (success) { + controller.kr_currentListStatus.value = + KRHomeViewsListStatus.kr_none; + } + } catch (e) { + KRLogUtil.kr_e( + '节点切换异常: $e', + tag: 'NodeListView'); + } }, child: Container( padding: EdgeInsets.symmetric(vertical: 4.w),