From 01a4d1a81e15299f00a8cb6d965fc04c06b6eb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=9A=E5=81=9A=E9=A5=AD=E7=9A=84=E4=BA=8C=E5=93=88?= Date: Tue, 18 Mar 2025 16:28:59 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=B0=86=E5=8E=9F=E6=9C=89=E7=9A=84Provid?= =?UTF-8?q?er=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E4=B8=BARiverpod=EF=BC=8C=E6=8F=90=E5=8D=87=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=95=88=E7=8E=87=202.=20=E9=87=8D=E6=9E=84A?= =?UTF-8?q?ppConfig=E7=B1=BB=EF=BC=8C=E4=BD=BF=E7=94=A8Hive=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=8C=81=E4=B9=85=E5=8C=96=E5=AD=98=E5=82=A8=203.=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9EAdvancedConfig=E7=B1=BB=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E7=AE=A1=E7=90=86=E9=AB=98=E7=BA=A7=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9=204.=20=E4=BC=98=E5=8C=96=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=92=8C=E8=8A=82=E7=82=B9=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9A=84=E7=AE=A1=E7=90=86=E9=80=BB=E8=BE=91=205.=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=A4=9A=E4=B8=AAProvider=E6=9D=A5=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E6=A8=A1=E5=9D=97=E7=9A=84=E7=8A=B6=E6=80=81?= =?UTF-8?q?=206.=20=E6=9B=B4=E6=96=B0=E4=BE=9D=E8=B5=96=E9=A1=B9=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0Riverpod=E5=8F=8A=E7=9B=B8=E5=85=B3=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app.dart | 6 +- lib/config/app_config.dart | 1035 ++++++++--- lib/main.dart | 5 +- lib/screens/front.dart | 256 ++- lib/screens/kvroom.dart | 27 +- lib/screens/settings.dart | 1602 ++++++++++++++--- lib/src/rust/api/simple.dart | 103 +- lib/src/rust/frb_generated.dart | 150 +- lib/src/rust/frb_generated.io.dart | 27 + lib/src/rust/frb_generated.web.dart | 27 + lib/utils/kv_state.dart | 470 ++++- lib/utils/serverjs.dart | 198 ++ pubspec.lock | 469 +++-- pubspec.yaml | 11 +- rust/easytier/src/common/constants.rs | 4 +- rust/src/api/simple.rs | 53 +- rust/src/frb_generated.rs | 124 ++ rust_builder/cargokit/build_tool/pubspec.lock | 116 +- 18 files changed, 3857 insertions(+), 826 deletions(-) create mode 100644 lib/utils/serverjs.dart diff --git a/lib/app.dart b/lib/app.dart index 0ba8d36..504656c 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -4,7 +4,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:system_tray/system_tray.dart'; import 'dart:io'; import 'screens/Home.dart'; -import 'config/themeconfiguration.dart'; +import 'config/themeconfiguration.dart' as theme_config; import 'config/app_config.dart'; // 定义应用程序的主要StatefulWidget @@ -114,7 +114,7 @@ class _MyAppState extends State { ], // Insert this line supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")], - theme: ThemeConfig.getLightTheme( + theme: theme_config.ThemeConfig.getLightTheme( useMaterial3: useMaterial3, seedColor: _seedColor, ).copyWith( @@ -124,7 +124,7 @@ class _MyAppState extends State { primaryTextTheme: Typography.material2021().black.apply( fontFamily: 'MiSans', )), - darkTheme: ThemeConfig.getDarkTheme( + darkTheme: theme_config.ThemeConfig.getDarkTheme( useMaterial3: useMaterial3, seedColor: _seedColor, ).copyWith( diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index de1c5bb..fc02bd1 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -1,297 +1,872 @@ import 'package:flutter/material.dart'; import 'dart:io'; import 'package:path/path.dart' as path; -import 'cof.dart'; +import 'package:hive/hive.dart'; +import 'package:hive_flutter/hive_flutter.dart'; +// 配置模型基类 +abstract class ConfigModel { + Map toJson(); + String get configKey; // 配置在存储中的键名 +} + +// 添加高级配置类 +class AdvancedConfig implements ConfigModel { + final String defaultProtocol; + final String devName; + final bool enableEncryption; + final bool enableIpv6; + final int mtu; + final bool latencyFirst; + final bool enableExitNode; + final bool proxyForwardBySystem; + final bool noTun; + final bool useSmoltcp; + final String relayNetworkWhitelist; + final bool disableP2p; + final bool relayAllPeerRpc; + final bool disableUdpHolePunching; + final bool multiThread; + final String dataCompressAlgo; + final bool bindDevice; + final bool enableKcpProxy; + final bool disableKcpInput; + final bool disableRelayKcp; + + @override + String get configKey => 'advanced'; + + AdvancedConfig({ + this.defaultProtocol = "tcp", + this.devName = "", + this.enableEncryption = true, + this.enableIpv6 = true, + this.mtu = 1380, + this.latencyFirst = false, + this.enableExitNode = false, + this.proxyForwardBySystem = false, + this.noTun = false, + this.useSmoltcp = false, + this.relayNetworkWhitelist = "*", + this.disableP2p = false, + this.relayAllPeerRpc = false, + this.disableUdpHolePunching = false, + this.multiThread = true, + this.dataCompressAlgo = "None", + this.bindDevice = true, + this.enableKcpProxy = false, + this.disableKcpInput = false, + this.disableRelayKcp = true, + }); + + factory AdvancedConfig.fromJson(Map json) { + return AdvancedConfig( + defaultProtocol: json['defaultProtocol'] ?? "tcp", + devName: json['devName'] ?? "", + enableEncryption: json['enableEncryption'] ?? true, + enableIpv6: json['enableIpv6'] ?? true, + mtu: json['mtu'] ?? 1380, + latencyFirst: json['latencyFirst'] ?? false, + enableExitNode: json['enableExitNode'] ?? false, + proxyForwardBySystem: json['proxyForwardBySystem'] ?? false, + noTun: json['noTun'] ?? false, + useSmoltcp: json['useSmoltcp'] ?? false, + relayNetworkWhitelist: json['relayNetworkWhitelist'] ?? "*", + disableP2p: json['disableP2p'] ?? false, + relayAllPeerRpc: json['relayAllPeerRpc'] ?? false, + disableUdpHolePunching: json['disableUdpHolePunching'] ?? false, + multiThread: json['multiThread'] ?? true, + dataCompressAlgo: json['dataCompressAlgo'] ?? "None", + bindDevice: json['bindDevice'] ?? true, + enableKcpProxy: json['enableKcpProxy'] ?? false, + disableKcpInput: json['disableKcpInput'] ?? false, + disableRelayKcp: json['disableRelayKcp'] ?? true, + ); + } + + @override + Map toJson() => { + 'defaultProtocol': defaultProtocol, + 'devName': devName, + 'enableEncryption': enableEncryption, + 'enableIpv6': enableIpv6, + 'mtu': mtu, + 'latencyFirst': latencyFirst, + 'enableExitNode': enableExitNode, + 'proxyForwardBySystem': proxyForwardBySystem, + 'noTun': noTun, + 'useSmoltcp': useSmoltcp, + 'relayNetworkWhitelist': relayNetworkWhitelist, + 'disableP2p': disableP2p, + 'relayAllPeerRpc': relayAllPeerRpc, + 'disableUdpHolePunching': disableUdpHolePunching, + 'multiThread': multiThread, + 'dataCompressAlgo': dataCompressAlgo, + 'bindDevice': bindDevice, + 'enableKcpProxy': enableKcpProxy, + 'disableKcpInput': disableKcpInput, + 'disableRelayKcp': disableRelayKcp, + }; +} + +// 配置模型类 - 保持原有结构 +class ThemeConfig implements ConfigModel { + final String mode; + final int seedColor; + + @override + String get configKey => 'theme'; + + ThemeConfig({ + this.mode = 'system', + this.seedColor = 0xFF2196F3, // Colors.blue.value + }); + + factory ThemeConfig.fromJson(Map json) { + return ThemeConfig( + mode: json['mode'] ?? 'system', + seedColor: json['seedColor'] ?? 0xFF2196F3, + ); + } + + @override + Map toJson() => { + 'mode': mode, + 'seedColor': seedColor, + }; +} + +class ServerConfig implements ConfigModel { + final String url; + final String name; + final bool selected; + final bool tcp; + final bool udp; + final bool ws; + final bool wss; + final bool quic; + + @override + String get configKey => 'server'; + + ServerConfig({ + required this.url, + required this.name, + this.selected = false, + this.tcp = true, + this.udp = true, + this.ws = false, + this.wss = false, + this.quic = false, + }); + + factory ServerConfig.fromJson(Map json) { + return ServerConfig( + url: json['url'] ?? '', + name: json['name'] ?? '', + selected: json['selected'] ?? false, + tcp: json['tcp'] ?? true, + udp: json['udp'] ?? true, + ws: json['ws'] ?? false, + wss: json['wss'] ?? false, + quic: json['quic'] ?? false, + ); + } + + Map toJson() => { + 'url': url, + 'name': name, + 'selected': selected, + 'tcp': tcp, + 'udp': udp, + 'ws': ws, + 'wss': wss, + 'quic': quic, + }; +} + +// 创建一个新的服务器列表配置类 +class ServerListConfig implements ConfigModel { + final List servers; + + @override + String get configKey => 'server'; + + ServerListConfig({ + required this.servers, + }); + + factory ServerListConfig.fromJson(Map json) { + List serverList = []; + + if (json['list'] is List) { + try { + serverList = (json['list'] as List).map((item) { + // 安全地将 Map 转换为 Map + if (item is Map) { + Map serverMap = {}; + item.forEach((key, value) { + if (key is String) { + serverMap[key] = value; + } + }); + return ServerConfig.fromJson(serverMap); + } + // 如果不是 Map,返回默认服务器配置 + return ServerConfig( + url: 'public.easytier.cn:11010', + name: '公共服务器', + selected: true, + tcp: true, + udp: true, + ws: false, + wss: false, + quic: false, + ); + }).toList(); + + // 确保至少有一个服务器被选中 + if (!serverList.any((server) => server.selected) && + serverList.isNotEmpty) { + print('没有选中的服务器,将第一个服务器设为选中状态'); + serverList[0] = ServerConfig( + url: serverList[0].url, + name: serverList[0].name, + selected: true, + tcp: serverList[0].tcp, + udp: serverList[0].udp, + ws: serverList[0].ws, + wss: serverList[0].wss, + quic: serverList[0].quic, + ); + } else { + print('已有选中的服务器,保持原状'); + } + } catch (e) { + print('解析服务器列表失败: $e'); + // 解析失败时使用默认值 + } + } + + // 如果列表为空,添加默认服务器 + if (serverList.isEmpty) { + print('服务器列表为空,添加默认服务器'); + serverList = [ + ServerConfig( + url: 'public.easytier.cn:11010', + name: '公共服务器', + selected: true, + tcp: true, + udp: true, + ws: false, + wss: false, + quic: false, + ) + ]; + } + + return ServerListConfig(servers: serverList); + } + + @override + Map toJson() => { + 'list': servers.map((server) => server.toJson()).toList(), + }; +} + +class RoomConfig implements ConfigModel { + final String name; + final String password; + + @override + String get configKey => 'room'; + + RoomConfig({ + this.name = 'kevin', + this.password = 'kevin', + }); + + factory RoomConfig.fromJson(Map json) { + return RoomConfig( + name: json['name'] ?? 'kevin', + password: json['password'] ?? 'kevin', + ); + } + + Map toJson() => { + 'name': name, + 'password': password, + }; +} + +class UserConfig implements ConfigModel { + final String name; + + @override + String get configKey => 'user'; + + UserConfig({ + required this.name, + }); + + factory UserConfig.fromJson(Map json) { + return UserConfig( + name: json['name'] ?? Platform.localHostname, + ); + } + + Map toJson() => { + 'name': name, + }; +} + +class NetworkConfig implements ConfigModel { + final String virtualIP; + final bool dynamicIP; + + @override + String get configKey => 'network'; + + NetworkConfig({ + this.virtualIP = '', + this.dynamicIP = true, + }); + + factory NetworkConfig.fromJson(Map json) { + return NetworkConfig( + virtualIP: json['virtualIP'] ?? '', + dynamicIP: json['dynamicIP'] ?? true, + ); + } + + Map toJson() => { + 'virtualIP': virtualIP, + 'dynamicIP': dynamicIP, + }; +} + +class SystemConfig implements ConfigModel { + final bool closeToTray; + final bool enablePing; + + @override + String get configKey => 'system'; + + SystemConfig({ + this.closeToTray = true, + this.enablePing = true, + }); + + factory SystemConfig.fromJson(Map json) { + return SystemConfig( + closeToTray: json['closeToTray'] ?? true, + enablePing: json['enablePing'] ?? true, + ); + } + + Map toJson() => { + 'closeToTray': closeToTray, + 'enablePing': enablePing, + }; +} + +// 配置管理器 class AppConfig { static final AppConfig _instance = AppConfig._internal(); - static late ConfigManager _configManager; + static late Box _configBox; static late String _configDirectory; + static bool _initialized = false; + + // 配置模型映射表 + final Map _configModels = {}; + + // 配置类型与工厂函数映射 + final Map _configFactories = {}; factory AppConfig() { return _instance; } - AppConfig._internal(); - - // 初始化配置 - static Future init() async { - // 获取可执行文件所在目录而不是当前工作目录 - _configDirectory = File(Platform.resolvedExecutable).parent.path; - final configPath = path.join(_configDirectory, 'config.yaml'); - - _configManager = ConfigManager( - filePath: configPath, - defaultConfig: { - 'theme': { - 'mode': 'system', - 'seedColor': Colors.blue.value, - }, - 'server': { - 'list': [ - { - 'url': 'public.easytier.cn:11010', - 'name': '公共服务器', - 'selected': true, - } - ], - }, - 'room': { - 'name': 'kevin', - 'password': 'kevin', - }, - 'user': { - 'name': Platform.localHostname, - }, - 'network': { - 'virtualIP': '', - 'dynamicIP': true, - }, - 'system': { - 'closeToTray': true, - 'enablePing': true, // 添加全局ping开关默认配置 - }, - }, + AppConfig._internal() { + // 注册所有配置类型 + _registerConfig( + (json) => ThemeConfig.fromJson(json), + ThemeConfig(), ); - await _configManager.load(); - - // 验证配置格式,如果不正确则使用默认值覆盖 - validateAndFixConfig(); + _registerConfig( + (json) => RoomConfig.fromJson(json), + RoomConfig(), + ); + + _registerConfig( + (json) => UserConfig.fromJson(json), + UserConfig(name: Platform.localHostname), + ); + + _registerConfig( + (json) => NetworkConfig.fromJson(json), + NetworkConfig(), + ); + + _registerConfig( + (json) => SystemConfig.fromJson(json), + SystemConfig(), + ); + + // 注册服务器列表配置 + _registerConfig( + (json) => ServerListConfig.fromJson(json), + ServerListConfig(servers: [ + ServerConfig( + url: 'public.easytier.cn:11010', + name: '公共服务器', + selected: true, + tcp: true, + udp: true, + ws: false, + wss: false, + quic: false, + ) + ]), + ); + + // 注册高级配置 + _registerConfig( + (json) => AdvancedConfig.fromJson(json), + AdvancedConfig(), + ); } - - // 验证配置格式并修复不正确的配置 - static void validateAndFixConfig() { - // 验证主题设置 - _validateThemeConfig(); - - // 验证服务器列表 - _validateServerListConfig(); - - // 验证房间设置 - _validateRoomConfig(); - - // 验证用户设置 - _validateUserConfig(); - - // 验证网络设置 - _validateNetworkConfig(); - - // 验证系统设置 - _validateSystemConfig(); - - // 保存修复后的配置 - _configManager.save(); + + // 注册配置类型 + void _registerConfig( + T Function(Map) fromJson, + T defaultValue, + ) { + _configFactories[T] = fromJson; + _configModels[T] = defaultValue; } - - // 验证主题配置 - static void _validateThemeConfig() { - // 验证主题模式 - final String? themeMode = _configManager.get('theme.mode'); - if (themeMode == null || - !['system', 'light', 'dark'].contains(themeMode.toLowerCase())) { - _configManager.set('theme.mode', 'system'); - } - - // 验证主题色 - final int? seedColor = _configManager.get('theme.seedColor'); - if (seedColor == null) { - _configManager.set('theme.seedColor', Colors.blue.value); - } - } - - // 验证服务器列表配置 - static void _validateServerListConfig() { - final dynamic serverList = _configManager.get('server.list'); - bool isValid = true; - - if (serverList is! List) { - isValid = false; - } else { - for (var server in serverList) { - if (server is! Map || - !server.containsKey('url') || - !server.containsKey('name') || - !server.containsKey('selected')) { - isValid = false; - break; + + static Future init() async { + if (_initialized) return; + + try { + // 获取可执行文件所在目录作为Hive数据库存储位置 + _configDirectory = File(Platform.resolvedExecutable).parent.path; + print('配置目录: $_configDirectory'); + + // 初始化Hive + await Hive.initFlutter(_configDirectory); + print('Hive初始化完成'); + + // 打开配置Box + _configBox = await Hive.openBox('app_config'); + print('配置Box打开成功,包含 ${_configBox.length} 个条目'); + + // 打印所有键值,用于调试 + print('配置Box中的所有键: ${_configBox.keys.toList()}'); + + // 先创建实例并注册所有配置类型 + // 由于AppConfig是单例模式,这里不需要显式创建实例 + + // 加载并验证配置 + await _instance._loadAndValidateConfig(); + print('配置加载和验证完成'); + + _initialized = true; + } catch (e) { + print('AppConfig初始化失败: $e'); + // 尝试使用备用目录 + try { + final appDocDir = Directory(path.join( + Directory.current.path, + 'config', + )); + + // 确保目录存在 + if (!appDocDir.existsSync()) { + appDocDir.createSync(recursive: true); } + + _configDirectory = appDocDir.path; + print('尝试使用备用目录: $_configDirectory'); + + await Hive.initFlutter(_configDirectory); + _configBox = await Hive.openBox('app_config'); + + await _instance._loadAndValidateConfig(); + _initialized = true; + print('使用备用目录初始化成功'); + } catch (e2) { + print('备用初始化也失败: $e2'); + rethrow; } } - - if (!isValid) { - _configManager.set('server.list', [ - { - 'url': 'public.easytier.cn:11010', - 'name': '公共服务器', - 'selected': true, - } - ]); - } - } - - // 验证房间配置 - static void _validateRoomConfig() { - final String? roomName = _configManager.get('room.name'); - if (roomName == null || roomName.isEmpty) { - _configManager.set('room.name', 'kevin'); - } - - final String? roomPassword = _configManager.get('room.password'); - if (roomPassword == null || roomPassword.isEmpty) { - _configManager.set('room.password', 'kevin'); - } - } - - // 验证用户配置 - static void _validateUserConfig() { - final String? username = _configManager.get('user.name'); - if (username == null || username.isEmpty) { - _configManager.set('user.name', Platform.localHostname); - } - } - - // 验证网络配置 - static void _validateNetworkConfig() { - final String? virtualIP = _configManager.get('network.virtualIP'); - if (virtualIP == null) { - _configManager.set('network.virtualIP', ''); - } - - final bool? dynamicIP = _configManager.get('network.dynamicIP'); - if (dynamicIP == null) { - _configManager.set('network.dynamicIP', true); - } - } - - // 验证系统配置 - static void _validateSystemConfig() { - final bool? closeToTray = _configManager.get('system.closeToTray'); - if (closeToTray == null) { - _configManager.set('system.closeToTray', true); - } - - final bool? enablePing = _configManager.get('system.enablePing'); - if (enablePing == null) { - _configManager.set('system.enablePing', true); - } } + // 加载并验证所有配置 + Future _loadAndValidateConfig() async { + print('开始加载配置...'); + // 加载所有注册的配置 + for (var entry in _configFactories.entries) { + final type = entry.key; + final fromJson = entry.value; + final defaultValue = _configModels[type]!; + final configKey = defaultValue.configKey; + + print('加载配置: $configKey (${type.toString()})'); + _configModels[type] = _loadConfig( + configKey, + fromJson as ConfigModel Function(Map), + defaultValue, + ); + } + + // 更新服务器列表缓存 + _serverConfigs = (getModel()).servers; + print('服务器列表缓存更新完成,共 ${_serverConfigs.length} 个服务器'); + + // 打印服务器列表,用于调试 + for (var server in _serverConfigs) { + print( + '服务器: ${server.name} (${server.url}), 选中: ${server.selected}, 协议: TCP=${server.tcp}, UDP=${server.udp}, WS=${server.ws}, WSS=${server.wss}, QUIC=${server.quic}'); + } + } + + // 通用配置加载方法 + T _loadConfig( + String key, + T Function(Map) fromJson, + T defaultValue, + ) { + final dynamic config = _configBox.get(key); + print('读取配置 $key: ${config != null ? '存在' : '不存在'}'); + + if (config != null) { + try { + print('配置内容: $config'); + final result = fromJson(Map.from(config)); + print('配置解析成功'); + return result; + } catch (e) { + print('配置解析失败: $e'); + // 如果解析失败,使用默认值 + } + } + + // 保存默认值 + final defaultMap = defaultValue.toJson(); + print('使用默认配置: $defaultMap'); + _configBox.put(key, defaultMap); + + return defaultValue; + } + + // 获取配置 + T getModel() { + if (!_configModels.containsKey(T)) { + throw Exception('未注册的配置类型: $T'); + } + return _configModels[T] as T; + } + + // 更新配置 + Future updateModel(T newConfig) async { + if (!_configModels.containsKey(T)) { + throw Exception('未注册的配置类型: $T'); + } + + _configModels[T] = newConfig; + await _configBox.put(newConfig.configKey, newConfig.toJson()); + } + + // 缓存的服务器配置列表 + late List _serverConfigs; + + // 保存服务器列表 + Future _saveServerList() async { + try { + // 打印调试信息 + print('保存服务器列表: ${_serverConfigs.length} 个服务器'); + + // 确保配置模型已更新 + await updateModel( + ServerListConfig(servers: _serverConfigs)); + + // 强制刷新 Hive 存储 + await _configBox.flush(); + + print('服务器列表保存完成'); + } catch (e) { + print('保存服务器列表时出错: $e'); + } + } + + // 以下是为了保持原有API的getter和setter + // 主题设置 + ThemeConfig get theme => getModel(); + ThemeMode get themeMode { - final String? value = _configManager.get('theme.mode'); + final String mode = theme.mode.toLowerCase(); return ThemeMode.values.firstWhere( - (mode) => mode.toString() == 'ThemeMode.$value', + (m) => m.toString().split('.').last.toLowerCase() == mode, orElse: () => ThemeMode.system, ); } Future setThemeMode(ThemeMode mode) async { final modeString = mode.toString().split('.').last.toLowerCase(); - _configManager.set('theme.mode', modeString); - await _configManager.save(); + await updateModel(ThemeConfig( + mode: modeString, + seedColor: theme.seedColor, + )); } // 主题色设置 - Color get seedColor { - final int? value = _configManager.get('theme.seedColor'); - return value != null ? Color(value) : Colors.blue; - } + Color get seedColor => Color(theme.seedColor); Future setSeedColor(Color color) async { - _configManager.set('theme.seedColor', color.value); - await _configManager.save(); + await updateModel(ThemeConfig( + mode: theme.mode, + seedColor: color.value, + )); } // 服务器列表设置 List> get serverList { - final dynamic value = _configManager.get('server.list'); - if (value is List) { - return List>.from( - value.map((item) => Map.from(item as Map)), - ); - } - return [ - { - 'url': 'public.easytier.cn:11010', - 'name': '公共服务器', - 'selected': true, - } - ]; + return _serverConfigs.map((server) => server.toJson()).toList(); } Future setServerList(List> servers) async { - _configManager.set('server.list', servers); - await _configManager.save(); + try { + print('设置服务器列表: ${servers.length} 个服务器'); + + _serverConfigs = servers.map((server) { + // 确保所有必要的字段都存在 + return ServerConfig( + url: server['url'] ?? '', + name: server['name'] ?? '', + selected: server['selected'] ?? false, + tcp: server['tcp'] ?? true, + udp: server['udp'] ?? true, + ws: server['ws'] ?? false, + wss: server['wss'] ?? false, + quic: server['quic'] ?? false, + ); + }).toList(); + + await _saveServerList(); + + // 打印保存后的服务器列表,用于调试 + print( + '服务器列表已更新: ${_serverConfigs.map((s) => '${s.name}(${s.url})').join(', ')}'); + } catch (e) { + print('设置服务器列表时出错: $e'); + } } - // 房间名设置 - String get roomName { - return _configManager.get('room.name') ?? 'kevin'; - } + // 房间配置 + RoomConfig get room => getModel(); + String get roomName => room.name; + String get roomPassword => room.password; Future setRoomName(String name) async { - _configManager.set('room.name', name); - await _configManager.save(); - } - - // 房间密码设置 - String get roomPassword { - return _configManager.get('room.password') ?? 'kevin'; + await updateModel(RoomConfig( + name: name, + password: room.password, + )); } Future setRoomPassword(String password) async { - _configManager.set('room.password', password); - await _configManager.save(); + await updateModel(RoomConfig( + name: room.name, + password: password, + )); } - // 用户名设置 - String get username { - return _configManager.get('user.name') ?? Platform.localHostname; - } + // 用户配置 + UserConfig get user => getModel(); + String get username => user.name; Future setUsername(String name) async { - _configManager.set('user.name', name); - await _configManager.save(); + await updateModel(UserConfig(name: name)); } - // 虚拟IP设置 - String get virtualIP { - return _configManager.get('network.virtualIP') ?? ''; - } + // 网络配置 + NetworkConfig get network => getModel(); + String get virtualIP => network.virtualIP; + bool get dynamicIP => network.dynamicIP; Future setVirtualIP(String ip) async { - _configManager.set('network.virtualIP', ip); - await _configManager.save(); - } - - // 关闭按钮进入托盘 - bool get closeToTray { - return _configManager.get('system.closeToTray') ?? true; - } - - Future setCloseToTray(bool enabled) async { - _configManager.set('system.closeToTray', enabled); - await _configManager.save(); - } - - // 动态获取IP设置 - bool get dynamicIP { - return _configManager.get('network.dynamicIP') ?? true; + await updateModel(NetworkConfig( + virtualIP: ip, + dynamicIP: network.dynamicIP, + )); } Future setDynamicIP(bool enabled) async { - _configManager.set('network.dynamicIP', enabled); - await _configManager.save(); + await updateModel(NetworkConfig( + virtualIP: network.virtualIP, + dynamicIP: enabled, + )); } - // 全局ping开关设置 - bool get enablePing { - return _configManager.get('system.enablePing') ?? true; + // 系统配置 + SystemConfig get system => getModel(); + bool get closeToTray => system.closeToTray; + bool get enablePing => system.enablePing; + + Future setCloseToTray(bool enabled) async { + await updateModel(SystemConfig( + closeToTray: enabled, + enablePing: system.enablePing, + )); } Future setEnablePing(bool enabled) async { - _configManager.set('system.enablePing', enabled); - await _configManager.save(); + await updateModel(SystemConfig( + closeToTray: system.closeToTray, + enablePing: enabled, + )); + } + + // 通用配置获取方法 + T? getConfig(String key) { + return _configBox.get(key) as T?; + } + + // 通用配置设置方法 + Future setConfig(String key, T value) async { + await _configBox.put(key, value); + + // 更新缓存的配置对象 + await _loadAndValidateConfig(); + } + + // 高级配置 + AdvancedConfig get advanced => getModel(); + + // 高级配置 getter + String get defaultProtocol => advanced.defaultProtocol; + String get devName => advanced.devName; + bool get enableEncryption => advanced.enableEncryption; + bool get enableIpv6 => advanced.enableIpv6; + int get mtu => advanced.mtu; + bool get latencyFirst => advanced.latencyFirst; + bool get enableExitNode => advanced.enableExitNode; + bool get proxyForwardBySystem => advanced.proxyForwardBySystem; + bool get noTun => advanced.noTun; + bool get useSmoltcp => advanced.useSmoltcp; + String get relayNetworkWhitelist => advanced.relayNetworkWhitelist; + bool get disableP2p => advanced.disableP2p; + bool get relayAllPeerRpc => advanced.relayAllPeerRpc; + bool get disableUdpHolePunching => advanced.disableUdpHolePunching; + bool get multiThread => advanced.multiThread; + String get dataCompressAlgo => advanced.dataCompressAlgo; + bool get bindDevice => advanced.bindDevice; + bool get enableKcpProxy => advanced.enableKcpProxy; + bool get disableKcpInput => advanced.disableKcpInput; + bool get disableRelayKcp => advanced.disableRelayKcp; + + // 高级配置 setter 方法 + Future setDefaultProtocol(String value) async { + await updateModel(AdvancedConfig( + defaultProtocol: value, + devName: advanced.devName, + enableEncryption: advanced.enableEncryption, + enableIpv6: advanced.enableIpv6, + mtu: advanced.mtu, + latencyFirst: advanced.latencyFirst, + enableExitNode: advanced.enableExitNode, + proxyForwardBySystem: advanced.proxyForwardBySystem, + noTun: advanced.noTun, + useSmoltcp: advanced.useSmoltcp, + relayNetworkWhitelist: advanced.relayNetworkWhitelist, + disableP2p: advanced.disableP2p, + relayAllPeerRpc: advanced.relayAllPeerRpc, + disableUdpHolePunching: advanced.disableUdpHolePunching, + multiThread: advanced.multiThread, + dataCompressAlgo: advanced.dataCompressAlgo, + bindDevice: advanced.bindDevice, + enableKcpProxy: advanced.enableKcpProxy, + disableKcpInput: advanced.disableKcpInput, + disableRelayKcp: advanced.disableRelayKcp, + )); + } + + Future setDevName(String value) async { + await updateModel(AdvancedConfig( + defaultProtocol: advanced.defaultProtocol, + devName: value, + enableEncryption: advanced.enableEncryption, + enableIpv6: advanced.enableIpv6, + mtu: advanced.mtu, + latencyFirst: advanced.latencyFirst, + enableExitNode: advanced.enableExitNode, + proxyForwardBySystem: advanced.proxyForwardBySystem, + noTun: advanced.noTun, + useSmoltcp: advanced.useSmoltcp, + relayNetworkWhitelist: advanced.relayNetworkWhitelist, + disableP2p: advanced.disableP2p, + relayAllPeerRpc: advanced.relayAllPeerRpc, + disableUdpHolePunching: advanced.disableUdpHolePunching, + multiThread: advanced.multiThread, + dataCompressAlgo: advanced.dataCompressAlgo, + bindDevice: advanced.bindDevice, + enableKcpProxy: advanced.enableKcpProxy, + disableKcpInput: advanced.disableKcpInput, + disableRelayKcp: advanced.disableRelayKcp, + )); + } + + // 更新高级配置的通用方法 + Future updateAdvancedConfig({ + String? defaultProtocol, + String? devName, + bool? enableEncryption, + bool? enableIpv6, + int? mtu, + bool? latencyFirst, + bool? enableExitNode, + bool? proxyForwardBySystem, + bool? noTun, + bool? useSmoltcp, + String? relayNetworkWhitelist, + bool? disableP2p, + bool? relayAllPeerRpc, + bool? disableUdpHolePunching, + bool? multiThread, + String? dataCompressAlgo, + bool? bindDevice, + bool? enableKcpProxy, + bool? disableKcpInput, + bool? disableRelayKcp, + }) async { + await updateModel(AdvancedConfig( + defaultProtocol: defaultProtocol ?? advanced.defaultProtocol, + devName: devName ?? advanced.devName, + enableEncryption: enableEncryption ?? advanced.enableEncryption, + enableIpv6: enableIpv6 ?? advanced.enableIpv6, + mtu: mtu ?? advanced.mtu, + latencyFirst: latencyFirst ?? advanced.latencyFirst, + enableExitNode: enableExitNode ?? advanced.enableExitNode, + proxyForwardBySystem: + proxyForwardBySystem ?? advanced.proxyForwardBySystem, + noTun: noTun ?? advanced.noTun, + useSmoltcp: useSmoltcp ?? advanced.useSmoltcp, + relayNetworkWhitelist: + relayNetworkWhitelist ?? advanced.relayNetworkWhitelist, + disableP2p: disableP2p ?? advanced.disableP2p, + relayAllPeerRpc: relayAllPeerRpc ?? advanced.relayAllPeerRpc, + disableUdpHolePunching: + disableUdpHolePunching ?? advanced.disableUdpHolePunching, + multiThread: multiThread ?? advanced.multiThread, + dataCompressAlgo: dataCompressAlgo ?? advanced.dataCompressAlgo, + bindDevice: bindDevice ?? advanced.bindDevice, + enableKcpProxy: enableKcpProxy ?? advanced.enableKcpProxy, + disableKcpInput: disableKcpInput ?? advanced.disableKcpInput, + disableRelayKcp: disableRelayKcp ?? advanced.disableRelayKcp, + )); } } diff --git a/lib/main.dart b/lib/main.dart index 4395f05..78e3024 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,9 +3,9 @@ import 'package:astral/src/rust/frb_generated.dart'; import 'package:window_manager/window_manager.dart'; import 'app.dart'; import 'config/windowconfiguration.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'config/app_config.dart'; import 'utils/kv_state.dart'; -import 'package:provider/provider.dart'; // 添加这一行 import 'package:astral/utils/app_info.dart'; Future main() async { @@ -19,8 +19,7 @@ Future main() async { // 初始化应用信息 await RustLib.init(); runApp( - ChangeNotifierProvider( - create: (context) => KM(), + ProviderScope( child: const MyApp(), ), ); diff --git a/lib/screens/front.dart b/lib/screens/front.dart index a6d0ea2..2768b06 100644 --- a/lib/screens/front.dart +++ b/lib/screens/front.dart @@ -1,13 +1,16 @@ // 导入必要的包 import 'dart:convert'; +import 'package:astral/config/app_config.dart'; import 'package:astral/src/rust/api/simple.dart'; +import 'package:astral/src/rust/frb_generated.dart'; import 'package:astral/utils/kv_state.dart'; import 'package:astral/utils/app_info.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import 'dart:async'; -import 'package:provider/provider.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; // 替换 provider 导入 import '../widgets/card.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import '../utils/runin.dart'; @@ -31,7 +34,7 @@ String _intToIpv4String(int addr) { /// 首页组件 /// 用于显示应用的主页面,包含主题切换和问候功能 -class HomePage extends StatefulWidget { +class HomePage extends ConsumerStatefulWidget { // 主题模式切换回调函数 final Function toggleThemeMode; // 主题色更改回调函数 @@ -48,10 +51,10 @@ class HomePage extends StatefulWidget { }); @override - State createState() => _HomePageState(); + ConsumerState createState() => _HomePageState(); } -class _HomePageState extends State { +class _HomePageState extends ConsumerState { int connectionTimeoutCounter = 0; // 当前连接状态 ConnectionState _connectionState = ConnectionState.notStarted; @@ -74,6 +77,7 @@ class _HomePageState extends State { bool _isAutoIP = true; // 只用于控制IP自动/手动模式 List Serverip = [""]; + List Serveripz = []; int _uploadBytes = 0; int _downloadBytes = 0; int _lastUploadBytes = 0; @@ -129,32 +133,32 @@ class _HomePageState extends State { void _onVirtualIPFocusChange() { if (!_virtualIPFocusNode.hasFocus && !_isAutoIP) { // 当失去焦点且不是自动IP模式时更新值 - Provider.of(context, listen: false).virtualIP = - _virtualIPController.text; + ref + .read(virtualIPProvider.notifier) + .setVirtualIP(_virtualIPController.text); } } // 添加用户名焦点变化监听方法 void _onUsernameFocusChange() { if (!_usernameControllerFocusNode.hasFocus) { - Provider.of(context, listen: false).username = - _usernameController.text; + ref.read(usernameProvider.notifier).setUsername(_usernameController.text); } } // 添加房间名焦点变化监听方法 void _onRoomNameFocusChange() { if (!_roomNameControllerFocusNode.hasFocus) { - Provider.of(context, listen: false).roomName = - _roomNameController.text; + ref.read(roomNameProvider.notifier).setRoomName(_roomNameController.text); } } // 添加房间密码焦点变化监听方法 void _onRoomPasswordFocusChange() { if (!_roomPasswordControllerFocusNode.hasFocus) { - Provider.of(context, listen: false).roomPassword = - _roomPasswordController.text; + ref + .read(roomPasswordProvider.notifier) + .setRoomPassword(_roomPasswordController.text); } } @@ -186,19 +190,75 @@ class _HomePageState extends State { } void toggleRunning() { - final km = Provider.of(context, listen: false); setState(() { isRunning = !isRunning; if (isRunning) { // 切换到连接中状态 _connectionState = ConnectionState.connecting; + + //利用 Serveripz 重组 + List ssServerip = []; + print(ssServerip); + for (var item in Serveripz) { + if (item.tcp) { + ssServerip.add("tcp://" + item.url); + } + if (item.udp) { + ssServerip.add("udp://" + item.url); + } + if (item.ws) { + ssServerip.add("ws://" + item.url); + } + if (item.wss) { + ssServerip.add("wss://" + item.url); + } + if (item.quic) { + ssServerip.add("quic://" + item.url); + } + } + // 复制 createServer( username: username, enableDhcp: _isAutoIP, specifiedIp: publicIP, roomName: roomName, roomPassword: roomPassword, - severurl: Serverip); + severurl: ssServerip, + flag: FlagsC( + defaultProtocol: ref.read(advancedConfigProvider)['defaultProtocol'] ?? + "tcp", + devName: ref.read(advancedConfigProvider)['devName'] ?? "", + enableEncryption: + ref.read(advancedConfigProvider)['enableEncryption'] ?? + true, + enableIpv6: + ref.read(advancedConfigProvider)['enableIpv6'] ?? true, + mtu: ref.read(advancedConfigProvider)['mtu'] ?? 1380, + multiThread: + ref.read(advancedConfigProvider)['multiThread'] ?? true, + latencyFirst: + ref.read(advancedConfigProvider)['latencyFirst'] ?? false, + enableExitNode: + ref.read(advancedConfigProvider)['enableExitNode'] ?? false, + noTun: ref.read(advancedConfigProvider)['noTun'] ?? false, + useSmoltcp: + ref.read(advancedConfigProvider)['useSmoltcp'] ?? false, + relayNetworkWhitelist: + ref.read(advancedConfigProvider)['relayNetworkWhitelist'] ?? + "*", + disableP2P: + ref.read(advancedConfigProvider)['disableP2p'] ?? false, + relayAllPeerRpc: + ref.read(advancedConfigProvider)['relayAllPeerRpc'] ?? + false, + disableUdpHolePunching: + ref.read(advancedConfigProvider)['disableUdpHolePunching'] ?? false, + dataCompressAlgo: ref.read(advancedConfigProvider)['dataCompressAlgo'] ?? "None", + bindDevice: ref.read(advancedConfigProvider)['bindDevice'] ?? true, + enableKcpProxy: ref.read(advancedConfigProvider)['enableKcpProxy'] ?? false, + disableKcpInput: ref.read(advancedConfigProvider)['disableKcpInput'] ?? false, + disableRelayKcp: ref.read(advancedConfigProvider)['disableRelayKcp'] ?? true, + proxyForwardBySystem: ref.read(advancedConfigProvider)['proxyForwardBySystem'] ?? false)); // 不再使用固定延迟模拟连接成功,而是通过定时检查IP来确定连接状态 timer = Timer.periodic(const Duration(seconds: 1), (timer) async { @@ -213,8 +273,7 @@ class _HomePageState extends State { // 获取网络状态 final networkStatus = await getNetworkStatus(); - final km = Provider.of(context, listen: false); - km.nodes = networkStatus.nodes; + ref.read(nodesProvider.notifier).setNodes(networkStatus.nodes); // 更新网络流量数据 _updateNetworkStats(networkStatus.nodes); @@ -223,7 +282,7 @@ class _HomePageState extends State { if (version != null) { String ipStr = _intToIpv4String(version); if (publicIP != ipStr) { - km.virtualIP = ipStr; + ref.read(virtualIPProvider.notifier).setVirtualIP(ipStr); } // 如果当前状态还是连接中,则更新为已连接 @@ -245,8 +304,8 @@ class _HomePageState extends State { }); closeAllServer(); -// 清空玩家列表数据 - Provider.of(context, listen: false).nodes = []; + // 清空玩家列表数据 + ref.read(nodesProvider.notifier).setNodes([]); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -269,13 +328,12 @@ class _HomePageState extends State { runningTime += const Duration(seconds: 1); }); }); - // 移除原来的Future.delayed模拟连接成功的代码 } else { // 停止时重置状态 _connectionState = ConnectionState.notStarted; closeAllServer(); // 清空玩家列表数据 - Provider.of(context, listen: false).nodes = []; + ref.read(nodesProvider.notifier).setNodes([]); timer?.cancel(); runningTime = Duration.zero; // 重置网络统计数据 @@ -285,7 +343,6 @@ class _HomePageState extends State { _lastDownloadBytes = 0; uploadSpeed = 0; downloadSpeed = 0; - //connectionTimeoutCounter } }); } @@ -296,7 +353,7 @@ class _HomePageState extends State { int totalUploadBytes = 0; int totalDownloadBytes = 0; - String myIP = Provider.of(context, listen: false).virtualIP; + String myIP = ref.read(virtualIPProvider); // 查找本机节点 for (var node in nodes) { @@ -343,22 +400,22 @@ class _HomePageState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - final km = Provider.of(context); + // 使用 Riverpod 读取数据 _roomNameController.value = TextEditingValue( - text: km.roomName, + text: ref.read(roomNameProvider), selection: _roomNameController.selection, ); _roomPasswordController.value = TextEditingValue( - text: km.roomPassword, + text: ref.read(roomPasswordProvider), selection: _roomPasswordController.selection, ); _usernameController.value = TextEditingValue( - text: km.username, + text: ref.read(usernameProvider), selection: _usernameController.selection, ); // 添加虚拟IP控制器的值同步 _virtualIPController.value = TextEditingValue( - text: km.virtualIP, + text: ref.read(virtualIPProvider), selection: _virtualIPController.selection, ); } @@ -366,15 +423,17 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - publicIP = Provider.of(context).virtualIP; - _isAutoIP = Provider.of(context).dynamicIP; // 更新自动IP状态 - //我的房间 - roomName = Provider.of(context).roomName; - //我的密码 - roomPassword = Provider.of(context).roomPassword; - //我的用户名 - username = Provider.of(context).username; - Serverip = Provider.of(context).serverIP; + // 使用 Riverpod 读取数据 + publicIP = ref.watch(virtualIPProvider); + _isAutoIP = ref.watch(dynamicIPProvider); + roomName = ref.watch(roomNameProvider); + roomPassword = ref.watch(roomPasswordProvider); + username = ref.watch(usernameProvider); + Serverip = ref.watch(serverIPProvider); + // 从 selectedServerProvider 获取服务器配置列表 + final serverConfigs = + ref.watch(selectedServerProvider) as List; + Serveripz = serverConfigs; // 使用 LayoutBuilder 来处理布局变化,同时保留状态 return Scaffold( @@ -576,53 +635,57 @@ class _HomePageState extends State { // 添加服务器列表卡片 Widget _buildServerListCard(ColorScheme colorScheme) { - final km = Provider.of(context); - // 直接使用 serverIP 列表,而不是尝试访问 serverList - final serverUrls = km.serverIP; + return Consumer( + builder: (context, ref, child) { + // 直接使用 serverIP 列表 + final serverUrls = ref.watch(serverIPProvider); - return FloatingCard( - colorScheme: colorScheme, - maxWidth: 600, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 标题栏 - Row( + return FloatingCard( + colorScheme: colorScheme, + maxWidth: 600, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon(Icons.dns, color: colorScheme.primary, size: 22), - const SizedBox(width: 8), - const Text('当前服务器', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + // 标题栏 + Row( + children: [ + Icon(Icons.dns, color: colorScheme.primary, size: 22), + const SizedBox(width: 8), + const Text('当前服务器', + style: + TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + ], + ), + const SizedBox(height: 16), + + // 当前选中的服务器 + Wrap( + spacing: 8, + runSpacing: 8, + children: serverUrls.isEmpty || + (serverUrls.length == 1 && serverUrls[0].isEmpty) + ? [ + Chip( + avatar: Icon(Icons.info_outline, + size: 16, color: colorScheme.error), + label: const Text('未选择服务器'), + backgroundColor: + colorScheme.errorContainer.withOpacity(0.3), + ) + ] + : serverUrls + .map((url) => Chip( + avatar: Icon(Icons.dns, + size: 16, color: colorScheme.primary), + label: Text(url), + backgroundColor: colorScheme.surfaceVariant, + )) + .toList(), + ), ], ), - const SizedBox(height: 16), - - // 当前选中的服务器 - Wrap( - spacing: 8, - runSpacing: 8, - children: serverUrls.isEmpty || - (serverUrls.length == 1 && serverUrls[0].isEmpty) - ? [ - Chip( - avatar: Icon(Icons.info_outline, - size: 16, color: colorScheme.error), - label: const Text('未选择服务器'), - backgroundColor: - colorScheme.errorContainer.withOpacity(0.3), - ) - ] - : serverUrls - .map((url) => Chip( - avatar: Icon(Icons.dns, - size: 16, color: colorScheme.primary), - label: Text(url), - backgroundColor: colorScheme.surfaceVariant, - )) - .toList(), - ), - ], - ), + ); + }, ); } @@ -716,8 +779,7 @@ class _HomePageState extends State { // 优化用户信息卡片 Widget _buildUserInfoCard(ColorScheme colorScheme) { - final km = Provider.of(context, listen: false); - final isValidIP = _isAutoIP || _isValidIPv4(km.virtualIP); + final isValidIP = _isAutoIP || _isValidIPv4(ref.watch(virtualIPProvider)); return FloatingCard( colorScheme: colorScheme, @@ -762,8 +824,9 @@ class _HomePageState extends State { enabled: _connectionState != ConnectionState.connected, onEditingComplete: () { // 改为完成编辑时更新 - Provider.of(context, listen: false).username = - _usernameController.text; + ref + .read(usernameProvider.notifier) + .setUsername(_usernameController.text); }, decoration: InputDecoration( labelText: '用户名', @@ -789,8 +852,9 @@ class _HomePageState extends State { onEditingComplete: () { // 添加完成编辑回调 if (!_isAutoIP) { - Provider.of(context, listen: false).virtualIP = - _virtualIPController.text; + ref + .read(virtualIPProvider.notifier) + .setVirtualIP(_virtualIPController.text); } }, decoration: InputDecoration( @@ -812,10 +876,14 @@ class _HomePageState extends State { setState(() { _isAutoIP = value; }); - km.dynamicIP = value; + ref + .read(dynamicIPProvider.notifier) + .setDynamicIP(value); // 切换模式时同步最新值 if (!value) { - km.virtualIP = _virtualIPController.text; + ref + .read(virtualIPProvider.notifier) + .setVirtualIP(_virtualIPController.text); } } : null, @@ -846,7 +914,6 @@ class _HomePageState extends State { // 优化房间信息卡片 Widget _buildRoomInfoCard(ColorScheme colorScheme) { - final km = Provider.of(context, listen: false); return FloatingCard( colorScheme: colorScheme, maxWidth: 600, @@ -890,8 +957,9 @@ class _HomePageState extends State { enabled: _connectionState != ConnectionState.connected, onEditingComplete: () { // 改为完成编辑时更新 - Provider.of(context, listen: false).roomName = - _roomNameController.text; + ref + .read(roomNameProvider.notifier) + .setRoomName(_roomNameController.text); }, decoration: InputDecoration( labelText: '房间名称', @@ -908,9 +976,9 @@ class _HomePageState extends State { focusNode: _roomPasswordControllerFocusNode, // 添加焦点节点 enabled: _connectionState != ConnectionState.connected, onEditingComplete: () { - // 改为完成编辑时更新 - Provider.of(context, listen: false).roomPassword = - _roomPasswordController.text; + ref + .read(roomPasswordProvider.notifier) + .setRoomPassword(_roomPasswordController.text); }, obscureText: true, decoration: InputDecoration( diff --git a/lib/screens/kvroom.dart b/lib/screens/kvroom.dart index d142981..b22af90 100644 --- a/lib/screens/kvroom.dart +++ b/lib/screens/kvroom.dart @@ -3,7 +3,7 @@ import 'package:flutter/services.dart'; // 添加这一行导入剪贴板服务 import 'package:astral/utils/kv_state.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; // 替换 provider 导入 import '../widgets/card.dart'; /// 玩家信息模型类 @@ -37,14 +37,17 @@ class PlayerInfo { /// 房间页面组件 /// 用于显示所有玩家的信息 -class RoomPage extends StatefulWidget { +class RoomPage extends ConsumerStatefulWidget { + // 修改为 ConsumerStatefulWidget const RoomPage({super.key}); @override - State createState() => _RoomPageState(); + ConsumerState createState() => + _RoomPageState(); // 修改为 ConsumerState } -class _RoomPageState extends State { +class _RoomPageState extends ConsumerState { + // 修改为 ConsumerState List players = []; List filteredPlayers = []; // 添加过滤后的玩家列表 bool isLoading = true; @@ -88,6 +91,7 @@ class _RoomPageState extends State { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; + // 使用 Riverpod 监听节点数据 return Scaffold( appBar: AppBar( @@ -109,11 +113,10 @@ class _RoomPageState extends State { ), ], ), - body: Consumer( - builder: (context, km, child) { - // 当 KM 更新时,这个 builder 会被重新调用 + body: Builder( + builder: (context) { // 异步处理数据 - _processNodeData(km); + _processNodeData(); if (isLoading) { return Center( @@ -203,9 +206,9 @@ class _RoomPageState extends State { } // 处理节点数据 - 合并了原来的两个相似方法 - Future _processNodeData(KM km) async { + Future _processNodeData() async { try { - final nodes = await km.nodes; // 获取最新的节点信息 + final nodes = await ref.read(nodesProvider); // 获取最新的节点信息 // 将节点数据转换为PlayerInfo对象 List nodePlayerInfos = []; @@ -216,8 +219,8 @@ class _RoomPageState extends State { int downloadSpeed = 0; int sentPackets = 0; int receivedPackets = 0; - String connectionType = - _mapConnectionType(node.cost, node.ipv4, km.virtualIP); + String connectionType = _mapConnectionType( + node.cost, node.ipv4, ref.read(virtualIPProvider)); // 如果有连接信息,计算网络统计数据 if (node.connections.isNotEmpty) { diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 372a6b0..0de7f9a 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -2,16 +2,42 @@ import 'dart:async'; import 'package:astral/utils/app_info.dart'; import 'package:astral/utils/up.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; // 添加 Riverpod 导入 import '../config/app_config.dart'; import '../utils/ping_util.dart'; import 'package:astral/utils/kv_state.dart'; -import 'package:provider/provider.dart'; +import 'package:http/http.dart' as http; // 添加 http 包导入 +import 'dart:convert'; // 添加 json 解析支持 +import 'package:astral/utils/serverjs.dart'; -class SettingsPage extends StatefulWidget { +class Pserver { + final int id; + final String name; + final String address; + final String provider; + final String version; + final bool isOfficialServer; + final bool isTransferable; + final String ip; + + Pserver({ + required this.id, + required this.name, + required this.address, + required this.provider, + required this.version, + required this.isOfficialServer, + required this.isTransferable, + required this.ip, + }); +} + +// 将 StatefulWidget 改为 ConsumerStatefulWidget +class SettingsPage extends ConsumerStatefulWidget { const SettingsPage({super.key}); @override - State createState() => _SettingsPageState(); + ConsumerState createState() => _SettingsPageState(); } final updateChecker = UpdateChecker( @@ -19,12 +45,39 @@ final updateChecker = UpdateChecker( repo: 'astral', ); -class _SettingsPageState extends State { +// 存储服务器组列表的状态变量 +// 使用 StateProvider 来管理服务器组列表状态 +final pServerProvider = StateProvider>((ref) => []); + +// 将 State 改为 ConsumerState +class _SettingsPageState extends ConsumerState { late List> _serverList; final _appConfig = AppConfig(); bool _closeToTray = false; // 添加关闭进入托盘变量 bool _pingEnabled = true; // 添加全局ping开关 + // 添加高级选项变量 + String _defaultProtocol = "tcp"; + String _devName = ""; + bool _enableEncryption = true; + bool _enableIpv6 = true; + int _mtu = 1380; + bool _latencyFirst = false; + bool _enableExitNode = false; + bool _proxyForwardBySystem = false; + bool _noTun = false; + bool _useSmoltcp = false; + String _relayNetworkWhitelist = "*"; + bool _disableP2p = false; + bool _relayAllPeerRpc = false; + bool _disableUdpHolePunching = false; + bool _multiThread = true; + String _dataCompressAlgo = "None"; + bool _bindDevice = true; + bool _enableKcpProxy = false; + bool _disableKcpInput = false; + bool _disableRelayKcp = true; + String serverIP = ""; // 添加 ping 相关状态 Map pingResults = {}; @@ -32,12 +85,36 @@ class _SettingsPageState extends State { @override void initState() { super.initState(); - _serverList = _appConfig.serverList; + // 从 Riverpod 获取服务器列表 + _serverList = ref.read(serverListProvider); serverIP = _getSelectedServersString(); _closeToTray = _appConfig.closeToTray; // 初始化托盘设置 // 初始化全局ping开关 _pingEnabled = _appConfig.enablePing; + // 初始化高级设置 + final advancedConfig = ref.read(advancedConfigProvider); + _defaultProtocol = advancedConfig['defaultProtocol'] ?? "tcp"; + _devName = advancedConfig['devName'] ?? ""; + _enableEncryption = advancedConfig['enableEncryption'] ?? true; + _enableIpv6 = advancedConfig['enableIpv6'] ?? true; + _mtu = advancedConfig['mtu'] ?? 1380; + _latencyFirst = advancedConfig['latencyFirst'] ?? false; + _enableExitNode = advancedConfig['enableExitNode'] ?? false; + _proxyForwardBySystem = advancedConfig['proxyForwardBySystem'] ?? false; + _noTun = advancedConfig['noTun'] ?? false; + _useSmoltcp = advancedConfig['useSmoltcp'] ?? false; + _relayNetworkWhitelist = advancedConfig['relayNetworkWhitelist'] ?? "*"; + _disableP2p = advancedConfig['disableP2p'] ?? false; + _relayAllPeerRpc = advancedConfig['relayAllPeerRpc'] ?? false; + _disableUdpHolePunching = advancedConfig['disableUdpHolePunching'] ?? false; + _multiThread = advancedConfig['multiThread'] ?? true; + _dataCompressAlgo = advancedConfig['dataCompressAlgo'] ?? "None"; + _bindDevice = advancedConfig['bindDevice'] ?? true; + _enableKcpProxy = advancedConfig['enableKcpProxy'] ?? false; + _disableKcpInput = advancedConfig['disableKcpInput'] ?? false; + _disableRelayKcp = advancedConfig['disableRelayKcp'] ?? true; + // 初始化 ping 状态 for (var server in _serverList) { pingResults[server['url']] = null; @@ -99,6 +176,236 @@ class _SettingsPageState extends State { } } + // 显示公共服务器列表 + // 显示公共服务器列表 + void _showPublicServerList(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + builder: (context) { + return DraggableScrollableSheet( + initialChildSize: 0.7, + minChildSize: 0.5, + maxChildSize: 0.9, + expand: false, + builder: (context, scrollController) { + return Consumer( + builder: (context, ref, child) { + final publicServers = ref.watch(pServerProvider); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + const Icon(Icons.public), + const SizedBox(width: 8), + const Text( + '公共服务器列表', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const Spacer(), + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + // 不关闭底部表单,直接刷新数据 + _fetchEasyTierStatus(); + }, + tooltip: '刷新服务器列表', + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.pop(context), + tooltip: '关闭', + ), + ], + ), + ), + const Divider(), + Expanded( + child: publicServers.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('暂无公共服务器数据'), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + _fetchEasyTierStatus(); + }, + child: const Text('获取公共服务器数据'), + ), + ], + ), + ) + : ListView.builder( + controller: scrollController, + itemCount: publicServers.length, + itemBuilder: (context, index) { + final server = publicServers[index]; + return ListTile( + leading: Icon( + server.isOfficialServer + ? Icons.verified_user + : Icons.person, + color: server.isOfficialServer + ? Colors.green + : Colors.blue, + ), + title: Text(server.name), + subtitle: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '地址: ${server.address}', + overflow: TextOverflow.ellipsis, + ), + Text( + 'IP: ${server.ip}', + overflow: TextOverflow.ellipsis, + ), + Text( + '提供商: ${server.provider}', + overflow: TextOverflow.ellipsis, + ), + Text( + '版本: ${server.version}', + overflow: TextOverflow.ellipsis, + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Chip( + label: Text( + server.isOfficialServer + ? '官方' + : '第三方', + style: const TextStyle( + fontSize: 10), + ), + backgroundColor: + server.isOfficialServer + ? Colors.green + .withOpacity(0.2) + : Colors.blue + .withOpacity(0.2), + visualDensity: + VisualDensity.compact, + labelPadding: + const EdgeInsets.symmetric( + horizontal: 4), + ), + ), + const SizedBox(width: 4), + Flexible( + child: Chip( + label: Text( + server.isTransferable + ? '可转发' + : '不可转发', + style: const TextStyle( + fontSize: 10), + ), + backgroundColor: server + .isTransferable + ? Colors.orange + .withOpacity(0.2) + : Colors.red.withOpacity(0.2), + visualDensity: + VisualDensity.compact, + labelPadding: + const EdgeInsets.symmetric( + horizontal: 4), + ), + ), + ], + ), + ], + ), + isThreeLine: true, + trailing: IconButton( + icon: const Icon(Icons.add_circle), + color: Colors.green, + onPressed: () => + _addPublicServerToList(server), + tooltip: '添加到我的服务器', + ), + ); + }, + ), + ), + ], + ); + }, + ); + }, + ); + }, + ); + } + + // 添加公共服务器到我的服务器列表 + void _addPublicServerToList(Pserver publicServer) { + // 检查是否已存在相同地址的服务器 + final existingServer = _serverList.firstWhere( + (server) => server['url'] == publicServer.ip, + orElse: () => {}, + ); + + if (existingServer.isNotEmpty) { + // 已存在相同地址的服务器 + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('服务器 ${publicServer.ip} 已存在于您的列表中'), + behavior: SnackBarBehavior.floating, + ), + ); + return; + } + + setState(() { + // 添加新服务器,默认不选中,使用IP地址而不是address + _serverList.add({ + 'url': publicServer.ip, + 'name': publicServer.name, + 'selected': false, + 'tcp': true, + 'udp': true, + 'ws': false, + 'wss': false, + 'quic': false, + }); + _appConfig.setServerList(_serverList); + + // 初始化新服务器的 ping 状态 + pingResults[publicServer.ip] = null; + }); + + // 显示添加成功提示 + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('已添加服务器: ${publicServer.name}'), + behavior: SnackBarBehavior.floating, + action: SnackBarAction( + label: '确定', + onPressed: () { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + }, + ), + ), + ); + } + // 切换全局ping状态 void _togglePingStatus(bool value) { setState(() { @@ -117,60 +424,114 @@ class _SettingsPageState extends State { Future _showAddServerDialog() async { final urlController = TextEditingController(); final nameController = TextEditingController(); + // 添加协议开关状态 + bool tcpEnabled = true; // 默认启用TCP + bool udpEnabled = true; // 默认启用UDP + bool wsEnabled = false; + bool wssEnabled = false; + bool quicEnabled = false; final result = await showDialog>( context: context, - builder: (context) => AlertDialog( - title: const Text('添加服务器'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: nameController, - decoration: const InputDecoration( - labelText: '服务器名称', - hintText: '自定义名称', - ), + builder: (context) => StatefulBuilder( + builder: (context, setState) => AlertDialog( + title: const Text('添加服务器'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: nameController, + decoration: const InputDecoration( + labelText: '服务器名称', + hintText: '自定义名称', + ), + ), + const SizedBox(height: 8), + TextField( + controller: urlController, + decoration: const InputDecoration( + labelText: '服务器地址', + hintText: 'example.com:port', + ), + ), + const SizedBox(height: 16), + const Divider(), + const Text('支持的协议', + style: TextStyle(fontWeight: FontWeight.bold)), + SwitchListTile( + title: const Text('TCP'), + value: tcpEnabled, + onChanged: (value) => setState(() => tcpEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('UDP'), + value: udpEnabled, + onChanged: (value) => setState(() => udpEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('WebSocket (WS)'), + value: wsEnabled, + onChanged: (value) => setState(() => wsEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('WebSocket Secure (WSS)'), + value: wssEnabled, + onChanged: (value) => setState(() => wssEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('QUIC'), + value: quicEnabled, + onChanged: (value) => setState(() => quicEnabled = value), + dense: true, + ), + ], ), - const SizedBox(height: 8), - TextField( - controller: urlController, - decoration: const InputDecoration( - labelText: '服务器地址', - hintText: 'example.com:port', - ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('取消'), + ), + TextButton( + onPressed: () { + if (urlController.text.isNotEmpty) { + Navigator.pop(context, { + 'url': urlController.text, + 'name': nameController.text.isNotEmpty + ? nameController.text + : urlController.text, + 'tcp': tcpEnabled, + 'udp': udpEnabled, + 'ws': wsEnabled, + 'wss': wssEnabled, + 'quic': quicEnabled, + }); + } + }, + child: const Text('添加'), ), ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('取消'), - ), - TextButton( - onPressed: () { - if (urlController.text.isNotEmpty) { - Navigator.pop(context, { - 'url': urlController.text, - 'name': nameController.text.isNotEmpty - ? nameController.text - : urlController.text, - }); - } - }, - child: const Text('添加'), - ), - ], ), ); if (result != null) { setState(() { - // 添加新服务器,默认不选中 + // 添加新服务器,默认不选中,包含协议设置 _serverList.add({ 'url': result['url'], 'name': result['name'], 'selected': false, + 'tcp': result['tcp'] ?? true, + 'udp': result['udp'] ?? true, + 'ws': result['ws'] ?? false, + 'wss': result['wss'] ?? false, + 'quic': result['quic'] ?? false, }); _appConfig.setServerList(_serverList); @@ -185,50 +546,99 @@ class _SettingsPageState extends State { final server = _serverList[index]; final urlController = TextEditingController(text: server['url']); final nameController = TextEditingController(text: server['name']); + // 初始化协议开关状态 + bool tcpEnabled = server['tcp'] ?? true; + bool udpEnabled = server['udp'] ?? true; + bool wsEnabled = server['ws'] ?? false; + bool wssEnabled = server['wss'] ?? false; + bool quicEnabled = server['quic'] ?? false; final result = await showDialog>( context: context, - builder: (context) => AlertDialog( - title: const Text('编辑服务器'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: nameController, - decoration: const InputDecoration( - labelText: '服务器名称', - hintText: '自定义名称', - ), + builder: (context) => StatefulBuilder( + builder: (context, setState) => AlertDialog( + title: const Text('编辑服务器'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: nameController, + decoration: const InputDecoration( + labelText: '服务器名称', + hintText: '自定义名称', + ), + ), + const SizedBox(height: 8), + TextField( + controller: urlController, + decoration: const InputDecoration( + labelText: '服务器地址', + hintText: 'example.com:port', + ), + ), + const SizedBox(height: 16), + const Divider(), + const Text('支持的协议', + style: TextStyle(fontWeight: FontWeight.bold)), + SwitchListTile( + title: const Text('TCP'), + value: tcpEnabled, + onChanged: (value) => setState(() => tcpEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('UDP'), + value: udpEnabled, + onChanged: (value) => setState(() => udpEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('WebSocket (WS)'), + value: wsEnabled, + onChanged: (value) => setState(() => wsEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('WebSocket Secure (WSS)'), + value: wssEnabled, + onChanged: (value) => setState(() => wssEnabled = value), + dense: true, + ), + SwitchListTile( + title: const Text('QUIC'), + value: quicEnabled, + onChanged: (value) => setState(() => quicEnabled = value), + dense: true, + ), + ], ), - const SizedBox(height: 8), - TextField( - controller: urlController, - decoration: const InputDecoration( - labelText: '服务器地址', - hintText: 'example.com:port', - ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('取消'), + ), + TextButton( + onPressed: () { + if (urlController.text.isNotEmpty) { + Navigator.pop(context, { + 'url': urlController.text, + 'name': nameController.text.isNotEmpty + ? nameController.text + : urlController.text, + 'tcp': tcpEnabled, + 'udp': udpEnabled, + 'ws': wsEnabled, + 'wss': wssEnabled, + 'quic': quicEnabled, + }); + } + }, + child: const Text('保存'), ), ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('取消'), - ), - TextButton( - onPressed: () { - if (urlController.text.isNotEmpty) { - Navigator.pop(context, { - 'url': urlController.text, - 'name': nameController.text.isNotEmpty - ? nameController.text - : urlController.text, - }); - } - }, - child: const Text('保存'), - ), - ], ), ); @@ -239,6 +649,11 @@ class _SettingsPageState extends State { 'url': result['url'], 'name': result['name'], 'selected': server['selected'], + 'tcp': result['tcp'] ?? true, + 'udp': result['udp'] ?? true, + 'ws': result['ws'] ?? false, + 'wss': result['wss'] ?? false, + 'quic': result['quic'] ?? false, }; _appConfig.setServerList(_serverList); @@ -250,8 +665,8 @@ class _SettingsPageState extends State { // 更新当前选中的服务器显示 serverIP = _getSelectedServersString(); - // 使用新的 serverList 方法替代直接设置 serverIP - Provider.of(context, listen: false).serverList = _serverList; + // 使用 Riverpod 更新服务器列表 + ref.read(serverListProvider.notifier).setServerList(_serverList); }); } } @@ -294,8 +709,8 @@ class _SettingsPageState extends State { // 更新当前选中的服务器显示 serverIP = _getSelectedServersString(); - // 使用新的 serverList 方法替代直接设置 serverIP - Provider.of(context, listen: false).serverList = _serverList; + // 使用 Riverpod 更新服务器列表 + ref.read(serverListProvider.notifier).setServerList(_serverList); }); } } @@ -309,15 +724,9 @@ class _SettingsPageState extends State { // 更新当前选中的服务器显示 serverIP = _getSelectedServersString(); - // 使用新的 serverList 方法替代直接设置 serverIP - Provider.of(context, listen: false).serverList = _serverList; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(_serverList[index]['selected'] - ? '已选中服务器: ${_serverList[index]['name']}' - : '已取消选中: ${_serverList[index]['name']}')), - ); + // 使用 Riverpod 更新服务器列表 + ref.read(serverListProvider.notifier).setServerList(_serverList); }); } @@ -340,125 +749,912 @@ class _SettingsPageState extends State { } } + // 添加一个方法来显示服务器支持的协议 + Widget _buildProtocolChips(Map server) { + List chips = []; + + if (server['tcp'] == true) { + chips.add(Chip( + label: const Text('TCP', style: TextStyle(fontSize: 10)), + backgroundColor: Colors.blue.withOpacity(0.2), + visualDensity: VisualDensity.compact, + labelPadding: const EdgeInsets.symmetric(horizontal: 4), + )); + } + + if (server['udp'] == true) { + chips.add(Chip( + label: const Text('UDP', style: TextStyle(fontSize: 10)), + backgroundColor: Colors.green.withOpacity(0.2), + visualDensity: VisualDensity.compact, + labelPadding: const EdgeInsets.symmetric(horizontal: 4), + )); + } + + if (server['ws'] == true) { + chips.add(Chip( + label: const Text('WS', style: TextStyle(fontSize: 10)), + backgroundColor: Colors.orange.withOpacity(0.2), + visualDensity: VisualDensity.compact, + labelPadding: const EdgeInsets.symmetric(horizontal: 4), + )); + } + + if (server['wss'] == true) { + chips.add(Chip( + label: const Text('WSS', style: TextStyle(fontSize: 10)), + backgroundColor: Colors.purple.withOpacity(0.2), + visualDensity: VisualDensity.compact, + labelPadding: const EdgeInsets.symmetric(horizontal: 4), + )); + } + + if (server['quic'] == true) { + chips.add(Chip( + label: const Text('QUIC', style: TextStyle(fontSize: 10)), + backgroundColor: Colors.teal.withOpacity(0.2), + visualDensity: VisualDensity.compact, + labelPadding: const EdgeInsets.symmetric(horizontal: 4), + )); + } + + return Wrap( + spacing: 4, + runSpacing: 0, + children: chips, + ); + } + @override Widget build(BuildContext context) { // updateChecker.checkForUpdates(context); - return ListView( - padding: const EdgeInsets.all(16.0), - children: [ - Card( - child: Column( - children: [ - ListTile( - leading: const Icon(Icons.dns), - title: const Text('已选中的服务器'), - subtitle: Text(serverIP), - ), - ExpansionTile( - leading: const Icon(Icons.list), - title: const Text('服务器列表'), - children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: _serverList.length, - itemBuilder: (context, index) { - final server = _serverList[index]; - final isSelected = server['selected'] == true; + return ListView(padding: const EdgeInsets.all(16.0), children: [ + Card( + child: Column( + children: [ + ListTile( + leading: const Icon(Icons.dns), + title: const Text('已选中的服务器'), + subtitle: Text(serverIP), + ), + ExpansionTile( + leading: const Icon(Icons.list), + title: const Text('服务器列表'), + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: _serverList.length, + itemBuilder: (context, index) { + final server = _serverList[index]; + final isSelected = server['selected'] == true; - return ListTile( - leading: Icon( - isSelected - ? Icons.check_circle - : Icons.circle_outlined, - color: isSelected ? Colors.green : null, - ), - title: Row( - children: [ - Text(server['name']), - const SizedBox(width: 8), - _buildPingWidget(server['url']), - ], - ), - subtitle: Text(server['url']), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.edit), - onPressed: () => _showEditServerDialog(index), + return ListTile( + leading: Icon( + isSelected ? Icons.check_circle : Icons.circle_outlined, + color: isSelected ? Colors.green : null, + ), + title: Row( + children: [ + Expanded( + child: Text( + server['name'], + overflow: TextOverflow.ellipsis, ), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () => _deleteServer(index), - ), - ], + ), + const SizedBox(width: 8), + _buildPingWidget(server['url']), + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(server['url']), + const SizedBox(height: 4), + _buildProtocolChips(server), + ], + ), + isThreeLine: true, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => _showEditServerDialog(index), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _deleteServer(index), + ), + ], + ), + onTap: () => _toggleServerSelection(index), + ); + }, + ), + ListTile( + leading: const Icon(Icons.add), + title: const Text('添加新服务器'), + onTap: _showAddServerDialog, + ), + // 添加公共服务器列表入口 + ListTile( + leading: const Icon(Icons.public), + title: const Text('查看公共服务器列表'), + onTap: () => _showPublicServerList(context), + ), + ], + ), + ], + ), + ), + const SizedBox(height: 16), + Card( + child: Column( + children: [ + const ListTile( + leading: Icon(Icons.settings), + title: Text('应用设置'), + ), + SwitchListTile( + title: const Text('关闭时最小化到托盘'), + subtitle: const Text('关闭窗口时应用将继续在后台运行'), + value: _closeToTray, + onChanged: (value) { + setState(() { + _closeToTray = value; + _appConfig.setCloseToTray(value); + }); + }, + ), + SwitchListTile( + title: const Text('启用服务器Ping测试'), + subtitle: const Text('定期测试所有服务器的网络延迟'), + value: _pingEnabled, + onChanged: _togglePingStatus, + ), + ], + ), + ), + + const SizedBox(height: 16), + // 添加高级选项卡 + Card( + child: Column( + children: [ + ExpansionTile( + leading: const Icon(Icons.tune), + title: const Text('高级选项'), + subtitle: const Text('调整网络和性能参数'), + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 协议设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + '协议设置', + style: TextStyle(fontWeight: FontWeight.bold), ), - onTap: () => _toggleServerSelection(index), - ); - }, - ), - ListTile( - leading: const Icon(Icons.add), - title: const Text('添加新服务器'), - onTap: _showAddServerDialog, - ), - ], - ), - ], - ), - ), - const SizedBox(height: 16), + ), + DropdownButtonFormField( + decoration: const InputDecoration( + labelText: '默认协议', + border: OutlineInputBorder(), + ), + value: _defaultProtocol, + items: const [ + DropdownMenuItem(value: 'tcp', child: Text('TCP')), + DropdownMenuItem(value: 'udp', child: Text('UDP')), + DropdownMenuItem(value: 'quic', child: Text('QUIC')), + DropdownMenuItem( + value: 'ws', child: Text('WebSocket')), + DropdownMenuItem( + value: 'wss', child: Text('WebSocket Secure')), + ], + onChanged: (value) { + setState(() { + _defaultProtocol = value!; + ref + .read(advancedConfigProvider.notifier) + .setDefaultProtocol(value); + }); + }, + ), + const SizedBox(height: 16), - // 添加应用设置卡片 - Card( - child: Column( - children: [ - const ListTile( - leading: Icon(Icons.settings), - title: Text('应用设置'), - ), - SwitchListTile( - title: const Text('关闭时最小化到托盘'), - subtitle: const Text('关闭窗口时应用将继续在后台运行'), - value: _closeToTray, - onChanged: (value) { - setState(() { - _closeToTray = value; - _appConfig.setCloseToTray(value); - }); - }, - ), - SwitchListTile( - title: const Text('启用服务器Ping测试'), - subtitle: const Text('定期测试所有服务器的网络延迟'), - value: _pingEnabled, - onChanged: _togglePingStatus, - ), - ], - ), - ), + // 设备设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + '设备设置', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + TextField( + decoration: const InputDecoration( + labelText: '设备名称', + hintText: '留空使用默认值', + border: OutlineInputBorder(), + ), + controller: TextEditingController(text: _devName), + onChanged: (value) { + setState(() { + _devName = value; + ref + .read(advancedConfigProvider.notifier) + .setDevName(value); + }); + }, + ), + const SizedBox(height: 16), - const SizedBox(height: 16), - Card( - child: Column( - children: [ - ListTile( - leading: Icon(Icons.info), - title: Text('应用版本'), - subtitle: Text(AppInfoUtil.getVersion()), - ), - ListTile( - leading: const Icon(Icons.update), - title: const Text('检查更新'), - onTap: () { - updateChecker.checkForUpdates(context); - }, - ), - ], - ), + // MTU设置 + Row( + children: [ + Expanded( + child: TextField( + decoration: const InputDecoration( + labelText: 'MTU值', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + controller: + TextEditingController(text: _mtu.toString()), + onChanged: (value) { + final mtuValue = int.tryParse(value) ?? 1380; + setState(() { + _mtu = mtuValue; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('mtu', mtuValue); + }); + }, + ), + ), + ], + ), + const SizedBox(height: 16), + + // 安全设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + '安全设置', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('启用加密'), + subtitle: const Text('加密网络流量以提高安全性'), + value: _enableEncryption, + onChanged: (value) { + setState(() { + _enableEncryption = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('enableEncryption', value); + }); + }, + ), + + // 网络设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + '网络设置', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('启用IPv6'), + subtitle: const Text('允许IPv6网络连接'), + value: _enableIpv6, + onChanged: (value) { + setState(() { + _enableIpv6 = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('enableIpv6', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('优先低延迟'), + subtitle: const Text('优先考虑连接延迟而非带宽'), + value: _latencyFirst, + onChanged: (value) { + setState(() { + _latencyFirst = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('latencyFirst', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('启用出口节点'), + subtitle: const Text('允许作为网络出口节点'), + value: _enableExitNode, + onChanged: (value) { + setState(() { + _enableExitNode = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('enableExitNode', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('系统代理转发'), + subtitle: const Text('通过系统代理转发流量'), + value: _proxyForwardBySystem, + onChanged: (value) { + setState(() { + _proxyForwardBySystem = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('proxyForwardBySystem', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('禁用TUN'), + subtitle: const Text('不使用TUN虚拟网络接口'), + value: _noTun, + onChanged: (value) { + setState(() { + _noTun = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('noTun', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('使用Smoltcp'), + subtitle: const Text('使用Smoltcp网络栈'), + value: _useSmoltcp, + onChanged: (value) { + setState(() { + _useSmoltcp = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('useSmoltcp', value); + }); + }, + ), + + // 中继设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + '中继设置', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + TextField( + decoration: const InputDecoration( + labelText: '中继网络白名单', + hintText: '使用*表示允许所有', + border: OutlineInputBorder(), + ), + controller: + TextEditingController(text: _relayNetworkWhitelist), + onChanged: (value) { + setState(() { + _relayNetworkWhitelist = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('relayNetworkWhitelist', value); + }); + }, + ), + const SizedBox(height: 16), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('禁用P2P'), + subtitle: const Text('禁用点对点直接连接'), + value: _disableP2p, + onChanged: (value) { + setState(() { + _disableP2p = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('disableP2p', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('中继所有对等RPC'), + subtitle: const Text('通过中继服务器转发所有RPC请求'), + value: _relayAllPeerRpc, + onChanged: (value) { + setState(() { + _relayAllPeerRpc = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('relayAllPeerRpc', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('禁用UDP打洞'), + subtitle: const Text('禁用UDP NAT穿透技术'), + value: _disableUdpHolePunching, + onChanged: (value) { + setState(() { + _disableUdpHolePunching = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('disableUdpHolePunching', value); + }); + }, + ), + + // 性能设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + '性能设置', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('多线程'), + subtitle: const Text('启用多线程处理以提高性能'), + value: _multiThread, + onChanged: (value) { + setState(() { + _multiThread = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('multiThread', value); + }); + }, + ), + DropdownButtonFormField( + decoration: const InputDecoration( + labelText: '数据压缩算法', + border: OutlineInputBorder(), + ), + value: _dataCompressAlgo, + items: const [ + DropdownMenuItem(value: 'None', child: Text('无压缩')), + DropdownMenuItem(value: 'Lz4', child: Text('LZ4')), + DropdownMenuItem(value: 'Zstd', child: Text('Zstd')), + ], + onChanged: (value) { + setState(() { + _dataCompressAlgo = value!; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('dataCompressAlgo', value); + }); + }, + ), + const SizedBox(height: 16), + + // KCP设置 + const ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + 'KCP设置', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('绑定设备'), + subtitle: const Text('将连接绑定到特定网络设备'), + value: _bindDevice, + onChanged: (value) { + setState(() { + _bindDevice = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('bindDevice', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('启用KCP代理'), + subtitle: const Text('使用KCP协议进行代理'), + value: _enableKcpProxy, + onChanged: (value) { + setState(() { + _enableKcpProxy = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('enableKcpProxy', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('禁用KCP输入'), + subtitle: const Text('禁止KCP协议输入'), + value: _disableKcpInput, + onChanged: (value) { + setState(() { + _disableKcpInput = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('disableKcpInput', value); + }); + }, + ), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('禁用中继KCP'), + subtitle: const Text('禁用中继服务器的KCP协议'), + value: _disableRelayKcp, + onChanged: (value) { + setState(() { + _disableRelayKcp = value; + ref + .read(advancedConfigProvider.notifier) + .updateConfig('disableRelayKcp', value); + }); + }, + ), + ], + ), + ), + ], + ), + ], ), - ], - ); + ), + + const SizedBox(height: 16), + Card( + child: Column( + children: [ + ListTile( + leading: Icon(Icons.info), + title: Text('应用版本'), + subtitle: Text(AppInfoUtil.getVersion()), + ), + ListTile( + leading: const Icon(Icons.update), + title: const Text('检查更新'), + onTap: () { + updateChecker.checkForUpdates(context); + }, + ), + ListTile( + leading: const Icon(Icons.cloud), + title: const Text('获取 EasyTier 状态'), + onTap: _fetchEasyTierStatus, + ), + ], + ), + ), + ]); + } + + // 添加网络请求方法 + Future _fetchEasyTierStatus() async { + try { + // 显示加载指示器 + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('正在获取服务器数据...')), + ); + } + + final response = await http + .get(Uri.parse('https://easytier.gd.nkbpal.cn/status/easytier')); + + if (response.statusCode == 200) { + // 请求成功,打印响应内容 + print('EasyTier 状态: ${response.body}'); + + // 提取 window.preloadData = 和 之间的内容 + try { + final String responseBody = response.body; + final startMarker = 'window.preloadData = '; + final endMarker = ''; + + final startIndex = responseBody.indexOf(startMarker); + final endIndex = responseBody.indexOf(endMarker, startIndex); + + if (startIndex != -1 && endIndex != -1) { + final startDataIndex = startIndex + startMarker.length; + final extractedData = + responseBody.substring(startDataIndex, endIndex).trim(); + + print('提取的数据: $extractedData'); + + // 尝试解析提取的JSON数据 + try { + // 先将字符串解析为Map + // 去除最后的分号并将单引号替换为双引号 + String cleanData = extractedData + .replaceAll(RegExp(r';$'), '') + .replaceAll("'", '"'); + Map jsonMap = json.decode(cleanData); + StatusPageData? pageData = StatusPageData.fromJson(jsonMap); + // 打印解析后的数据 + print('解析后的数据: $pageData'); + + // 初始化空列表 + final List pGroupList = []; + + if (pageData.publicGroupList.length > 1) { + pageData.publicGroupList[1].monitorList.forEach((monitor) { + try { + List parts = monitor.name.split('/'); + if (parts.length >= 4) { + // 尝试从各个部分中提取IP:端口格式 + String finalIp = ""; + + // 遍历所有部分,查找符合IP:端口或域名:端口格式的字符串 + for (String part in parts) { + // 检查是否符合IP:端口或域名:端口格式 + bool isIpPort = + RegExp(r'^(\d{1,3}\.){3}\d{1,3}(:\d+)?$') + .hasMatch(part); + bool isDomainPort = RegExp( + r'^[a-zA-Z0-9][-a-zA-Z0-9\.]+\.[a-zA-Z]+:\d+$') + .hasMatch(part); + + if (isIpPort || isDomainPort) { + finalIp = part; + break; + } + } + + // 如果没有找到,尝试查找IPv4或IPv6标记后的部分 + if (finalIp.isEmpty) { + for (int i = 0; i < parts.length - 1; i++) { + if (parts[i] == "IPv4" || + parts[i] == "IPv6" || + parts[i] == "TCP" || + parts[i] == "UDP") { + String nextPart = parts[i + 1]; + if (RegExp(r'^[a-zA-Z0-9][-a-zA-Z0-9\.]+\.[a-zA-Z]+:\d+$') + .hasMatch(nextPart) || + RegExp(r'^(\d{1,3}\.){3}\d{1,3}(:\d+)?$') + .hasMatch(nextPart)) { + finalIp = nextPart; + break; + } + } + } + } + + // 如果仍然没有找到,使用默认值 + if (finalIp.isEmpty && parts.length > 3) { + finalIp = parts[3]; // 使用默认位置 + } + + pGroupList.add(Pserver( + id: monitor.id, + name: monitor.name, + address: parts[0], + provider: parts[1], + version: parts[2], + isOfficialServer: true, + isTransferable: true, + ip: finalIp, + )); + } + } catch (e) { + print('Error parsing server data: $e'); + } + }); + } + + if (pageData.publicGroupList.length > 2) { + pageData.publicGroupList[2].monitorList.forEach((monitor) { + try { + List parts = monitor.name.split('/'); + if (parts.length >= 4) { + // 尝试从各个部分中提取IP:端口格式 + String finalIp = ""; + + // 遍历所有部分,查找符合IP:端口或域名:端口格式的字符串 + for (String part in parts) { + // 检查是否符合IP:端口或域名:端口格式 + bool isIpPort = + RegExp(r'^(\d{1,3}\.){3}\d{1,3}(:\d+)?$') + .hasMatch(part); + bool isDomainPort = RegExp( + r'^[a-zA-Z0-9][-a-zA-Z0-9\.]+\.[a-zA-Z]+:\d+$') + .hasMatch(part); + + if (isIpPort || isDomainPort) { + finalIp = part; + break; + } + } + + // 如果没有找到,尝试查找IPv4或IPv6标记后的部分 + if (finalIp.isEmpty) { + for (int i = 0; i < parts.length - 1; i++) { + if (parts[i] == "IPv4" || + parts[i] == "IPv6" || + parts[i] == "TCP" || + parts[i] == "UDP") { + String nextPart = parts[i + 1]; + if (RegExp(r'^[a-zA-Z0-9][-a-zA-Z0-9\.]+\.[a-zA-Z]+:\d+$') + .hasMatch(nextPart) || + RegExp(r'^(\d{1,3}\.){3}\d{1,3}(:\d+)?$') + .hasMatch(nextPart)) { + finalIp = nextPart; + break; + } + } + } + } + + // 如果仍然没有找到,使用默认值 + if (finalIp.isEmpty && parts.length > 3) { + finalIp = parts[3]; // 使用默认位置 + } + + pGroupList.add(Pserver( + id: monitor.id, + name: monitor.name, + address: parts[0], + provider: parts[1], + version: parts[2], + isOfficialServer: false, + isTransferable: true, + ip: finalIp, + )); + } + } catch (e) { + print('Error parsing server data: $e'); + } + }); + } + + if (pageData.publicGroupList.length > 3) { + pageData.publicGroupList[3].monitorList.forEach((monitor) { + try { + List parts = monitor.name.split('/'); + if (parts.length >= 4) { + // 尝试从各个部分中提取IP:端口格式 + String finalIp = ""; + + // 遍历所有部分,查找符合IP:端口或域名:端口格式的字符串 + for (String part in parts) { + // 检查是否符合IP:端口或域名:端口格式 + bool isIpPort = + RegExp(r'^(\d{1,3}\.){3}\d{1,3}(:\d+)?$') + .hasMatch(part); + bool isDomainPort = RegExp( + r'^[a-zA-Z0-9][-a-zA-Z0-9\.]+\.[a-zA-Z]+:\d+$') + .hasMatch(part); + + if (isIpPort || isDomainPort) { + finalIp = part; + break; + } + } + + // 如果没有找到,尝试查找IPv4或IPv6标记后的部分 + if (finalIp.isEmpty) { + for (int i = 0; i < parts.length - 1; i++) { + if (parts[i] == "IPv4" || + parts[i] == "IPv6" || + parts[i] == "TCP" || + parts[i] == "UDP") { + String nextPart = parts[i + 1]; + if (RegExp(r'^[a-zA-Z0-9][-a-zA-Z0-9\.]+\.[a-zA-Z]+:\d+$') + .hasMatch(nextPart) || + RegExp(r'^(\d{1,3}\.){3}\d{1,3}(:\d+)?$') + .hasMatch(nextPart)) { + finalIp = nextPart; + break; + } + } + } + } + + // 如果仍然没有找到,使用默认值 + if (finalIp.isEmpty && parts.length > 3) { + finalIp = parts[3]; // 使用默认位置 + } + + pGroupList.add(Pserver( + id: monitor.id, + name: monitor.name, + address: parts[0], + provider: parts[1], + version: parts[2], + isOfficialServer: false, + isTransferable: false, + ip: finalIp, + )); + } + } catch (e) { + print('Error parsing server data: $e'); + } + }); + } + + // 更新状态 + ref.read(pServerProvider.notifier).state = pGroupList; + + // 显示成功提示 + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(pGroupList.isEmpty + ? '未找到可用的服务器数据' + : '成功获取并解析 ${pGroupList.length} 个服务器数据'), + ), + ); + } + } catch (e) { + print('JSON 解析错误: $e'); + // 确保状态被设置为空列表,而不是null + ref.read(pServerProvider.notifier).state = []; + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('数据解析失败: $e')), + ); + } + } + } else { + print('未找到目标数据标记'); + // 确保状态被设置为空列表 + ref.read(pServerProvider.notifier).state = []; + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('未找到目标数据标记')), + ); + } + } + } catch (e) { + print('数据提取错误: $e'); + // 确保状态被设置为空列表 + ref.read(pServerProvider.notifier).state = []; + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('数据提取错误: $e')), + ); + } + } + } else { + // 请求失败 + print('请求失败,状态码: ${response.statusCode}'); + // 确保状态被设置为空列表 + ref.read(pServerProvider.notifier).state = []; + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('获取失败,状态码: ${response.statusCode}')), + ); + } + } + } catch (e) { + // 捕获网络错误 + print('网络请求错误: $e'); + // 确保状态被设置为空列表 + ref.read(pServerProvider.notifier).state = []; + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('网络请求错误: $e')), + ); + } + } } } diff --git a/lib/src/rust/api/simple.dart b/lib/src/rust/api/simple.dart index 9cb79fd..23189b4 100644 --- a/lib/src/rust/api/simple.dart +++ b/lib/src/rust/api/simple.dart @@ -37,14 +37,16 @@ Future createServer( required String specifiedIp, required String roomName, required String roomPassword, - required List severurl}) => + required List severurl, + required FlagsC flag}) => RustLib.instance.api.crateApiSimpleCreateServer( username: username, enableDhcp: enableDhcp, specifiedIp: specifiedIp, roomName: roomName, roomPassword: roomPassword, - severurl: severurl); + severurl: severurl, + flag: flag); Future closeAllServer() => RustLib.instance.api.crateApiSimpleCloseAllServer(); @@ -63,6 +65,103 @@ abstract class PeerRoutePair implements RustOpaqueInterface {} // Rust type: RustOpaqueMoi> abstract class Route implements RustOpaqueInterface {} +class FlagsC { + final String defaultProtocol; + final String devName; + final bool enableEncryption; + final bool enableIpv6; + final int mtu; + final bool latencyFirst; + final bool enableExitNode; + final bool noTun; + final bool useSmoltcp; + final String relayNetworkWhitelist; + final bool disableP2P; + final bool relayAllPeerRpc; + final bool disableUdpHolePunching; + + /// string ipv6_listener = 14; \[deprecated = true\]; use -l udp://\[::\]:12345 instead + final bool multiThread; + final int dataCompressAlgo; + final bool bindDevice; + final bool enableKcpProxy; + final bool disableKcpInput; + final bool disableRelayKcp; + final bool proxyForwardBySystem; + + const FlagsC({ + required this.defaultProtocol, + required this.devName, + required this.enableEncryption, + required this.enableIpv6, + required this.mtu, + required this.latencyFirst, + required this.enableExitNode, + required this.noTun, + required this.useSmoltcp, + required this.relayNetworkWhitelist, + required this.disableP2P, + required this.relayAllPeerRpc, + required this.disableUdpHolePunching, + required this.multiThread, + required this.dataCompressAlgo, + required this.bindDevice, + required this.enableKcpProxy, + required this.disableKcpInput, + required this.disableRelayKcp, + required this.proxyForwardBySystem, + }); + + @override + int get hashCode => + defaultProtocol.hashCode ^ + devName.hashCode ^ + enableEncryption.hashCode ^ + enableIpv6.hashCode ^ + mtu.hashCode ^ + latencyFirst.hashCode ^ + enableExitNode.hashCode ^ + noTun.hashCode ^ + useSmoltcp.hashCode ^ + relayNetworkWhitelist.hashCode ^ + disableP2P.hashCode ^ + relayAllPeerRpc.hashCode ^ + disableUdpHolePunching.hashCode ^ + multiThread.hashCode ^ + dataCompressAlgo.hashCode ^ + bindDevice.hashCode ^ + enableKcpProxy.hashCode ^ + disableKcpInput.hashCode ^ + disableRelayKcp.hashCode ^ + proxyForwardBySystem.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FlagsC && + runtimeType == other.runtimeType && + defaultProtocol == other.defaultProtocol && + devName == other.devName && + enableEncryption == other.enableEncryption && + enableIpv6 == other.enableIpv6 && + mtu == other.mtu && + latencyFirst == other.latencyFirst && + enableExitNode == other.enableExitNode && + noTun == other.noTun && + useSmoltcp == other.useSmoltcp && + relayNetworkWhitelist == other.relayNetworkWhitelist && + disableP2P == other.disableP2P && + relayAllPeerRpc == other.relayAllPeerRpc && + disableUdpHolePunching == other.disableUdpHolePunching && + multiThread == other.multiThread && + dataCompressAlgo == other.dataCompressAlgo && + bindDevice == other.bindDevice && + enableKcpProxy == other.enableKcpProxy && + disableKcpInput == other.disableKcpInput && + disableRelayKcp == other.disableRelayKcp && + proxyForwardBySystem == other.proxyForwardBySystem; +} + class KVNetworkStatus { final BigInt totalNodes; final List nodes; diff --git a/lib/src/rust/frb_generated.dart b/lib/src/rust/frb_generated.dart index 319753d..444e073 100644 --- a/lib/src/rust/frb_generated.dart +++ b/lib/src/rust/frb_generated.dart @@ -85,7 +85,8 @@ abstract class RustLibApi extends BaseApi { required String specifiedIp, required String roomName, required String roomPassword, - required List severurl}); + required List severurl, + required FlagsC flag}); Future crateApiSimpleEasytierVersion(); @@ -174,7 +175,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { required String specifiedIp, required String roomName, required String roomPassword, - required List severurl}) { + required List severurl, + required FlagsC flag}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -184,6 +186,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(roomName, serializer); sse_encode_String(roomPassword, serializer); sse_encode_list_String(severurl, serializer); + sse_encode_box_autoadd_flags_c(flag, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2, port: port_); }, @@ -198,7 +201,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { specifiedIp, roomName, roomPassword, - severurl + severurl, + flag ], apiImpl: this, )); @@ -212,7 +216,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { "specifiedIp", "roomName", "roomPassword", - "severurl" + "severurl", + "flag" ], ); @@ -515,6 +520,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as bool; } + @protected + FlagsC dco_decode_box_autoadd_flags_c(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_flags_c(raw); + } + @protected double dco_decode_f_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -527,6 +538,36 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as double; } + @protected + FlagsC dco_decode_flags_c(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 20) + throw Exception('unexpected arr length: expect 20 but see ${arr.length}'); + return FlagsC( + defaultProtocol: dco_decode_String(arr[0]), + devName: dco_decode_String(arr[1]), + enableEncryption: dco_decode_bool(arr[2]), + enableIpv6: dco_decode_bool(arr[3]), + mtu: dco_decode_u_32(arr[4]), + latencyFirst: dco_decode_bool(arr[5]), + enableExitNode: dco_decode_bool(arr[6]), + noTun: dco_decode_bool(arr[7]), + useSmoltcp: dco_decode_bool(arr[8]), + relayNetworkWhitelist: dco_decode_String(arr[9]), + disableP2P: dco_decode_bool(arr[10]), + relayAllPeerRpc: dco_decode_bool(arr[11]), + disableUdpHolePunching: dco_decode_bool(arr[12]), + multiThread: dco_decode_bool(arr[13]), + dataCompressAlgo: dco_decode_i_32(arr[14]), + bindDevice: dco_decode_bool(arr[15]), + enableKcpProxy: dco_decode_bool(arr[16]), + disableKcpInput: dco_decode_bool(arr[17]), + disableRelayKcp: dco_decode_bool(arr[18]), + proxyForwardBySystem: dco_decode_bool(arr[19]), + ); + } + @protected int dco_decode_i_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -657,6 +698,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + int dco_decode_u_32(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as int; + } + @protected BigInt dco_decode_u_64(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -766,6 +813,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8() != 0; } + @protected + FlagsC sse_decode_box_autoadd_flags_c(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_flags_c(deserializer)); + } + @protected double sse_decode_f_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -778,6 +831,52 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getFloat64(); } + @protected + FlagsC sse_decode_flags_c(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_defaultProtocol = sse_decode_String(deserializer); + var var_devName = sse_decode_String(deserializer); + var var_enableEncryption = sse_decode_bool(deserializer); + var var_enableIpv6 = sse_decode_bool(deserializer); + var var_mtu = sse_decode_u_32(deserializer); + var var_latencyFirst = sse_decode_bool(deserializer); + var var_enableExitNode = sse_decode_bool(deserializer); + var var_noTun = sse_decode_bool(deserializer); + var var_useSmoltcp = sse_decode_bool(deserializer); + var var_relayNetworkWhitelist = sse_decode_String(deserializer); + var var_disableP2P = sse_decode_bool(deserializer); + var var_relayAllPeerRpc = sse_decode_bool(deserializer); + var var_disableUdpHolePunching = sse_decode_bool(deserializer); + var var_multiThread = sse_decode_bool(deserializer); + var var_dataCompressAlgo = sse_decode_i_32(deserializer); + var var_bindDevice = sse_decode_bool(deserializer); + var var_enableKcpProxy = sse_decode_bool(deserializer); + var var_disableKcpInput = sse_decode_bool(deserializer); + var var_disableRelayKcp = sse_decode_bool(deserializer); + var var_proxyForwardBySystem = sse_decode_bool(deserializer); + return FlagsC( + defaultProtocol: var_defaultProtocol, + devName: var_devName, + enableEncryption: var_enableEncryption, + enableIpv6: var_enableIpv6, + mtu: var_mtu, + latencyFirst: var_latencyFirst, + enableExitNode: var_enableExitNode, + noTun: var_noTun, + useSmoltcp: var_useSmoltcp, + relayNetworkWhitelist: var_relayNetworkWhitelist, + disableP2P: var_disableP2P, + relayAllPeerRpc: var_relayAllPeerRpc, + disableUdpHolePunching: var_disableUdpHolePunching, + multiThread: var_multiThread, + dataCompressAlgo: var_dataCompressAlgo, + bindDevice: var_bindDevice, + enableKcpProxy: var_enableKcpProxy, + disableKcpInput: var_disableKcpInput, + disableRelayKcp: var_disableRelayKcp, + proxyForwardBySystem: var_proxyForwardBySystem); + } + @protected int sse_decode_i_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -940,6 +1039,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (var_field0, var_field1); } + @protected + int sse_decode_u_32(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getUint32(); + } + @protected BigInt sse_decode_u_64(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1049,6 +1154,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8(self ? 1 : 0); } + @protected + void sse_encode_box_autoadd_flags_c(FlagsC self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_flags_c(self, serializer); + } + @protected void sse_encode_f_32(double self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1061,6 +1172,31 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putFloat64(self); } + @protected + void sse_encode_flags_c(FlagsC self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.defaultProtocol, serializer); + sse_encode_String(self.devName, serializer); + sse_encode_bool(self.enableEncryption, serializer); + sse_encode_bool(self.enableIpv6, serializer); + sse_encode_u_32(self.mtu, serializer); + sse_encode_bool(self.latencyFirst, serializer); + sse_encode_bool(self.enableExitNode, serializer); + sse_encode_bool(self.noTun, serializer); + sse_encode_bool(self.useSmoltcp, serializer); + sse_encode_String(self.relayNetworkWhitelist, serializer); + sse_encode_bool(self.disableP2P, serializer); + sse_encode_bool(self.relayAllPeerRpc, serializer); + sse_encode_bool(self.disableUdpHolePunching, serializer); + sse_encode_bool(self.multiThread, serializer); + sse_encode_i_32(self.dataCompressAlgo, serializer); + sse_encode_bool(self.bindDevice, serializer); + sse_encode_bool(self.enableKcpProxy, serializer); + sse_encode_bool(self.disableKcpInput, serializer); + sse_encode_bool(self.disableRelayKcp, serializer); + sse_encode_bool(self.proxyForwardBySystem, serializer); + } + @protected void sse_encode_i_32(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1183,6 +1319,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { self.$2, serializer); } + @protected + void sse_encode_u_32(int self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putUint32(self); + } + @protected void sse_encode_u_64(BigInt self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/lib/src/rust/frb_generated.io.dart b/lib/src/rust/frb_generated.io.dart index 74f9ef2..e31022f 100644 --- a/lib/src/rust/frb_generated.io.dart +++ b/lib/src/rust/frb_generated.io.dart @@ -77,12 +77,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool dco_decode_bool(dynamic raw); + @protected + FlagsC dco_decode_box_autoadd_flags_c(dynamic raw); + @protected double dco_decode_f_32(dynamic raw); @protected double dco_decode_f_64(dynamic raw); + @protected + FlagsC dco_decode_flags_c(dynamic raw); + @protected int dco_decode_i_32(dynamic raw); @@ -130,6 +136,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) dco_decode_record_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_peer_info_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_route( dynamic raw); + @protected + int dco_decode_u_32(dynamic raw); + @protected BigInt dco_decode_u_64(dynamic raw); @@ -188,12 +197,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); + @protected + FlagsC sse_decode_box_autoadd_flags_c(SseDeserializer deserializer); + @protected double sse_decode_f_32(SseDeserializer deserializer); @protected double sse_decode_f_64(SseDeserializer deserializer); + @protected + FlagsC sse_decode_flags_c(SseDeserializer deserializer); + @protected int sse_decode_i_32(SseDeserializer deserializer); @@ -242,6 +257,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) sse_decode_record_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_peer_info_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_route( SseDeserializer deserializer); + @protected + int sse_decode_u_32(SseDeserializer deserializer); + @protected BigInt sse_decode_u_64(SseDeserializer deserializer); @@ -300,12 +318,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_bool(bool self, SseSerializer serializer); + @protected + void sse_encode_box_autoadd_flags_c(FlagsC self, SseSerializer serializer); + @protected void sse_encode_f_32(double self, SseSerializer serializer); @protected void sse_encode_f_64(double self, SseSerializer serializer); + @protected + void sse_encode_flags_c(FlagsC self, SseSerializer serializer); + @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -355,6 +379,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { sse_encode_record_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_peer_info_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_route( (List, List) self, SseSerializer serializer); + @protected + void sse_encode_u_32(int self, SseSerializer serializer); + @protected void sse_encode_u_64(BigInt self, SseSerializer serializer); diff --git a/lib/src/rust/frb_generated.web.dart b/lib/src/rust/frb_generated.web.dart index 00dbb1b..8504568 100644 --- a/lib/src/rust/frb_generated.web.dart +++ b/lib/src/rust/frb_generated.web.dart @@ -79,12 +79,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool dco_decode_bool(dynamic raw); + @protected + FlagsC dco_decode_box_autoadd_flags_c(dynamic raw); + @protected double dco_decode_f_32(dynamic raw); @protected double dco_decode_f_64(dynamic raw); + @protected + FlagsC dco_decode_flags_c(dynamic raw); + @protected int dco_decode_i_32(dynamic raw); @@ -132,6 +138,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) dco_decode_record_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_peer_info_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_route( dynamic raw); + @protected + int dco_decode_u_32(dynamic raw); + @protected BigInt dco_decode_u_64(dynamic raw); @@ -190,12 +199,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); + @protected + FlagsC sse_decode_box_autoadd_flags_c(SseDeserializer deserializer); + @protected double sse_decode_f_32(SseDeserializer deserializer); @protected double sse_decode_f_64(SseDeserializer deserializer); + @protected + FlagsC sse_decode_flags_c(SseDeserializer deserializer); + @protected int sse_decode_i_32(SseDeserializer deserializer); @@ -244,6 +259,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) sse_decode_record_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_peer_info_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_route( SseDeserializer deserializer); + @protected + int sse_decode_u_32(SseDeserializer deserializer); + @protected BigInt sse_decode_u_64(SseDeserializer deserializer); @@ -302,12 +320,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_bool(bool self, SseSerializer serializer); + @protected + void sse_encode_box_autoadd_flags_c(FlagsC self, SseSerializer serializer); + @protected void sse_encode_f_32(double self, SseSerializer serializer); @protected void sse_encode_f_64(double self, SseSerializer serializer); + @protected + void sse_encode_flags_c(FlagsC self, SseSerializer serializer); + @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -357,6 +381,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { sse_encode_record_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_peer_info_list_auto_owned_rust_opaque_flutter_rust_bridgefor_generated_rust_auto_opaque_inner_route( (List, List) self, SseSerializer serializer); + @protected + void sse_encode_u_32(int self, SseSerializer serializer); + @protected void sse_encode_u_64(BigInt self, SseSerializer serializer); diff --git a/lib/utils/kv_state.dart b/lib/utils/kv_state.dart index c658e42..76b4dca 100644 --- a/lib/utils/kv_state.dart +++ b/lib/utils/kv_state.dart @@ -1,104 +1,436 @@ import 'package:astral/src/rust/api/simple.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../config/app_config.dart'; -class KM extends ChangeNotifier { - final _config = AppConfig(); - int _count = 0; - int get count => _count; +// 全局配置实例 +final appConfigProvider = Provider((ref) => AppConfig()); - void increment() { - _count++; - notifyListeners(); // 通知监听器重建UI - } +// 计数器相关 +class CountNotifier extends StateNotifier { + CountNotifier() : super(0); -// 房间名 - String get roomName => _config.roomName; - set roomName(String value) { + void increment() => state++; +} + +final countProvider = + StateNotifierProvider((ref) => CountNotifier()); + +// 房间名相关 +class RoomNameNotifier extends StateNotifier { + final AppConfig _config; + + RoomNameNotifier(this._config) : super(_config.roomName); + + void setRoomName(String value) { _config.setRoomName(value); - notifyListeners(); // 通知监听器重建UI + state = value; } +} - //房间密码设置 - String get roomPassword => _config.roomPassword; - set roomPassword(String value) { +final roomNameProvider = StateNotifierProvider((ref) { + return RoomNameNotifier(ref.watch(appConfigProvider)); +}); + +// 房间密码相关 +class RoomPasswordNotifier extends StateNotifier { + final AppConfig _config; + + RoomPasswordNotifier(this._config) : super(_config.roomPassword); + + void setRoomPassword(String value) { _config.setRoomPassword(value); - notifyListeners(); + state = value; } +} - //用户名设置 - String get username => _config.username; - set username(String value) { +final roomPasswordProvider = + StateNotifierProvider((ref) { + return RoomPasswordNotifier(ref.watch(appConfigProvider)); +}); + +// 用户名相关 +class UsernameNotifier extends StateNotifier { + final AppConfig _config; + + UsernameNotifier(this._config) : super(_config.username); + + void setUsername(String value) { _config.setUsername(value); - notifyListeners(); + state = value; } +} - //虚拟IP设置 - String get virtualIP => _config.virtualIP; - set virtualIP(String value) { +final usernameProvider = StateNotifierProvider((ref) { + return UsernameNotifier(ref.watch(appConfigProvider)); +}); + +// 虚拟IP相关 +class VirtualIPNotifier extends StateNotifier { + final AppConfig _config; + + VirtualIPNotifier(this._config) : super(_config.virtualIP); + + void setVirtualIP(String value) { _config.setVirtualIP(value); - notifyListeners(); + state = value; } +} - //动态获取IP设置 - bool get dynamicIP => _config.dynamicIP; - set dynamicIP(bool value) { +final virtualIPProvider = + StateNotifierProvider((ref) { + return VirtualIPNotifier(ref.watch(appConfigProvider)); +}); + +// 动态获取IP设置相关 +class DynamicIPNotifier extends StateNotifier { + final AppConfig _config; + + DynamicIPNotifier(this._config) : super(_config.dynamicIP); + + void setDynamicIP(bool value) { _config.setDynamicIP(value); - notifyListeners(); + state = value; } +} -//服务器列表 - List> get serverList => _config.serverList; - set serverList(List> value) { +final dynamicIPProvider = StateNotifierProvider((ref) { + return DynamicIPNotifier(ref.watch(appConfigProvider)); +}); + +// 服务器列表相关 +class ServerListNotifier extends StateNotifier>> { + final AppConfig _config; + + ServerListNotifier(this._config) : super(_config.serverList); + + void setServerList(List> value) { List> convertedList = value.map((item) { return Map.from(item); }).toList(); _config.setServerList(convertedList); - notifyListeners(); + state = convertedList; } - //获取选中的服务器 - //获取选中的服务器 - List get serverIP { - try { - final selected = - serverList.where((server) => server['selected'] == true).toList(); - - if (selected.isEmpty && serverList.isNotEmpty) { - final firstServer = serverList.first; - if (firstServer['url'] is String) { - return [firstServer['url'] as String]; - } - return []; - } - - return selected - .where((server) => server['url'] is String) - .map((server) => server['url'] as String) - .toList(); - } catch (e) { - debugPrint('获取服务器IP时出错: $e'); - - return []; - } - } - - //设置服务器选中状态 void setServerSelected(String url, bool selected) { - final servers = serverList; + final servers = [...state]; for (var i = 0; i < servers.length; i++) { if (servers[i]['url'] == url) { servers[i]['selected'] = selected; } } - serverList = servers; - } - - // 节点列表 - List _nodes = []; - List get nodes => _nodes; - set nodes(List value) { - _nodes = value; - notifyListeners(); + setServerList(servers); } } + +final serverListProvider = + StateNotifierProvider>>( + (ref) { + return ServerListNotifier(ref.watch(appConfigProvider)); +}); + +// 选中的服务器IP +final serverIPProvider = Provider>((ref) { + final serverList = ref.watch(serverListProvider); + + try { + final selected = + serverList.where((server) => server['selected'] == true).toList(); + + if (selected.isEmpty && serverList.isNotEmpty) { + final firstServer = serverList.first; + if (firstServer['url'] is String) { + return [firstServer['url'] as String]; + } + return []; + } + + return selected + .where((server) => server['url'] is String) + .map((server) => server['url'] as String) + .toList(); + } catch (e) { + debugPrint('获取服务器IP时出错: $e'); + return []; + } +}); + +//返回选中的服务器 +final selectedServerProvider = Provider>((ref) { + final serverList = ref.watch(serverListProvider); + final selected = + serverList.where((server) => server['selected'] == true).toList(); + if (selected.isEmpty) { + return []; + } + + // 将选中的服务器转换为ServerConfig对象列表 + return selected + .map((server) => ServerConfig( + url: server['url'] ?? '', + name: server['name'] ?? '', + selected: true, + tcp: server['tcp'] ?? true, + udp: server['udp'] ?? true, + ws: server['ws'] ?? false, + wss: server['wss'] ?? false, + quic: server['quic'] ?? false, + )) + .toList(); +}); + +// 节点列表相关 +class NodesNotifier extends StateNotifier> { + NodesNotifier() : super([]); + + void setNodes(List value) { + state = value; + } +} + +final nodesProvider = StateNotifierProvider>( + (ref) => NodesNotifier()); + +// 高级配置相关 +class AdvancedConfigNotifier extends StateNotifier> { + final AppConfig _config; + + AdvancedConfigNotifier(this._config) + : super({ + 'defaultProtocol': _config.advanced.defaultProtocol, + 'devName': _config.advanced.devName, + 'enableEncryption': _config.advanced.enableEncryption, + 'enableIpv6': _config.advanced.enableIpv6, + 'mtu': _config.advanced.mtu, + 'latencyFirst': _config.advanced.latencyFirst, + 'enableExitNode': _config.advanced.enableExitNode, + 'proxyForwardBySystem': _config.advanced.proxyForwardBySystem, + 'noTun': _config.advanced.noTun, + 'useSmoltcp': _config.advanced.useSmoltcp, + 'relayNetworkWhitelist': _config.advanced.relayNetworkWhitelist, + 'disableP2p': _config.advanced.disableP2p, + 'relayAllPeerRpc': _config.advanced.relayAllPeerRpc, + 'disableUdpHolePunching': _config.advanced.disableUdpHolePunching, + 'multiThread': _config.advanced.multiThread, + 'dataCompressAlgo': _config.advanced.dataCompressAlgo, + 'bindDevice': _config.advanced.bindDevice, + 'enableKcpProxy': _config.advanced.enableKcpProxy, + 'disableKcpInput': _config.advanced.disableKcpInput, + 'disableRelayKcp': _config.advanced.disableRelayKcp, + }); + + // 更新单个配置项 + Future updateConfig(String key, dynamic value) async { + if (state.containsKey(key) && state[key] != value) { + final newState = {...state, key: value}; + state = newState; + + // 使用通用方法更新配置 + await _config.updateAdvancedConfig( + defaultProtocol: key == 'defaultProtocol' ? value : null, + devName: key == 'devName' ? value : null, + enableEncryption: key == 'enableEncryption' ? value : null, + enableIpv6: key == 'enableIpv6' ? value : null, + mtu: key == 'mtu' ? value : null, + latencyFirst: key == 'latencyFirst' ? value : null, + enableExitNode: key == 'enableExitNode' ? value : null, + proxyForwardBySystem: key == 'proxyForwardBySystem' ? value : null, + noTun: key == 'noTun' ? value : null, + useSmoltcp: key == 'useSmoltcp' ? value : null, + relayNetworkWhitelist: key == 'relayNetworkWhitelist' ? value : null, + disableP2p: key == 'disableP2p' ? value : null, + relayAllPeerRpc: key == 'relayAllPeerRpc' ? value : null, + disableUdpHolePunching: key == 'disableUdpHolePunching' ? value : null, + multiThread: key == 'multiThread' ? value : null, + dataCompressAlgo: key == 'dataCompressAlgo' ? value : null, + bindDevice: key == 'bindDevice' ? value : null, + enableKcpProxy: key == 'enableKcpProxy' ? value : null, + disableKcpInput: key == 'disableKcpInput' ? value : null, + disableRelayKcp: key == 'disableRelayKcp' ? value : null, + ); + } + } + + // 批量更新配置 + Future updateMultipleConfigs(Map updates) async { + final newState = {...state, ...updates}; + state = newState; + + // 使用通用方法更新配置 + await _config.updateAdvancedConfig( + defaultProtocol: updates.containsKey('defaultProtocol') + ? updates['defaultProtocol'] + : null, + devName: updates.containsKey('devName') ? updates['devName'] : null, + enableEncryption: updates.containsKey('enableEncryption') + ? updates['enableEncryption'] + : null, + enableIpv6: + updates.containsKey('enableIpv6') ? updates['enableIpv6'] : null, + mtu: updates.containsKey('mtu') ? updates['mtu'] : null, + latencyFirst: + updates.containsKey('latencyFirst') ? updates['latencyFirst'] : null, + enableExitNode: updates.containsKey('enableExitNode') + ? updates['enableExitNode'] + : null, + proxyForwardBySystem: updates.containsKey('proxyForwardBySystem') + ? updates['proxyForwardBySystem'] + : null, + noTun: updates.containsKey('noTun') ? updates['noTun'] : null, + useSmoltcp: + updates.containsKey('useSmoltcp') ? updates['useSmoltcp'] : null, + relayNetworkWhitelist: updates.containsKey('relayNetworkWhitelist') + ? updates['relayNetworkWhitelist'] + : null, + disableP2p: + updates.containsKey('disableP2p') ? updates['disableP2p'] : null, + relayAllPeerRpc: updates.containsKey('relayAllPeerRpc') + ? updates['relayAllPeerRpc'] + : null, + disableUdpHolePunching: updates.containsKey('disableUdpHolePunching') + ? updates['disableUdpHolePunching'] + : null, + multiThread: + updates.containsKey('multiThread') ? updates['multiThread'] : null, + dataCompressAlgo: updates.containsKey('dataCompressAlgo') + ? updates['dataCompressAlgo'] + : null, + bindDevice: + updates.containsKey('bindDevice') ? updates['bindDevice'] : null, + enableKcpProxy: updates.containsKey('enableKcpProxy') + ? updates['enableKcpProxy'] + : null, + disableKcpInput: updates.containsKey('disableKcpInput') + ? updates['disableKcpInput'] + : null, + disableRelayKcp: updates.containsKey('disableRelayKcp') + ? updates['disableRelayKcp'] + : null, + ); + } + + // 为每个配置项提供单独的更新方法 + Future setDefaultProtocol(String value) async => + updateConfig('defaultProtocol', value); + Future setDevName(String value) async => updateConfig('devName', value); + Future setEnableEncryption(bool value) async => + updateConfig('enableEncryption', value); + Future setEnableIpv6(bool value) async => + updateConfig('enableIpv6', value); + Future setMtu(int value) async => updateConfig('mtu', value); + Future setLatencyFirst(bool value) async => + updateConfig('latencyFirst', value); + Future setEnableExitNode(bool value) async => + updateConfig('enableExitNode', value); + Future setProxyForwardBySystem(bool value) async => + updateConfig('proxyForwardBySystem', value); + Future setNoTun(bool value) async => updateConfig('noTun', value); + Future setUseSmoltcp(bool value) async => + updateConfig('useSmoltcp', value); + Future setRelayNetworkWhitelist(String value) async => + updateConfig('relayNetworkWhitelist', value); + Future setDisableP2p(bool value) async => + updateConfig('disableP2p', value); + Future setRelayAllPeerRpc(bool value) async => + updateConfig('relayAllPeerRpc', value); + Future setDisableUdpHolePunching(bool value) async => + updateConfig('disableUdpHolePunching', value); + Future setMultiThread(bool value) async => + updateConfig('multiThread', value); + Future setDataCompressAlgo(String value) async => + updateConfig('dataCompressAlgo', value); + Future setBindDevice(bool value) async => + updateConfig('bindDevice', value); + Future setEnableKcpProxy(bool value) async => + updateConfig('enableKcpProxy', value); + Future setDisableKcpInput(bool value) async => + updateConfig('disableKcpInput', value); + Future setDisableRelayKcp(bool value) async => + updateConfig('disableRelayKcp', value); +} + +final advancedConfigProvider = + StateNotifierProvider>((ref) { + return AdvancedConfigNotifier(ref.watch(appConfigProvider)); +}); + +// 为每个高级配置项创建单独的Provider,方便在UI中使用 +final defaultProtocolProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['defaultProtocol']; +}); + +final devNameProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['devName']; +}); + +final enableEncryptionProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['enableEncryption']; +}); + +final enableIpv6Provider = Provider((ref) { + return ref.watch(advancedConfigProvider)['enableIpv6']; +}); + +final mtuProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['mtu']; +}); + +final latencyFirstProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['latencyFirst']; +}); + +final enableExitNodeProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['enableExitNode']; +}); + +final proxyForwardBySystemProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['proxyForwardBySystem']; +}); + +final noTunProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['noTun']; +}); + +final useSmoltcpProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['useSmoltcp']; +}); + +final relayNetworkWhitelistProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['relayNetworkWhitelist']; +}); + +final disableP2pProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['disableP2p']; +}); + +final relayAllPeerRpcProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['relayAllPeerRpc']; +}); + +final disableUdpHolePunchingProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['disableUdpHolePunching']; +}); + +final multiThreadProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['multiThread']; +}); + +final dataCompressAlgoProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['dataCompressAlgo']; +}); + +final bindDeviceProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['bindDevice']; +}); + +final enableKcpProxyProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['enableKcpProxy']; +}); + +final disableKcpInputProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['disableKcpInput']; +}); + +final disableRelayKcpProvider = Provider((ref) { + return ref.watch(advancedConfigProvider)['disableRelayKcp']; +}); diff --git a/lib/utils/serverjs.dart b/lib/utils/serverjs.dart new file mode 100644 index 0000000..06a9d65 --- /dev/null +++ b/lib/utils/serverjs.dart @@ -0,0 +1,198 @@ +import 'dart:convert'; + +// 主入口类 +class StatusPageData { + final Config config; + final Incident incident; + final List publicGroupList; + + StatusPageData({ + required this.config, + required this.incident, + required this.publicGroupList, + }); + + factory StatusPageData.fromJson(Map json) => StatusPageData( + config: Config.fromJson(json['config']), + incident: Incident.fromJson(json['incident']), + publicGroupList: List.from( + json['publicGroupList'].map((x) => PublicGroup.fromJson(x))), + ); +} + +// 配置信息 +class Config { + final String slug; + final String title; + final String description; + final String icon; + final String theme; + final bool published; + final bool showTags; + final String customCSS; + final String footerText; + final bool showPoweredBy; + final dynamic googleAnalyticsId; + final bool showCertificateExpiry; + + Config({ + required this.slug, + required this.title, + required this.description, + required this.icon, + required this.theme, + required this.published, + required this.showTags, + required this.customCSS, + required this.footerText, + required this.showPoweredBy, + this.googleAnalyticsId, + required this.showCertificateExpiry, + }); + + factory Config.fromJson(Map json) => Config( + slug: json['slug'], + title: json['title'], + description: json['description'], + icon: json['icon'], + theme: json['theme'], + published: json['published'], + showTags: json['showTags'], + customCSS: json['customCSS'], + footerText: json['footerText'], + showPoweredBy: json['showPoweredBy'], + googleAnalyticsId: json['googleAnalyticsId'], + showCertificateExpiry: json['showCertificateExpiry'], + ); +} + +// 事件信息 +class Incident { + final int id; + final String style; + final String title; + final String content; + final int pin; + final DateTime createdDate; + final DateTime lastUpdatedDate; + + Incident({ + required this.id, + required this.style, + required this.title, + required this.content, + required this.pin, + required this.createdDate, + required this.lastUpdatedDate, + }); + + factory Incident.fromJson(Map json) => Incident( + id: json['id'], + style: json['style'], + title: json['title'], + content: json['content'], + pin: json['pin'], + createdDate: DateTime.parse(json['createdDate']), + lastUpdatedDate: DateTime.parse(json['lastUpdatedDate']), + ); +} + +// 公共服务器组 +class PublicGroup { + final int id; + final String name; + final int weight; + final List monitorList; + + PublicGroup({ + required this.id, + required this.name, + required this.weight, + required this.monitorList, + }); + + factory PublicGroup.fromJson(Map json) => PublicGroup( + id: json['id'], + name: json['name'], + weight: json['weight'], + monitorList: List.from( + json['monitorList'].map((x) => Monitor.fromJson(x))), + ); +} + +// 监控项 +class Monitor { + final int id; + final String name; + final int sendUrl; + final String type; + final List tags; + final int? certExpiryDaysRemaining; + final bool? validCert; + + Monitor({ + required this.id, + required this.name, + required this.sendUrl, + required this.type, + required this.tags, + this.certExpiryDaysRemaining, + this.validCert, + }); + + factory Monitor.fromJson(Map json) => Monitor( + id: json['id'], + name: json['name'], + sendUrl: json['sendUrl'], + type: json['type'], + tags: List.from(json['tags'].map((x) => Tag.fromJson(x))), + certExpiryDaysRemaining: json['certExpiryDaysRemaining'], + validCert: json['validCert'], + ); +} + +// 标签 +class Tag { + final int id; + final int monitorId; + final int tagId; + final String value; + final String name; + final String color; + + Tag({ + required this.id, + required this.monitorId, + required this.tagId, + required this.value, + required this.name, + required this.color, + }); + + factory Tag.fromJson(Map json) => Tag( + id: json['id'], + monitorId: json['monitor_id'], + tagId: json['tag_id'], + value: json['value'], + name: json['name'], + color: json['color'], + ); +} + +// // 使用示例 +// void main() { +// final jsonString = '''{/* 你的原始JSON数据 */}'''; + +// final data = StatusPageData.fromJson(jsonDecode(jsonString)); + +// // 示例:获取第一个服务器组的名称 +// print('第一组名称: ${data.publicGroupList.first.name}'); + +// // 示例:列出所有可中转的服务器 +// final transferableServers = data.publicGroupList +// .expand((group) => group.monitorList) +// .where((monitor) => monitor.tags.any((tag) => tag.name == '可中转')) +// .toList(); + +// print('可中转服务器数量: ${transferableServers.length}'); +// } diff --git a/pubspec.lock b/pubspec.lock index 5312922..6224fab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,24 +5,37 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57 - url: "https://pub.flutter-io.cn" + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + url: "https://pub.dev" source: hosted - version: "80.0.0" + version: "76.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.3" analyzer: dependency: transitive description: name: analyzer - sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e" - url: "https://pub.flutter-io.cn" + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "6.11.0" + archive: + dependency: transitive + description: + name: archive + sha256: "0c64e928dcbefddecd234205422bcfc2b5e6d31be0b86fef0d0dd48d7b4c9742" + url: "https://pub.dev" + source: hosted + version: "4.0.4" args: dependency: transitive description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.7.0" async: @@ -30,7 +43,7 @@ packages: description: name: async sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.12.0" boolean_selector: @@ -38,7 +51,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" build: @@ -46,7 +59,7 @@ packages: description: name: build sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2" build_cli_annotations: @@ -54,7 +67,7 @@ packages: description: name: build_cli_annotations sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" build_config: @@ -62,7 +75,7 @@ packages: description: name: build_config sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.2" build_daemon: @@ -70,7 +83,7 @@ packages: description: name: build_daemon sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.4" build_resolvers: @@ -78,15 +91,15 @@ packages: description: name: build_resolvers sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.4" build_runner: - dependency: "direct dev" + dependency: "direct main" description: name: build_runner sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.15" build_runner_core: @@ -94,7 +107,7 @@ packages: description: name: build_runner_core sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.0.0" built_collection: @@ -102,7 +115,7 @@ packages: description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.1.1" built_value: @@ -110,7 +123,7 @@ packages: description: name: built_value sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.9.5" characters: @@ -118,7 +131,7 @@ packages: description: name: characters sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.0" checked_yaml: @@ -126,7 +139,7 @@ packages: description: name: checked_yaml sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.3" clock: @@ -134,7 +147,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.2" code_builder: @@ -142,7 +155,7 @@ packages: description: name: code_builder sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.10.1" collection: @@ -150,7 +163,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.19.1" convert: @@ -158,7 +171,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.2" crypto: @@ -166,7 +179,7 @@ packages: description: name: crypto sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.6" cupertino_icons: @@ -174,23 +187,23 @@ packages: description: name: cupertino_icons sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.8" dart_style: dependency: transitive description: name: dart_style - sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" - url: "https://pub.flutter-io.cn" + sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.3.8" dbus: dependency: transitive description: name: dbus sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.11" equatable: @@ -198,7 +211,7 @@ packages: description: name: equatable sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.7" fake_async: @@ -206,7 +219,7 @@ packages: description: name: fake_async sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.2" ffi: @@ -214,7 +227,7 @@ packages: description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.4" file: @@ -222,7 +235,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.1" fixnum: @@ -230,7 +243,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" fl_chart: @@ -238,9 +251,25 @@ packages: description: name: fl_chart sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.70.2" + floor_annotation: + dependency: transitive + description: + name: floor_annotation + sha256: a40949580a7ab0eee572686e2d3b1638fd6bd6a753e661d792ab4236b365b23b + url: "https://pub.dev" + source: hosted + version: "1.5.0" + floor_generator: + dependency: "direct main" + description: + name: floor_generator + sha256: "1499b3ab878a807e6fbe6f140dc014124845cd1df3090a113aae5fa7577a1e77" + url: "https://pub.dev" + source: hosted + version: "1.5.0" flutter: dependency: "direct main" description: flutter @@ -251,7 +280,7 @@ packages: description: name: flutter_colorpicker sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" flutter_driver: @@ -264,7 +293,7 @@ packages: description: name: flutter_lints sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.0.0" flutter_local_notifications: @@ -272,7 +301,7 @@ packages: description: name: flutter_local_notifications sha256: d59eeafd6df92174b1d5f68fc9d66634c97ce2e7cfe2293476236547bb19bbbd - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "19.0.0" flutter_local_notifications_linux: @@ -280,7 +309,7 @@ packages: description: name: flutter_local_notifications_linux sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.0.0" flutter_local_notifications_platform_interface: @@ -288,7 +317,7 @@ packages: description: name: flutter_local_notifications_platform_interface sha256: "2569b973fc9d1f63a37410a9f7c1c552081226c597190cb359ef5d5762d1631c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "9.0.0" flutter_local_notifications_windows: @@ -296,7 +325,7 @@ packages: description: name: flutter_local_notifications_windows sha256: f8fc0652a601f83419d623c85723a3e82ad81f92b33eaa9bcc21ea1b94773e6e - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" flutter_localizations: @@ -304,12 +333,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" + url: "https://pub.dev" + source: hosted + version: "2.6.1" flutter_rust_bridge: dependency: "direct main" description: name: flutter_rust_bridge sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.9.0" flutter_staggered_grid_view: @@ -317,7 +354,7 @@ packages: description: name: flutter_staggered_grid_view sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.0" flutter_test: @@ -335,7 +372,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.0" fuchsia_remote_debug_protocol: @@ -348,7 +385,7 @@ packages: description: name: glob sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.3" graphs: @@ -356,15 +393,39 @@ packages: description: name: graphs sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.2" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc + url: "https://pub.dev" + source: hosted + version: "1.1.0" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4" + url: "https://pub.dev" + source: hosted + version: "2.0.1" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" http_multi_server: @@ -372,7 +433,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" http_parser: @@ -380,7 +441,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.2" integration_test: @@ -393,7 +454,7 @@ packages: description: name: intl sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.19.0" io: @@ -401,7 +462,7 @@ packages: description: name: io sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.5" js: @@ -409,7 +470,7 @@ packages: description: name: js sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.2" json_annotation: @@ -417,23 +478,23 @@ packages: description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.9.0" json_serializable: dependency: "direct dev" description: name: json_serializable - sha256: "81f04dee10969f89f604e1249382d46b97a1ccad53872875369622b5bfc9e58a" - url: "https://pub.flutter-io.cn" + sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c + url: "https://pub.dev" source: hosted - version: "6.9.4" + version: "6.9.0" leak_tracker: dependency: transitive description: name: leak_tracker sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.0.8" leak_tracker_flutter_testing: @@ -441,7 +502,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.9" leak_tracker_testing: @@ -449,7 +510,7 @@ packages: description: name: leak_tracker_testing sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.1" lints: @@ -457,7 +518,7 @@ packages: description: name: lints sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.1.1" logging: @@ -465,15 +526,23 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" + macros: + dependency: transitive + description: + name: macros + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + url: "https://pub.dev" + source: hosted + version: "0.1.3-main.0" matcher: dependency: transitive description: name: matcher sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.12.17" material_color_utilities: @@ -481,7 +550,7 @@ packages: description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.11.1" menu_base: @@ -489,7 +558,7 @@ packages: description: name: menu_base sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.1" meta: @@ -497,7 +566,7 @@ packages: description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.16.0" mime: @@ -505,7 +574,7 @@ packages: description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.0" nested: @@ -513,7 +582,7 @@ packages: description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" package_config: @@ -521,7 +590,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" package_info_plus: @@ -529,7 +598,7 @@ packages: description: name: package_info_plus sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.3.0" package_info_plus_platform_interface: @@ -537,23 +606,47 @@ packages: description: name: package_info_plus_platform_interface sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12" + url: "https://pub.dev" + source: hosted + version: "2.2.16" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" path_provider_linux: dependency: transitive description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -561,7 +654,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" path_provider_windows: @@ -569,7 +662,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.0" petitparser: @@ -577,7 +670,7 @@ packages: description: name: petitparser sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.0" platform: @@ -585,7 +678,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.6" plugin_platform_interface: @@ -593,7 +686,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.8" pool: @@ -601,15 +694,23 @@ packages: description: name: pool sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a + url: "https://pub.dev" + source: hosted + version: "6.0.1" process: dependency: transitive description: name: process sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.0.3" provider: @@ -617,7 +718,7 @@ packages: description: name: provider sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.2" pub_semver: @@ -625,7 +726,7 @@ packages: description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" pubspec_parse: @@ -633,9 +734,17 @@ packages: description: name: pubspec_parse sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.0" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" + url: "https://pub.dev" + source: hosted + version: "2.6.1" rust_lib_fltier: dependency: "direct main" description: @@ -648,7 +757,7 @@ packages: description: name: screen_retriever sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_linux: @@ -656,7 +765,7 @@ packages: description: name: screen_retriever_linux sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_macos: @@ -664,7 +773,7 @@ packages: description: name: screen_retriever_macos sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_platform_interface: @@ -672,7 +781,7 @@ packages: description: name: screen_retriever_platform_interface sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_windows: @@ -680,7 +789,7 @@ packages: description: name: screen_retriever_windows sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" shared_preferences: @@ -688,7 +797,7 @@ packages: description: name: shared_preferences sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.2" shared_preferences_android: @@ -696,7 +805,7 @@ packages: description: name: shared_preferences_android sha256: "3ec7210872c4ba945e3244982918e502fa2bfb5230dff6832459ca0e1879b7ad" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.8" shared_preferences_foundation: @@ -704,7 +813,7 @@ packages: description: name: shared_preferences_foundation sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.4" shared_preferences_linux: @@ -712,7 +821,7 @@ packages: description: name: shared_preferences_linux sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.1" shared_preferences_platform_interface: @@ -720,7 +829,7 @@ packages: description: name: shared_preferences_platform_interface sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.1" shared_preferences_web: @@ -728,7 +837,7 @@ packages: description: name: shared_preferences_web sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.3" shared_preferences_windows: @@ -736,7 +845,7 @@ packages: description: name: shared_preferences_windows sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.1" shelf: @@ -744,7 +853,7 @@ packages: description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.2" shelf_web_socket: @@ -752,7 +861,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" shortid: @@ -760,9 +869,17 @@ packages: description: name: shortid sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.2" + simple_sparse_list: + dependency: transitive + description: + name: simple_sparse_list + sha256: aa648fd240fa39b49dcd11c19c266990006006de6699a412de485695910fbc1f + url: "https://pub.dev" + source: hosted + version: "0.1.4" sky_engine: dependency: transitive description: flutter @@ -772,16 +889,16 @@ packages: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" - url: "https://pub.flutter-io.cn" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.5.0" source_helper: dependency: transitive description: name: source_helper sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.5" source_span: @@ -789,23 +906,71 @@ packages: description: name: source_span sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.10.1" + sqflite: + dependency: "direct main" + description: + name: sqflite + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + url: "https://pub.dev" + source: hosted + version: "2.5.5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.12.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" stream_channel: dependency: transitive description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.4" stream_transform: @@ -813,7 +978,7 @@ packages: description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.1" string_scanner: @@ -821,23 +986,39 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" + strings: + dependency: transitive + description: + name: strings + sha256: "482f1511d2cd8ab9f2c4cc148e6837be8a00c03b9a8eaedbc981a84d9c6305d8" + url: "https://pub.dev" + source: hosted + version: "3.2.0" sync_http: dependency: transitive description: name: sync_http sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + url: "https://pub.dev" + source: hosted + version: "3.3.1" system_tray: dependency: "direct main" description: name: system_tray sha256: "40444e5de8ed907822a98694fd031b8accc3cb3c0baa547634ce76189cf3d9cf" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.3" term_glyph: @@ -845,7 +1026,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.2" test_api: @@ -853,7 +1034,7 @@ packages: description: name: test_api sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.4" timezone: @@ -861,7 +1042,7 @@ packages: description: name: timezone sha256: ffc9d5f4d1193534ef051f9254063fa53d588609418c84299956c3db9383587d - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.10.0" timing: @@ -869,7 +1050,7 @@ packages: description: name: timing sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.2" tray_manager: @@ -877,7 +1058,7 @@ packages: description: name: tray_manager sha256: "80be6c508159a6f3c57983de795209ac13453e9832fd574143b06dceee188ed2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.2" typed_data: @@ -885,15 +1066,23 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.0" + unicode: + dependency: transitive + description: + name: unicode + sha256: "0d99edbd2e74726bed2e4989713c8bec02e5581628e334d8c88c0271593fb402" + url: "https://pub.dev" + source: hosted + version: "1.1.8" url_launcher: dependency: "direct main" description: name: url_launcher sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.1" url_launcher_android: @@ -901,7 +1090,7 @@ packages: description: name: url_launcher_android sha256: "1d0eae19bd7606ef60fe69ef3b312a437a16549476c42321d5dc1506c9ca3bf4" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.15" url_launcher_ios: @@ -909,7 +1098,7 @@ packages: description: name: url_launcher_ios sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.2" url_launcher_linux: @@ -917,7 +1106,7 @@ packages: description: name: url_launcher_linux sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.1" url_launcher_macos: @@ -925,7 +1114,7 @@ packages: description: name: url_launcher_macos sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" url_launcher_platform_interface: @@ -933,7 +1122,7 @@ packages: description: name: url_launcher_platform_interface sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.2" url_launcher_web: @@ -941,7 +1130,7 @@ packages: description: name: url_launcher_web sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.0" url_launcher_windows: @@ -949,7 +1138,7 @@ packages: description: name: url_launcher_windows sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.4" uuid: @@ -957,7 +1146,7 @@ packages: description: name: uuid sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.7" vector_math: @@ -965,7 +1154,7 @@ packages: description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.4" vm_service: @@ -973,7 +1162,7 @@ packages: description: name: vm_service sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "14.3.1" watcher: @@ -981,7 +1170,7 @@ packages: description: name: watcher sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" web: @@ -989,7 +1178,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" web_socket: @@ -997,7 +1186,7 @@ packages: description: name: web_socket sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.6" web_socket_channel: @@ -1005,7 +1194,7 @@ packages: description: name: web_socket_channel sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" webdriver: @@ -1013,7 +1202,7 @@ packages: description: name: webdriver sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.4" win32: @@ -1021,7 +1210,7 @@ packages: description: name: win32 sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.12.0" window_manager: @@ -1029,7 +1218,7 @@ packages: description: name: window_manager sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.4.3" windows_notification: @@ -1037,7 +1226,7 @@ packages: description: name: windows_notification sha256: be3e650874615f315402c9b9f3656e29af156709c4b5cc272cb4ca0ab7ba94a8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" xdg_directories: @@ -1045,7 +1234,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" xml: @@ -1053,7 +1242,7 @@ packages: description: name: xml sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.5.0" yaml: @@ -1061,7 +1250,7 @@ packages: description: name: yaml sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.3" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index 8c72100..21036a9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,11 +52,18 @@ dependencies: flutter_local_notifications: ^19.0.0 windows_notification: ^1.3.0 fl_chart: ^0.70.2 + sqflite: ^2.4.2 + path: ^1.9.1 + flutter_riverpod: ^2.6.1 + floor_generator: ^1.5.0 + build_runner: ^2.4.15 + hive: ^2.2.3 + hive_flutter: ^1.1.0 + http: ^1.1.0 dev_dependencies: flutter_test: sdk: flutter - build_runner: ^2.4.6 json_serializable: ^6.7.1 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is @@ -64,6 +71,8 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 + hive_generator: ^2.0.0 + build_runner: ^2.3.3 integration_test: sdk: flutter diff --git a/rust/easytier/src/common/constants.rs b/rust/easytier/src/common/constants.rs index 7272def..deff54d 100644 --- a/rust/easytier/src/common/constants.rs +++ b/rust/easytier/src/common/constants.rs @@ -30,6 +30,6 @@ pub const WIN_SERVICE_WORK_DIR_REG_KEY: &str = "SOFTWARE\\EasyTier\\Service\\Wor pub const EASYTIER_VERSION: &str = git_version::git_version!( args = ["--abbrev=8", "--always", "--dirty=~"], prefix = concat!(env!("CARGO_PKG_VERSION"), "-"), - suffix = "", - fallback = "astral" + suffix = "-astral", + fallback = concat!(env!("CARGO_PKG_VERSION"), "-astral") ); diff --git a/rust/src/api/simple.rs b/rust/src/api/simple.rs index 4fb10da..7f67015 100644 --- a/rust/src/api/simple.rs +++ b/rust/src/api/simple.rs @@ -2,7 +2,7 @@ pub use std::collections::BTreeMap; //use BTreeMap use dashmap::DashMap; pub use easytier::common::global_ctx::{EventBusSubscriber, GlobalCtxEvent}; -use easytier::common::scoped_task::ScopedTask; +use easytier::common::{config::Flags, scoped_task::ScopedTask}; pub use easytier::{ common::config::{ConfigLoader, TomlConfigLoader}, launcher::NetworkInstance, @@ -158,6 +158,7 @@ fn create_config() -> TomlConfigLoader { cfg.set_listeners(vec![ "tcp://0.0.0.0:11010".to_string().parse().unwrap(), "udp://0.0.0.0:11010".to_string().parse().unwrap(), + "tcp://[::]:11010".to_string().parse().unwrap(), ]); // cfg.set_inst_name("default".to_string()); // cfg.set_inst_name(name); @@ -566,6 +567,30 @@ pub fn get_running_info() -> String { .unwrap_or_else(|| "{}".to_string()) } +pub struct FlagsC { + pub default_protocol: String, + pub dev_name: String, + pub enable_encryption: bool, + pub enable_ipv6: bool, + pub mtu: u32, + pub latency_first: bool, + pub enable_exit_node: bool, + pub no_tun: bool, + pub use_smoltcp: bool, + pub relay_network_whitelist: String, + pub disable_p2p: bool, + pub relay_all_peer_rpc: bool, + pub disable_udp_hole_punching: bool, + /// string ipv6_listener = 14; \[deprecated = true\]; use -l udp://\[::\]:12345 instead + pub multi_thread: bool, + pub data_compress_algo: i32, + pub bind_device: bool, + pub enable_kcp_proxy: bool, + pub disable_kcp_input: bool, + pub disable_relay_kcp: bool, + pub proxy_forward_by_system: bool, +} + // 创建服务器 pub fn create_server( username: String, @@ -574,6 +599,7 @@ pub fn create_server( room_name: String, room_password: String, severurl: Vec, + flag:FlagsC, ) { RT.spawn(async move { // 创建一个示例配置 @@ -581,6 +607,26 @@ pub fn create_server( cfg.set_hostname(Option::from(username)); cfg.set_dhcp(enable_dhcp); let mut flags = cfg.get_flags(); + flags.default_protocol = flag.default_protocol; + flags.dev_name = flag.dev_name; + flags.enable_encryption = flag.enable_encryption; + flags.enable_ipv6 = flag.enable_ipv6; + flags.mtu = flag.mtu; + flags.latency_first = flag.latency_first; + flags.enable_exit_node = flag.enable_exit_node; + flags.no_tun = flag.no_tun; + flags.use_smoltcp = flag.use_smoltcp; + flags.relay_network_whitelist = flag.relay_network_whitelist; + flags.disable_p2p = flag.disable_p2p; + flags.relay_all_peer_rpc = flag.relay_all_peer_rpc; + flags.disable_udp_hole_punching = flag.disable_udp_hole_punching; + flags.multi_thread = flag.multi_thread; + flags.data_compress_algo = flag.data_compress_algo; + flags.bind_device = flag.bind_device; + flags.enable_kcp_proxy = flag.enable_kcp_proxy; + flags.disable_kcp_input = flag.disable_kcp_input; + flags.disable_relay_kcp = flag.disable_relay_kcp; + flags.proxy_forward_by_system = flag.proxy_forward_by_system; // flags.dev_name = "astral".to_string(); cfg.set_flags(flags); // 创建TCP和UDP连接配置列表 @@ -588,10 +634,7 @@ pub fn create_server( // 为每个服务器URL创建TCP和UDP配置 for url in severurl { peer_configs.push(PeerConfig { - uri: format!("tcp://{}", url).parse().unwrap(), - }); - peer_configs.push(PeerConfig { - uri: format!("udp://{}", url).parse().unwrap(), + uri: format!("{}", url).parse().unwrap(), }); } diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 7422e25..087c280 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -108,6 +108,7 @@ fn wire__crate__api__simple__create_server_impl( let api_room_name = ::sse_decode(&mut deserializer); let api_room_password = ::sse_decode(&mut deserializer); let api_severurl = >::sse_decode(&mut deserializer); + let api_flag = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { @@ -119,6 +120,7 @@ fn wire__crate__api__simple__create_server_impl( api_room_name, api_room_password, api_severurl, + api_flag, ); })?; Ok(output_ok) @@ -510,6 +512,54 @@ impl SseDecode for f64 { } } +impl SseDecode for crate::api::simple::FlagsC { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_defaultProtocol = ::sse_decode(deserializer); + let mut var_devName = ::sse_decode(deserializer); + let mut var_enableEncryption = ::sse_decode(deserializer); + let mut var_enableIpv6 = ::sse_decode(deserializer); + let mut var_mtu = ::sse_decode(deserializer); + let mut var_latencyFirst = ::sse_decode(deserializer); + let mut var_enableExitNode = ::sse_decode(deserializer); + let mut var_noTun = ::sse_decode(deserializer); + let mut var_useSmoltcp = ::sse_decode(deserializer); + let mut var_relayNetworkWhitelist = ::sse_decode(deserializer); + let mut var_disableP2P = ::sse_decode(deserializer); + let mut var_relayAllPeerRpc = ::sse_decode(deserializer); + let mut var_disableUdpHolePunching = ::sse_decode(deserializer); + let mut var_multiThread = ::sse_decode(deserializer); + let mut var_dataCompressAlgo = ::sse_decode(deserializer); + let mut var_bindDevice = ::sse_decode(deserializer); + let mut var_enableKcpProxy = ::sse_decode(deserializer); + let mut var_disableKcpInput = ::sse_decode(deserializer); + let mut var_disableRelayKcp = ::sse_decode(deserializer); + let mut var_proxyForwardBySystem = ::sse_decode(deserializer); + return crate::api::simple::FlagsC { + default_protocol: var_defaultProtocol, + dev_name: var_devName, + enable_encryption: var_enableEncryption, + enable_ipv6: var_enableIpv6, + mtu: var_mtu, + latency_first: var_latencyFirst, + enable_exit_node: var_enableExitNode, + no_tun: var_noTun, + use_smoltcp: var_useSmoltcp, + relay_network_whitelist: var_relayNetworkWhitelist, + disable_p2p: var_disableP2P, + relay_all_peer_rpc: var_relayAllPeerRpc, + disable_udp_hole_punching: var_disableUdpHolePunching, + multi_thread: var_multiThread, + data_compress_algo: var_dataCompressAlgo, + bind_device: var_bindDevice, + enable_kcp_proxy: var_enableKcpProxy, + disable_kcp_input: var_disableKcpInput, + disable_relay_kcp: var_disableRelayKcp, + proxy_forward_by_system: var_proxyForwardBySystem, + }; + } +} + impl SseDecode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -667,6 +717,13 @@ impl SseDecode for (Vec, Vec) { } } +impl SseDecode for u32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u32::().unwrap() + } +} + impl SseDecode for u64 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -790,6 +847,40 @@ impl flutter_rust_bridge::IntoIntoDart> for Route { } } +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::simple::FlagsC { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.default_protocol.into_into_dart().into_dart(), + self.dev_name.into_into_dart().into_dart(), + self.enable_encryption.into_into_dart().into_dart(), + self.enable_ipv6.into_into_dart().into_dart(), + self.mtu.into_into_dart().into_dart(), + self.latency_first.into_into_dart().into_dart(), + self.enable_exit_node.into_into_dart().into_dart(), + self.no_tun.into_into_dart().into_dart(), + self.use_smoltcp.into_into_dart().into_dart(), + self.relay_network_whitelist.into_into_dart().into_dart(), + self.disable_p2p.into_into_dart().into_dart(), + self.relay_all_peer_rpc.into_into_dart().into_dart(), + self.disable_udp_hole_punching.into_into_dart().into_dart(), + self.multi_thread.into_into_dart().into_dart(), + self.data_compress_algo.into_into_dart().into_dart(), + self.bind_device.into_into_dart().into_dart(), + self.enable_kcp_proxy.into_into_dart().into_dart(), + self.disable_kcp_input.into_into_dart().into_dart(), + self.disable_relay_kcp.into_into_dart().into_dart(), + self.proxy_forward_by_system.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::api::simple::FlagsC {} +impl flutter_rust_bridge::IntoIntoDart for crate::api::simple::FlagsC { + fn into_into_dart(self) -> crate::api::simple::FlagsC { + self + } +} // Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::api::simple::KVNetworkStatus { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { @@ -964,6 +1055,32 @@ impl SseEncode for f64 { } } +impl SseEncode for crate::api::simple::FlagsC { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.default_protocol, serializer); + ::sse_encode(self.dev_name, serializer); + ::sse_encode(self.enable_encryption, serializer); + ::sse_encode(self.enable_ipv6, serializer); + ::sse_encode(self.mtu, serializer); + ::sse_encode(self.latency_first, serializer); + ::sse_encode(self.enable_exit_node, serializer); + ::sse_encode(self.no_tun, serializer); + ::sse_encode(self.use_smoltcp, serializer); + ::sse_encode(self.relay_network_whitelist, serializer); + ::sse_encode(self.disable_p2p, serializer); + ::sse_encode(self.relay_all_peer_rpc, serializer); + ::sse_encode(self.disable_udp_hole_punching, serializer); + ::sse_encode(self.multi_thread, serializer); + ::sse_encode(self.data_compress_algo, serializer); + ::sse_encode(self.bind_device, serializer); + ::sse_encode(self.enable_kcp_proxy, serializer); + ::sse_encode(self.disable_kcp_input, serializer); + ::sse_encode(self.disable_relay_kcp, serializer); + ::sse_encode(self.proxy_forward_by_system, serializer); + } +} + impl SseEncode for i32 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1082,6 +1199,13 @@ impl SseEncode for (Vec, Vec) { } } +impl SseEncode for u32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u32::(self).unwrap(); + } +} + impl SseEncode for u64 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { diff --git a/rust_builder/cargokit/build_tool/pubspec.lock b/rust_builder/cargokit/build_tool/pubspec.lock index da34be3..8bc1056 100644 --- a/rust_builder/cargokit/build_tool/pubspec.lock +++ b/rust_builder/cargokit/build_tool/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "67.0.0" adaptive_number: @@ -14,7 +14,7 @@ packages: description: name: adaptive_number sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" analyzer: @@ -22,7 +22,7 @@ packages: description: name: analyzer sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.4.1" args: @@ -30,7 +30,7 @@ packages: description: name: args sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2" async: @@ -38,7 +38,7 @@ packages: description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.13.0" boolean_selector: @@ -46,7 +46,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" collection: @@ -54,7 +54,7 @@ packages: description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.18.0" convert: @@ -62,7 +62,7 @@ packages: description: name: convert sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.1" coverage: @@ -70,7 +70,7 @@ packages: description: name: coverage sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.11.1" crypto: @@ -78,7 +78,7 @@ packages: description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.3" ed25519_edwards: @@ -86,7 +86,7 @@ packages: description: name: ed25519_edwards sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.1" file: @@ -94,7 +94,7 @@ packages: description: name: file sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.4" fixnum: @@ -102,7 +102,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" frontend_server_client: @@ -110,7 +110,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.0" github: @@ -118,7 +118,7 @@ packages: description: name: github sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "9.17.0" glob: @@ -126,7 +126,7 @@ packages: description: name: glob sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.3" hex: @@ -134,7 +134,7 @@ packages: description: name: hex sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" http: @@ -142,7 +142,7 @@ packages: description: name: http sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" http_multi_server: @@ -150,7 +150,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" http_parser: @@ -158,7 +158,7 @@ packages: description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.2" io: @@ -166,7 +166,7 @@ packages: description: name: io sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.5" js: @@ -174,7 +174,7 @@ packages: description: name: js sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.2" json_annotation: @@ -182,7 +182,7 @@ packages: description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.9.0" lints: @@ -190,7 +190,7 @@ packages: description: name: lints sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.1" logging: @@ -198,7 +198,7 @@ packages: description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.0" matcher: @@ -206,7 +206,7 @@ packages: description: name: matcher sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.12.17" meta: @@ -214,7 +214,7 @@ packages: description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.16.0" mime: @@ -222,7 +222,7 @@ packages: description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.0" node_preamble: @@ -230,7 +230,7 @@ packages: description: name: node_preamble sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.2" package_config: @@ -238,7 +238,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" path: @@ -246,7 +246,7 @@ packages: description: name: path sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.8.0" petitparser: @@ -254,7 +254,7 @@ packages: description: name: petitparser sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.4.0" pool: @@ -262,7 +262,7 @@ packages: description: name: pool sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.1" pub_semver: @@ -270,7 +270,7 @@ packages: description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" shelf: @@ -278,7 +278,7 @@ packages: description: name: shelf sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" shelf_packages_handler: @@ -286,7 +286,7 @@ packages: description: name: shelf_packages_handler sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" shelf_static: @@ -294,7 +294,7 @@ packages: description: name: shelf_static sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.3" shelf_web_socket: @@ -302,7 +302,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" source_map_stack_trace: @@ -310,7 +310,7 @@ packages: description: name: source_map_stack_trace sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" source_maps: @@ -318,7 +318,7 @@ packages: description: name: source_maps sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.10.13" source_span: @@ -326,7 +326,7 @@ packages: description: name: source_span sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.10.0" stack_trace: @@ -334,7 +334,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.12.1" stream_channel: @@ -342,7 +342,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.4" string_scanner: @@ -350,7 +350,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" term_glyph: @@ -358,7 +358,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.2" test: @@ -366,7 +366,7 @@ packages: description: name: test sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.25.15" test_api: @@ -374,7 +374,7 @@ packages: description: name: test_api sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.4" test_core: @@ -382,7 +382,7 @@ packages: description: name: test_core sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.6.8" toml: @@ -390,7 +390,7 @@ packages: description: name: toml sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.14.0" typed_data: @@ -398,7 +398,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.0" version: @@ -406,7 +406,7 @@ packages: description: name: version sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" vm_service: @@ -414,7 +414,7 @@ packages: description: name: vm_service sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "15.0.0" watcher: @@ -422,7 +422,7 @@ packages: description: name: watcher sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" web: @@ -430,7 +430,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" web_socket: @@ -438,7 +438,7 @@ packages: description: name: web_socket sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.6" web_socket_channel: @@ -446,7 +446,7 @@ packages: description: name: web_socket_channel sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" webkit_inspection_protocol: @@ -454,7 +454,7 @@ packages: description: name: webkit_inspection_protocol sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.1" yaml: @@ -462,7 +462,7 @@ packages: description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.2" sdks: