feat: 更新代码仓库全部修改
Build Android APK / 编译 libcore.aar (push) Has been cancelled
Build Android APK / 编译 Android APK (release) (push) Has been cancelled
Build Android APK / 创建 GitHub Release (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Android) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Windows) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (macOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Linux) (push) Has been cancelled
Build Multi-Platform / 构建 Android APK (push) Has been cancelled
Build Multi-Platform / 构建 Windows (push) Has been cancelled
Build Multi-Platform / 构建 macOS (push) Has been cancelled
Build Multi-Platform / 构建 Linux (push) Has been cancelled
Build Multi-Platform / 创建 Release (push) Has been cancelled
Build Windows / build (push) Has been cancelled
Build Android APK / 编译 libcore.aar (push) Has been cancelled
Build Android APK / 编译 Android APK (release) (push) Has been cancelled
Build Android APK / 创建 GitHub Release (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Android) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Windows) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (macOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Linux) (push) Has been cancelled
Build Multi-Platform / 构建 Android APK (push) Has been cancelled
Build Multi-Platform / 构建 Windows (push) Has been cancelled
Build Multi-Platform / 构建 macOS (push) Has been cancelled
Build Multi-Platform / 构建 Linux (push) Has been cancelled
Build Multi-Platform / 创建 Release (push) Has been cancelled
Build Windows / build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../controllers/hi_node_list_controller.dart';
|
||||
|
||||
class HiNodeListBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<HINodeListController>(
|
||||
() => HINodeListController(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||
import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart';
|
||||
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
|
||||
import '../../../localization/app_translations.dart';
|
||||
import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart';
|
||||
|
||||
class HINodeListController extends GetxController {
|
||||
/// 订阅服务
|
||||
final KRSubscribeService kr_subscribeService = KRSubscribeService();
|
||||
|
||||
/// 首页服务
|
||||
final KRHomeController homeController = Get.find<KRHomeController>();
|
||||
|
||||
/// 获取连接类型字符串
|
||||
String kr_getConnectionTypeString() {
|
||||
final connectionType = KRSingBoxImp.instance.kr_connectionType.value;
|
||||
switch (connectionType) {
|
||||
case KRConnectionType.global:
|
||||
return AppTranslations.kr_setting.connectionTypeGlobal;
|
||||
case KRConnectionType.rule:
|
||||
return AppTranslations.kr_setting.connectionTypeRule;
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新连接类型
|
||||
void kr_updateConnectionType(KRConnectionType type) {
|
||||
KRSingBoxImp.instance.kr_updateConnectionType(type);
|
||||
KRLogUtil.kr_i('连接类型已更新为: $type', tag: 'HINodeListController');
|
||||
|
||||
// 如果当前有选择的国家,触发重选检查
|
||||
if (homeController.currentSelectedCountry.isNotEmpty) {
|
||||
KRLogUtil.kr_i('连接类型更新后,检查是否需要重选节点', tag: 'HINodeListController');
|
||||
// 延迟一下让连接类型更新完成
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
homeController.checkCountryReselection(KRSingBoxImp.instance.kr_activeGroups);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据国家选择最快的节点
|
||||
void kr_selectFastestNodeByCountry(String country) {
|
||||
KRLogUtil.kr_i('开始为国家 $country 选择最快节点', tag: 'HINodeListController');
|
||||
|
||||
// 通知kr_home当前选择的国家
|
||||
homeController.setCurrentSelectedCountry(country);
|
||||
|
||||
// 查找国家分组
|
||||
final countryGroup = kr_subscribeService.countryOutboundList
|
||||
.firstWhereOrNull((group) => group.country == country);
|
||||
|
||||
if (countryGroup == null) {
|
||||
KRLogUtil.kr_w('未找到国家分组: $country', tag: 'HINodeListController');
|
||||
return;
|
||||
}
|
||||
|
||||
if (countryGroup.outboundList.isEmpty) {
|
||||
KRLogUtil.kr_w('国家 $country 的节点列表为空', tag: 'HINodeListController');
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找延迟最小的有效节点
|
||||
String? fastestNodeTag;
|
||||
int minDelay = 65535;
|
||||
int validNodeCount = 0;
|
||||
|
||||
for (var node in countryGroup.outboundList) {
|
||||
final delay = node.urlTestDelay.value;
|
||||
|
||||
// 统计有效节点数量
|
||||
if (delay < 65535 && delay > 0) {
|
||||
validNodeCount++;
|
||||
|
||||
// 找到延迟更小的节点
|
||||
if (delay < minDelay) {
|
||||
minDelay = delay;
|
||||
fastestNodeTag = node.tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('国家 $country 内有 $validNodeCount 个有效节点可供切换', tag: 'HINodeListController');
|
||||
|
||||
// 选择最快的节点,如果没有有效延迟则选择第一个
|
||||
final selectedTag = fastestNodeTag ?? countryGroup.outboundList.first.tag;
|
||||
|
||||
KRLogUtil.kr_i('选择节点: $selectedTag (延迟: ${minDelay == 65535 ? "未知" : "${minDelay}ms"})',
|
||||
tag: 'HINodeListController');
|
||||
|
||||
// 调用homeController的选择节点方法
|
||||
homeController.kr_selectNode(selectedTag);
|
||||
}
|
||||
|
||||
/// 获取当前国家内的有效节点标签列表
|
||||
List<String> getValidNodesInCurrentCountry() {
|
||||
final country = homeController.currentSelectedCountry.value;
|
||||
if (country.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final countryGroup = kr_subscribeService.countryOutboundList
|
||||
.firstWhereOrNull((group) => group.country == country);
|
||||
|
||||
if (countryGroup == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return countryGroup.outboundList
|
||||
.where((node) => node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0)
|
||||
.map((node) => node.tag)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 手动触发当前国家内的重选
|
||||
void manualReselection() {
|
||||
final country = homeController.currentSelectedCountry.value;
|
||||
if (country.isEmpty) {
|
||||
KRLogUtil.kr_w('没有选择国家,无法进行重选', tag: 'HINodeListController');
|
||||
return;
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('用户手动触发国家内重选', tag: 'HINodeListController');
|
||||
homeController.performCountryReselection(country);
|
||||
}
|
||||
|
||||
/// 获取当前国家内节点的延迟统计(委托给homeController)
|
||||
Map<String, dynamic> getCurrentCountryLatencyStats() {
|
||||
return homeController.getCurrentCountryNodeStats();
|
||||
}
|
||||
|
||||
/// 启用/禁用动态重选功能(委托给homeController)
|
||||
void setDynamicReselectionEnabled(bool enabled) {
|
||||
homeController.setCountryReselectionEnabled(enabled);
|
||||
}
|
||||
|
||||
/// 获取动态重选状态信息
|
||||
Map<String, dynamic> getDynamicReselectionStatus() {
|
||||
return {
|
||||
'enabled': homeController.isCountryReselectionEnabled.value,
|
||||
'currentCountry': homeController.currentSelectedCountry.value,
|
||||
'latencyThreshold': homeController.countryReselectionLatencyThreshold,
|
||||
};
|
||||
}
|
||||
}
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
// hi_node_list_view.dart
|
||||
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/kr_country_flag.dart';
|
||||
import '../../../model/business/kr_outbound_item.dart';
|
||||
import '../../../utils/kr_log_util.dart';
|
||||
import '../controllers/hi_node_list_controller.dart';
|
||||
import 'package:kaer_with_panels/app/modules/kr_home/models/kr_home_views_status.dart';
|
||||
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
|
||||
|
||||
/// 节点列表视图组件
|
||||
/// 职责:只负责根据状态渲染不同的列表,不包含任何外部容器、背景或标题。
|
||||
/// 布局控制权完全交给父组件。
|
||||
class HINodeListView extends GetView<HINodeListController> {
|
||||
const HINodeListView({super.key});
|
||||
|
||||
// 定义颜色常量
|
||||
static const Color krModernGreen = Color(0xFF4CAF50);
|
||||
static const Color krModernGreenLight = Color(0xFF81C784);
|
||||
|
||||
// 存储随机延迟值,用于UI展示
|
||||
static final Map<String, int> _fakeDelays = {};
|
||||
|
||||
/// 获取用于显示的延迟值
|
||||
int _getDisplayDelay(HINodeListController controller, KROutboundItem item) {
|
||||
if (controller.homeController.kr_isConnected.value) {
|
||||
return item.urlTestDelay.value;
|
||||
}
|
||||
if (!_fakeDelays.containsKey(item.tag)) {
|
||||
final random = Random();
|
||||
_fakeDelays[item.tag] = 30 + random.nextInt(71); // 30-100ms
|
||||
}
|
||||
return _fakeDelays[item.tag] ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 1. 使用 Material 作为根组件,确保 InkWell 的水波纹效果正常
|
||||
// 并设置透明背景,让父组件的背景可以透出来
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
// child: _buildSubscribeList(context)
|
||||
child: _kr_buildRegionList(context)
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建国家/地区分组列表
|
||||
Widget _kr_buildRegionList(BuildContext context) {
|
||||
return Obx(() {
|
||||
return _kr_buildListContainer(
|
||||
context,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.w),
|
||||
// 2. 使用 children 属性,并一次性构建所有列表项
|
||||
children: [
|
||||
if (controller.kr_subscribeService.countryOutboundList.isEmpty)
|
||||
_buildEmptyListPlaceholder(context, AppTranslations.kr_home.noRegions)
|
||||
else ...[
|
||||
InkWell(
|
||||
onTap: () {
|
||||
controller.homeController.kr_selectNode('auto');
|
||||
controller.homeController.kr_currentListStatus.value =
|
||||
KRHomeViewsListStatus.kr_none;
|
||||
controller.homeController.kr_coutryText.value = 'auto';
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 12.w, horizontal: 16.w),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 32.w,
|
||||
height: 22.w,
|
||||
decoration: BoxDecoration(
|
||||
// 2. 设置背景色为主题色
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
// 4. 使用 Center 来确保内部的图片水平和垂直居中
|
||||
child: Center(
|
||||
child: KrLocalImage(
|
||||
imageName: "hi-home-logo",
|
||||
width: 14.w,
|
||||
height: 14.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'自动匹配最快网络', // 您指定的文本
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600, // 600 加粗
|
||||
),
|
||||
),
|
||||
// 2. 第二个 Text: "根据网络IP自动匹配最快线路"
|
||||
Text(
|
||||
'根据网络IP自动匹配最快线路', // 您指定的文本
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Obx(() => controller.homeController.kr_coutryText.value == 'auto'
|
||||
? KrLocalImage(
|
||||
imageName: 'radio-active-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)
|
||||
: KrLocalImage(
|
||||
imageName: 'radio-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
...controller.kr_subscribeService.countryOutboundList.map((country) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
// 选择国家
|
||||
controller.homeController.kr_coutryText.value = country.country;
|
||||
// 自动选择这个国家下的节点延迟中最快的
|
||||
controller.kr_selectFastestNodeByCountry(country.country);
|
||||
},
|
||||
child: _kr_buildCountryListItem(context, country: country),
|
||||
);
|
||||
}).toList(),
|
||||
]
|
||||
] //
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
/// 构建默认的订阅节点列表
|
||||
Widget _buildSubscribeList(BuildContext context) {
|
||||
return Obx(() {
|
||||
|
||||
// 自动触发延迟测试(仅在未连接状态下)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!controller.homeController.kr_isConnected.value && !controller.homeController.kr_isLatency.value) {
|
||||
KRLogUtil.kr_i('🔄 节点列表显示 - 自动触发延迟测试', tag: 'HINodeListView');
|
||||
controller.homeController.kr_urlTest();
|
||||
}
|
||||
});
|
||||
return _kr_buildListContainer(
|
||||
context,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.w),
|
||||
// 2. 使用 children 属性,并一次性构建所有列表项
|
||||
children: [
|
||||
if (controller.kr_subscribeService.allList.isEmpty)
|
||||
_buildEmptyListPlaceholder(context, AppTranslations.kr_home.noNodes)
|
||||
else ...[
|
||||
InkWell(
|
||||
onTap: () {
|
||||
controller.homeController.kr_selectNode('auto');
|
||||
controller.homeController.kr_currentListStatus.value =
|
||||
KRHomeViewsListStatus.kr_none;
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 12.w, horizontal: 16.w),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 32.w,
|
||||
height: 22.w,
|
||||
decoration: BoxDecoration(
|
||||
// 2. 设置背景色为主题色
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
// 4. 使用 Center 来确保内部的图片水平和垂直居中
|
||||
child: Center(
|
||||
child: KrLocalImage(
|
||||
imageName: "hi-home-logo",
|
||||
width: 14.w,
|
||||
height: 14.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'自动匹配最快网络', // 您指定的文本
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600, // 600 加粗
|
||||
),
|
||||
),
|
||||
// 2. 第二个 Text: "根据网络IP自动匹配最快线路"
|
||||
Text(
|
||||
'根据网络IP自动匹配最快线路', // 您指定的文本
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Obx(() => controller.homeController.kr_cutTag.value == 'auto'
|
||||
? KrLocalImage(
|
||||
imageName: 'radio-active-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)
|
||||
: KrLocalImage(
|
||||
imageName: 'radio-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
...controller.kr_subscribeService.allList.map((item) {
|
||||
return InkWell(
|
||||
onTap: () => _onNodeSelected(item),
|
||||
child: _kr_buildNodeListItem(context, item: item),
|
||||
);
|
||||
}).toList(),
|
||||
]
|
||||
] //
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 列表为空时的占位符
|
||||
Widget _buildEmptyListPlaceholder(BuildContext context, String text) {
|
||||
return Container(
|
||||
height: 400.w, // 这是一个示例值,你可能需要根据实际屏幕高度来计算
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
text,
|
||||
style: KrAppTextStyle(fontSize: 14, color: Theme.of(context).textTheme.bodySmall?.color),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 列表的白色圆角背景容器
|
||||
Widget _kr_buildListContainer(BuildContext context, {required Widget child}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
/// 节点选中时的通用处理逻辑
|
||||
void _onNodeSelected(KROutboundItem item) {
|
||||
KRLogUtil.kr_i('Node selected: ${item.tag}');
|
||||
KRSingBoxImp.instance.kr_selectOutbound(item.tag);
|
||||
controller.homeController.kr_selectNode(item.tag);
|
||||
// 切换回主页的仪表盘视图
|
||||
controller.homeController.kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
|
||||
}
|
||||
|
||||
/// 构建单个节点列表项的UI
|
||||
Widget _kr_buildNodeListItem(BuildContext context, {required KROutboundItem item}) {
|
||||
return Container(
|
||||
key: ValueKey(item.id),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
// 2. 将颜色设置为白色,并添加 30% 的透明度
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
width: 1.0, // 边框宽度 1px
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 12.w, horizontal: 16.w),
|
||||
child: Row(
|
||||
children: [
|
||||
KRCountryFlag(countryCode: item.country, width: 30.w, height: 20.w, isCircle: false, maintainSize: false),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
controller.homeController.kr_getCountryFullName(item.country),
|
||||
style: KrAppTextStyle(fontSize: 14, color: Colors.white),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
Obx(() => controller.homeController.kr_cutTag.value == item.tag
|
||||
? Container(
|
||||
margin: EdgeInsets.only(left: 4.w),
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 1.w),
|
||||
decoration: BoxDecoration(
|
||||
color: krModernGreenLight.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(4.w),
|
||||
),
|
||||
child: Text(
|
||||
AppTranslations.kr_home.selected,
|
||||
style: KrAppTextStyle(fontSize: 10, color: krModernGreen, fontWeight: FontWeight.w500),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink()),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 2.w),
|
||||
Obx(() {
|
||||
// 2. 获取用于显示的延迟值
|
||||
final int delay = _getDisplayDelay(controller, item);
|
||||
// 3. 根据延迟值显示不同内容
|
||||
// if (delay <= 0) {
|
||||
// // 如果延迟为0或负数(初始状态或测试失败),则不显示
|
||||
// return const SizedBox.shrink();
|
||||
// }
|
||||
// 4. 显示延迟值,并添加 "ms" 单位
|
||||
return Text(
|
||||
'${delay}ms',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 10,
|
||||
color: krModernGreen,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
);
|
||||
}),
|
||||
Text(
|
||||
item.city,
|
||||
style: KrAppTextStyle(fontSize: 12, color: Theme.of(context).textTheme.bodySmall?.color),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Obx(() => controller.homeController.kr_cutTag.value == item.tag
|
||||
? KrLocalImage(
|
||||
imageName: 'radio-active-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)
|
||||
: KrLocalImage(
|
||||
imageName: 'radio-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建国家列表项的UI
|
||||
Widget _kr_buildCountryListItem(BuildContext context, {required country}) {
|
||||
return Container(
|
||||
key: ValueKey(country),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
// 2. 将颜色设置为白色,并添加 30% 的透明度
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
width: 1.0, // 边框宽度 1px
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 12.w, horizontal: 16.w),
|
||||
child: Row(
|
||||
children: [
|
||||
KRCountryFlag(countryCode: country.country, width: 30.w, height: 20.w, isCircle: false, maintainSize: false),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
controller.homeController.kr_getCountryFullName(country.country),
|
||||
style: KrAppTextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
),
|
||||
),
|
||||
Obx(() => controller.homeController.kr_coutryText.value == country.country
|
||||
? KrLocalImage(
|
||||
imageName: 'radio-active-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)
|
||||
: KrLocalImage(
|
||||
imageName: 'radio-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 16.w, // 适当缩小 SVG 尺寸,留出白边
|
||||
height: 16.h,
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import './hi_node_list_view.dart';
|
||||
import '../controllers/hi_node_list_controller.dart';
|
||||
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
|
||||
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/hi_help_entrance.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
class HINodePageView extends GetView<HINodeListController> {
|
||||
const HINodePageView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HIBaseScaffold(
|
||||
child: Stack(
|
||||
children: [
|
||||
// 主要内容区域
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 模式切换器
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 100.w, right: 60.w),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// 2. 关键:移除多余的 SizedBox,让 Expanded 正确工作
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(4.w),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).primaryColor,
|
||||
width: 2.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(100.r),
|
||||
),
|
||||
child: Obx(() => Row(
|
||||
children: [
|
||||
// 3. 关键:修正按钮的点击逻辑和样式逻辑
|
||||
_buildModeButton(
|
||||
context,
|
||||
title: AppTranslations.kr_setting.connectionTypeRule,
|
||||
isSelected: KRSingBoxImp().kr_connectionType.value == KRConnectionType.rule,
|
||||
onTap: () => controller.kr_updateConnectionType(KRConnectionType.rule),
|
||||
),
|
||||
_buildModeButton(
|
||||
context,
|
||||
title: AppTranslations.kr_setting.connectionTypeGlobal,
|
||||
isSelected: KRSingBoxImp().kr_connectionType.value == KRConnectionType.global,
|
||||
onTap: () => controller.kr_updateConnectionType(KRConnectionType.global),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
HIDialog.show(
|
||||
customMessageWidget: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 0.w, vertical: 0.w),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${AppTranslations.kr_setting.connectionTypeRule}:',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.sp, color: Colors.black),
|
||||
),
|
||||
TextSpan(
|
||||
text: AppTranslations.kr_setting.connectionTypeRuleRemark,
|
||||
style: TextStyle(fontSize: 14.sp, color: Colors.black54),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.w),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${AppTranslations.kr_setting.connectionTypeGlobal}:',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.sp, color: Colors.black),
|
||||
),
|
||||
TextSpan(
|
||||
text: AppTranslations.kr_setting.connectionTypeGlobalRemark,
|
||||
style: TextStyle(fontSize: 14.sp, color: Colors.black54),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: KrLocalImage(
|
||||
imageName: 'question-icon',
|
||||
imageType: ImageType.svg,
|
||||
width: 30.w,
|
||||
height: 30.w,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.w),
|
||||
|
||||
// 4. 关键:将列表用 Expanded 包裹,使其自适应填充剩余空间,并为HIHelpEntrance预留空间
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 60.w, right: 60.w, bottom: 90.w), // 为HIHelpEntrance预留空间
|
||||
// 2. 在 Padding 内部使用 EasyRefresh
|
||||
child: EasyRefresh(
|
||||
// 3. 绑定 onRefresh 回调
|
||||
onRefresh: controller.kr_subscribeService.kr_refreshAll,
|
||||
// 4. 自定义 Header 样式
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '正在刷新...',
|
||||
processingText: '正在刷新...',
|
||||
processedText: '刷新成功',
|
||||
failedText: '刷新失败',
|
||||
messageText: '最后更新于 %T',
|
||||
textStyle: TextStyle(color: Colors.white.withOpacity(0.7)),
|
||||
messageStyle: TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 12.sp),
|
||||
iconTheme: IconThemeData(color: Colors.white.withOpacity(0.7)),
|
||||
),
|
||||
// 5. 将 HINodeListView 作为其 child
|
||||
child: HINodeListView(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 底部帮助入口
|
||||
const HIHelpEntrance(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildModeButton(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required bool isSelected,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: EdgeInsets.symmetric(vertical: 4.w),
|
||||
decoration: BoxDecoration(
|
||||
// 3. 关键:修正颜色逻辑
|
||||
color: isSelected
|
||||
? Theme.of(context).primaryColor // 选中时:主题色背景
|
||||
: Colors.transparent, // 未选中时:透明背景
|
||||
borderRadius: BorderRadius.circular(100.r),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isSelected
|
||||
? Colors.black // 选中时:黑色文字
|
||||
: Theme.of(context).primaryColor, // 未选中时:主题色文字
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user