diff --git a/lib/app.dart b/lib/app.dart index e448ef3..85a0135 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -39,17 +39,6 @@ class _MyAppState extends State { // 初始化系统托盘 initSystemTray(); - - // 添加版本检查(在下一帧执行确保context可用) - Future.microtask(() { - final updateChecker = UpdateChecker( - owner: 'ldoubil', - repo: 'astral', - ); - if (mounted) { - updateChecker.scheckForUpdates(context); - } - }); } // 初始化系统托盘 diff --git a/lib/screens/Home.dart b/lib/screens/Home.dart index e8fc36e..03e3c48 100644 --- a/lib/screens/Home.dart +++ b/lib/screens/Home.dart @@ -1,3 +1,4 @@ +import 'package:astral/utils/up.dart'; import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; import '../widgets/window_control_buttons.dart'; @@ -46,6 +47,17 @@ class _MainScreenState extends State duration: const Duration(seconds: 3), vsync: this, )..repeat(); + + // 添加异步更新检查(推荐方式) + WidgetsBinding.instance.addPostFrameCallback((_) { + final updateChecker = UpdateChecker( + owner: 'ldoubil', + repo: 'astral', + ); + if (mounted) { + updateChecker.scheckForUpdates(context); + } + }); } @override @@ -229,7 +241,8 @@ class PageKeepAlive extends StatefulWidget { State createState() => _PageKeepAliveState(); } -class _PageKeepAliveState extends State with AutomaticKeepAliveClientMixin { +class _PageKeepAliveState extends State + with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; diff --git a/lib/screens/front.dart b/lib/screens/front.dart index fb97cdd..1896c6f 100644 --- a/lib/screens/front.dart +++ b/lib/screens/front.dart @@ -91,18 +91,18 @@ class _HomePageState extends State { @override void initState() { super.initState(); - // 初始化 TextEditingController + // 初始化所有TextEditingController _roomNameController = TextEditingController(text: roomName); _roomPasswordController = TextEditingController(text: roomPassword); _usernameController = TextEditingController(text: username); - _virtualIPController = TextEditingController(text: publicIP); + _virtualIPController = TextEditingController(text: publicIP); // 添加缺失的初始化 // 修改卡片构建器列表,添加版本信息卡片 _cardBuilders = [ - _buildNetworkStatusCard, // 网络状态卡片 - _buildUserInfoCard, // 用户信息卡片 - _buildRoomInfoCard, // 房间信息卡片 - _buildVersionInfoCard, // 新增版本信息卡片 + _buildNetworkStatusCard, + _buildUserInfoCard, + _buildRoomInfoCard, + _buildVersionInfoCard, ]; } @@ -120,7 +120,6 @@ class _HomePageState extends State { roomName: roomName, roomPassword: roomPassword, severurl: Serverip); - // 添加连接超时计数器 int connectionTimeoutCounter = 0; @@ -286,6 +285,7 @@ class _HomePageState extends State { text: km.username, selection: _usernameController.selection, ); + // 添加虚拟IP控制器的值同步 _virtualIPController.value = TextEditingValue( text: km.virtualIP, selection: _virtualIPController.selection, @@ -744,8 +744,10 @@ class _HomePageState extends State { TextField( controller: _usernameController, enabled: _connectionState != ConnectionState.connected, - onChanged: (value) { - km.username = value; + onEditingComplete: () { + // 改为完成编辑时更新 + Provider.of(context, listen: false).username = + _usernameController.text; }, decoration: InputDecoration( labelText: '用户名', @@ -765,9 +767,13 @@ class _HomePageState extends State { enabled: !_isAutoIP && _connectionState != ConnectionState.connected, onChanged: (value) { + // 保留空回调以避免实时更新 + }, + onEditingComplete: () { + // 添加完成编辑回调 if (!_isAutoIP) { - setState(() {}); - km.virtualIP = value; + Provider.of(context, listen: false).virtualIP = + _virtualIPController.text; } }, decoration: InputDecoration( @@ -790,6 +796,10 @@ class _HomePageState extends State { _isAutoIP = value; }); km.dynamicIP = value; + // 切换模式时同步最新值 + if (!value) { + km.virtualIP = _virtualIPController.text; + } } : null, ), @@ -860,8 +870,10 @@ class _HomePageState extends State { TextField( controller: _roomNameController, enabled: _connectionState != ConnectionState.connected, - onChanged: (value) { - km.roomName = value; + onEditingComplete: () { + // 改为完成编辑时更新 + Provider.of(context, listen: false).roomName = + _roomNameController.text; }, decoration: InputDecoration( labelText: '房间名称', @@ -876,8 +888,10 @@ class _HomePageState extends State { TextField( controller: _roomPasswordController, enabled: _connectionState != ConnectionState.connected, - onChanged: (value) { - km.roomPassword = value; + onEditingComplete: () { + // 改为完成编辑时更新 + Provider.of(context, listen: false).roomPassword = + _roomPasswordController.text; }, obscureText: true, decoration: InputDecoration( diff --git a/lib/screens/kvroom.dart b/lib/screens/kvroom.dart index a54b22e..d142981 100644 --- a/lib/screens/kvroom.dart +++ b/lib/screens/kvroom.dart @@ -230,13 +230,7 @@ class _RoomPageState extends State { } // 计算丢包率 (简单估算) - double packetLossRate = 0.0; - if (sentPackets > 0 && receivedPackets > 0) { - packetLossRate = (1.0 - (receivedPackets / sentPackets)).abs() * 100; - if (packetLossRate > 100) packetLossRate = 100.0; - packetLossRate = double.parse(packetLossRate.toStringAsFixed(1)); - } - + double packetLossRate = node.lossRate; // 获取NAT类型 String natType = _mapNatType(node.nat); diff --git a/lib/src/rust/api/simple.dart b/lib/src/rust/api/simple.dart index 5d71124..f673ed4 100644 --- a/lib/src/rust/api/simple.dart +++ b/lib/src/rust/api/simple.dart @@ -124,6 +124,7 @@ class KVNodeInfo { final String ipv4; final double latencyMs; final String nat; + final double lossRate; final List connections; final String version; final int cost; @@ -133,6 +134,7 @@ class KVNodeInfo { required this.ipv4, required this.latencyMs, required this.nat, + required this.lossRate, required this.connections, required this.version, required this.cost, @@ -144,6 +146,7 @@ class KVNodeInfo { ipv4.hashCode ^ latencyMs.hashCode ^ nat.hashCode ^ + lossRate.hashCode ^ connections.hashCode ^ version.hashCode ^ cost.hashCode; @@ -157,6 +160,7 @@ class KVNodeInfo { ipv4 == other.ipv4 && latencyMs == other.latencyMs && nat == other.nat && + lossRate == other.lossRate && connections == other.connections && version == other.version && cost == other.cost; diff --git a/lib/src/rust/frb_generated.dart b/lib/src/rust/frb_generated.dart index a11ad19..c140062 100644 --- a/lib/src/rust/frb_generated.dart +++ b/lib/src/rust/frb_generated.dart @@ -515,6 +515,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as bool; } + @protected + double dco_decode_f_32(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as double; + } + @protected double dco_decode_f_64(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -558,16 +564,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { KVNodeInfo dco_decode_kv_node_info(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 7) - throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); + if (arr.length != 8) + throw Exception('unexpected arr length: expect 8 but see ${arr.length}'); return KVNodeInfo( hostname: dco_decode_String(arr[0]), ipv4: dco_decode_String(arr[1]), latencyMs: dco_decode_f_64(arr[2]), nat: dco_decode_String(arr[3]), - connections: dco_decode_list_kv_node_connection_stats(arr[4]), - version: dco_decode_String(arr[5]), - cost: dco_decode_i_32(arr[6]), + lossRate: dco_decode_f_32(arr[4]), + connections: dco_decode_list_kv_node_connection_stats(arr[5]), + version: dco_decode_String(arr[6]), + cost: dco_decode_i_32(arr[7]), ); } @@ -753,6 +760,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8() != 0; } + @protected + double sse_decode_f_32(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getFloat32(); + } + @protected double sse_decode_f_64(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -797,6 +810,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_ipv4 = sse_decode_String(deserializer); var var_latencyMs = sse_decode_f_64(deserializer); var var_nat = sse_decode_String(deserializer); + var var_lossRate = sse_decode_f_32(deserializer); var var_connections = sse_decode_list_kv_node_connection_stats(deserializer); var var_version = sse_decode_String(deserializer); @@ -806,6 +820,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ipv4: var_ipv4, latencyMs: var_latencyMs, nat: var_nat, + lossRate: var_lossRate, connections: var_connections, version: var_version, cost: var_cost); @@ -1016,6 +1031,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putUint8(self ? 1 : 0); } + @protected + void sse_encode_f_32(double self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putFloat32(self); + } + @protected void sse_encode_f_64(double self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1054,6 +1075,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(self.ipv4, serializer); sse_encode_f_64(self.latencyMs, serializer); sse_encode_String(self.nat, serializer); + sse_encode_f_32(self.lossRate, serializer); sse_encode_list_kv_node_connection_stats(self.connections, serializer); sse_encode_String(self.version, serializer); sse_encode_i_32(self.cost, serializer); diff --git a/lib/src/rust/frb_generated.io.dart b/lib/src/rust/frb_generated.io.dart index fd2c21d..5034fc7 100644 --- a/lib/src/rust/frb_generated.io.dart +++ b/lib/src/rust/frb_generated.io.dart @@ -77,6 +77,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool dco_decode_bool(dynamic raw); + @protected + double dco_decode_f_32(dynamic raw); + @protected double dco_decode_f_64(dynamic raw); @@ -182,6 +185,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); + @protected + double sse_decode_f_32(SseDeserializer deserializer); + @protected double sse_decode_f_64(SseDeserializer deserializer); @@ -288,6 +294,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_bool(bool self, SseSerializer serializer); + @protected + void sse_encode_f_32(double self, SseSerializer serializer); + @protected void sse_encode_f_64(double self, SseSerializer serializer); diff --git a/lib/src/rust/frb_generated.web.dart b/lib/src/rust/frb_generated.web.dart index 1b7520a..72cee18 100644 --- a/lib/src/rust/frb_generated.web.dart +++ b/lib/src/rust/frb_generated.web.dart @@ -79,6 +79,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool dco_decode_bool(dynamic raw); + @protected + double dco_decode_f_32(dynamic raw); + @protected double dco_decode_f_64(dynamic raw); @@ -184,6 +187,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected bool sse_decode_bool(SseDeserializer deserializer); + @protected + double sse_decode_f_32(SseDeserializer deserializer); + @protected double sse_decode_f_64(SseDeserializer deserializer); @@ -290,6 +296,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_bool(bool self, SseSerializer serializer); + @protected + void sse_encode_f_32(double self, SseSerializer serializer); + @protected void sse_encode_f_64(double self, SseSerializer serializer); diff --git a/pubspec.yaml b/pubspec.yaml index 1606cd7..77b709d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1 +version: 1.0.2 environment: sdk: ^3.5.4 diff --git a/rust/src/api/simple.rs b/rust/src/api/simple.rs index c31b011..f5f3a05 100644 --- a/rust/src/api/simple.rs +++ b/rust/src/api/simple.rs @@ -292,6 +292,7 @@ pub struct KVNodeInfo { pub ipv4: String, pub latency_ms: f64, pub nat: String, // NAT类型 + pub loss_rate: f32, pub connections: Vec, pub version: String, pub cost: i32, @@ -330,7 +331,6 @@ pub fn get_network_status() -> KVNetworkStatus { hostname: route.hostname.clone(), ipv4, latency_ms: if let Some(peer) = &pair.peer { - // 类似cli.rs中的get_latency_ms方法 let mut min_latency = u64::MAX; for conn in &peer.conns { if let Some(stats) = &conn.stats { @@ -348,6 +348,15 @@ pub fn get_network_status() -> KVNetworkStatus { // 如果没有peer信息,则使用路由路径延迟 f64::from(route.path_latency.max(0)) / 1000.0 }, + loss_rate: if let Some(peer) = &pair.peer { + let mut total_loss_rate = 0.0; + for conn in &peer.conns { + total_loss_rate += conn.loss_rate; + } + total_loss_rate + } else { + 0.0 // 如果没有连接信息,默认为0 + }, nat: route.stun_info.as_ref().map_or_else( || "Unknown".to_string(), |stun| { @@ -571,6 +580,9 @@ pub fn create_server( let cfg = create_config(); cfg.set_hostname(Option::from(username)); cfg.set_dhcp(enable_dhcp); + let mut flags = cfg.get_flags(); + flags.dev_name = "Astral".to_string(); + cfg.set_flags(flags); let peer_config = PeerConfig { uri: ("tcp://".to_string() + &severurl).parse().unwrap(), }; diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 1ab7b8a..9bdae1c 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -496,6 +496,13 @@ impl SseDecode for bool { } } +impl SseDecode for f32 { + // 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_f32::().unwrap() + } +} + impl SseDecode for f64 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -547,6 +554,7 @@ impl SseDecode for crate::api::simple::KVNodeInfo { let mut var_ipv4 = ::sse_decode(deserializer); let mut var_latencyMs = ::sse_decode(deserializer); let mut var_nat = ::sse_decode(deserializer); + let mut var_lossRate = ::sse_decode(deserializer); let mut var_connections = >::sse_decode(deserializer); let mut var_version = ::sse_decode(deserializer); @@ -556,6 +564,7 @@ impl SseDecode for crate::api::simple::KVNodeInfo { ipv4: var_ipv4, latency_ms: var_latencyMs, nat: var_nat, + loss_rate: var_lossRate, connections: var_connections, version: var_version, cost: var_cost, @@ -822,6 +831,7 @@ impl flutter_rust_bridge::IntoDart for crate::api::simple::KVNodeInfo { self.ipv4.into_into_dart().into_dart(), self.latency_ms.into_into_dart().into_dart(), self.nat.into_into_dart().into_dart(), + self.loss_rate.into_into_dart().into_dart(), self.connections.into_into_dart().into_dart(), self.version.into_into_dart().into_dart(), self.cost.into_into_dart().into_dart(), @@ -928,6 +938,13 @@ impl SseEncode for bool { } } +impl SseEncode for f32 { + // 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_f32::(self).unwrap(); + } +} + impl SseEncode for f64 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -968,6 +985,7 @@ impl SseEncode for crate::api::simple::KVNodeInfo { ::sse_encode(self.ipv4, serializer); ::sse_encode(self.latency_ms, serializer); ::sse_encode(self.nat, serializer); + ::sse_encode(self.loss_rate, serializer); >::sse_encode(self.connections, serializer); ::sse_encode(self.version, serializer); ::sse_encode(self.cost, serializer);