From 567b3518e8f3c2d76bb4e60a767b968b3aeb0c66 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: Sun, 16 Mar 2025 21:15:38 +0800 Subject: [PATCH] =?UTF-8?q?=E2=80=8B=E5=88=87=E6=8D=A2=E5=AE=98=E6=96=B9?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=20=E6=9B=B4=E6=8D=A2=E4=B8=BA?= =?UTF-8?q?=E5=AE=98=E6=96=B9=E6=9C=8D=E5=8A=A1=E5=99=A8=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E5=8D=87=E7=A8=B3=E5=AE=9A=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ​移除模拟延迟 改为直接检测连接,优化体验。 ​修复延迟计算 从 peer 获取最小延迟(μs→ms),无效时用路由延迟。 ​修复管理员权限 解决管理员操作无权限问题。 ​配置存储位置调整 将配置信息改为运行目录,方便管理。 ​查看 NAT 类型 添加功能检测并显示 NAT 类型。 ​修复页面状态丢失 解决缩放导致导航栏移动时页面状态丢失问题。 ​优化延迟检测 始终检测延迟,移除暂停/开始功能,简化逻辑。 ​最小化通知 应用最小化时提供通知,提醒用户状态。 ​增加客户端搜索功能 添加客户端搜索功能,支持快速查找内容或设备。 --- assets/tray_icon.png | Bin 7345 -> 0 bytes lib/app.dart | 20 +- lib/screens/about.dart | 2 +- lib/screens/front.dart | 139 +++--- lib/screens/kvroom.dart | 453 ++++++++++-------- lib/screens/settings.dart | 16 +- lib/utils/up.dart | 291 +++++++++++ lib/widgets/window_control_buttons.dart | 39 +- lib/代办 | 15 +- pubspec.lock | 72 +++ pubspec.yaml | 2 + rust_builder/cargokit/build_tool/pubspec.lock | 262 +++++----- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 2 + 14 files changed, 903 insertions(+), 413 deletions(-) delete mode 100644 assets/tray_icon.png create mode 100644 lib/utils/up.dart diff --git a/assets/tray_icon.png b/assets/tray_icon.png deleted file mode 100644 index 84245bb9f96e0e4322fbe1d8ebe0524ff885df0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7345 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 XTA^%0G9H%c00000NkvXXu0mjf$~y{Y diff --git a/lib/app.dart b/lib/app.dart index 0097600..e448ef3 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'screens/Home.dart'; import 'config/themeconfiguration.dart'; import 'config/app_config.dart'; +import 'package:astral/utils/up.dart'; // 添加这行导入 // 定义应用程序的主要StatefulWidget class MyApp extends StatefulWidget { @@ -38,6 +39,17 @@ class _MyAppState extends State { // 初始化系统托盘 initSystemTray(); + + // 添加版本检查(在下一帧执行确保context可用) + Future.microtask(() { + final updateChecker = UpdateChecker( + owner: 'ldoubil', + repo: 'astral', + ); + if (mounted) { + updateChecker.scheckForUpdates(context); + } + }); } // 初始化系统托盘 @@ -88,10 +100,10 @@ class _MyAppState extends State { // 更改主题色的方法 void changeSeedColor(Color color) { // 使用 Future.microtask 延迟状态更新,避免在当前帧中触发重建 - setState(() { - _seedColor = color; - AppConfig().setSeedColor(color); - }); + setState(() { + _seedColor = color; + AppConfig().setSeedColor(color); + }); } // 更改底部导航栏选中索引的方法 diff --git a/lib/screens/about.dart b/lib/screens/about.dart index 8c20403..1a82ac8 100644 --- a/lib/screens/about.dart +++ b/lib/screens/about.dart @@ -116,7 +116,7 @@ class _InfoPageState extends State ); }, child: Text( - AppInfoUtil.getFullVersion(), + AppInfoUtil.getVersion(), style: Theme.of(context).textTheme.bodyLarge, ), ), diff --git a/lib/screens/front.dart b/lib/screens/front.dart index e98deec..fb97cdd 100644 --- a/lib/screens/front.dart +++ b/lib/screens/front.dart @@ -120,62 +120,89 @@ class _HomePageState extends State { roomName: roomName, roomPassword: roomPassword, severurl: Serverip); - // 模拟连接过程,2秒后连接成功 - Future.delayed(const Duration(seconds: 2), () { + + // 添加连接超时计数器 + int connectionTimeoutCounter = 0; + + // 不再使用固定延迟模拟连接成功,而是通过定时检查IP来确定连接状态 + timer = Timer.periodic(const Duration(seconds: 1), (timer) async { // 检查组件是否仍然挂载 - if (!mounted) return; - - if (isRunning) { - // 确保用户没有在连接过程中取消 - setState(() { - _connectionState = ConnectionState.connected; - // 连接成功后开始计时 - - timer = Timer.periodic(const Duration(seconds: 1), (timer) async { - // 检查组件是否仍然挂载 - if (!mounted) { - timer.cancel(); - return; - } - - final info = await getRunningInfo(); - // 打印运行信息的详细内容 - // print("运行信息详情:${info}"); - Runin runin = parseRunin(info); - // 获取网络状态 - // 获取网络状态 - final networkStatus = await getNetworkStatus(); - km.nodes = networkStatus.nodes; - - // 更新网络流量数据 - _updateNetworkStats(networkStatus.nodes); - - // print('设备名称: ${runin.devName}'); - // print( - // '设备ID: ${_intToIpv4String(runin.myNodeInfo?.virtualIpv4?.address?.addr ?? 0)}'); - final int? version = - runin.myNodeInfo?.virtualIpv4?.address?.addr; - if (version != null) { - String ipStr = _intToIpv4String(version); - // 检查IP不为0.0.0.0且与当前IP不同时才更新 - if (ipStr != "0.0.0.0" && publicIP != ipStr) { - km.virtualIP = ipStr; - } - } - - // 再次检查组件是否仍然挂载 - if (!mounted) { - timer.cancel(); - return; - } - - setState(() { - runningTime += const Duration(seconds: 1); - }); - }); - }); + if (!mounted) { + timer.cancel(); + return; } + + final info = await getRunningInfo(); + Runin runin = parseRunin(info); + + // 获取网络状态 + final networkStatus = await getNetworkStatus(); + final km = Provider.of(context, listen: false); + km.nodes = networkStatus.nodes; + + // 更新网络流量数据 + _updateNetworkStats(networkStatus.nodes); + + final int? version = runin.myNodeInfo?.virtualIpv4?.address?.addr; + if (version != null) { + String ipStr = _intToIpv4String(version); + // 检查IP不为0.0.0.0时认为连接成功 + if (ipStr != "0.0.0.0") { + if (publicIP != ipStr) { + km.virtualIP = ipStr; + } + + // 如果当前状态还是连接中,则更新为已连接 + if (_connectionState == ConnectionState.connecting) { + setState(() { + _connectionState = ConnectionState.connected; + }); + } + + // 重置超时计数器 + connectionTimeoutCounter = 0; + } else if (_connectionState == ConnectionState.connecting) { + // 只在连接状态下增加超时计数 + connectionTimeoutCounter++; + + // 如果连续10秒都是0.0.0.0,判断为连接失败 + if (connectionTimeoutCounter >= 10) { + // 停止连接并显示失败消息 + timer.cancel(); + setState(() { + isRunning = false; + _connectionState = ConnectionState.notStarted; + runningTime = Duration.zero; + }); + + // 关闭服务器连接 + closeAllServer(); + + // 显示连接失败提示 + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('连接失败,请检查网络或房间信息后重试'), + duration: Duration(seconds: 3), + ), + ); + } + return; + } + } + } + + // 再次检查组件是否仍然挂载 + if (!mounted) { + timer.cancel(); + return; + } + + setState(() { + runningTime += const Duration(seconds: 1); + }); }); + // 移除原来的Future.delayed模拟连接成功的代码 } else { // 停止时重置状态 _connectionState = ConnectionState.notStarted; @@ -217,7 +244,7 @@ class _HomePageState extends State { // 再次检查挂载状态,确保在setState前组件仍然挂载 if (!mounted) return; - + // 计算速度 (字节/秒 转换为 MB/秒) setState(() { _uploadBytes = totalUploadBytes; @@ -881,7 +908,7 @@ class _HomePageState extends State { // 添加版本信息卡片 Widget _buildVersionInfoCard(ColorScheme colorScheme) { // 这里可以从配置或API获取实际版本号 - final String appVersion = AppInfoUtil.getFullVersion(); + final String appVersion = AppInfoUtil.getVersion(); return FloatingCard( colorScheme: colorScheme, @@ -940,5 +967,3 @@ Widget _buildVersionItem( ], ); } - - diff --git a/lib/screens/kvroom.dart b/lib/screens/kvroom.dart index 253c493..a54b22e 100644 --- a/lib/screens/kvroom.dart +++ b/lib/screens/kvroom.dart @@ -18,6 +18,7 @@ class PlayerInfo { final int receivedPackets; // 接收包数量 final double packetLossRate; // 丢包率(%) final String etVersion; // ET版本 + final String natType; // 添加NAT类型 PlayerInfo({ required this.name, @@ -30,6 +31,7 @@ class PlayerInfo { required this.receivedPackets, required this.packetLossRate, required this.etVersion, + required this.natType, // 添加NAT类型参数 }); } @@ -44,13 +46,43 @@ class RoomPage extends StatefulWidget { class _RoomPageState extends State { List players = []; + List filteredPlayers = []; // 添加过滤后的玩家列表 bool isLoading = true; - // 移除布局类型状态变量 + String searchQuery = ''; // 添加搜索查询字符串 + TextEditingController searchController = TextEditingController(); // 添加搜索控制器 @override void initState() { super.initState(); isLoading = true; + searchController.addListener(_onSearchChanged); // 添加搜索监听器 + } + + @override + void dispose() { + searchController.removeListener(_onSearchChanged); // 移除搜索监听器 + searchController.dispose(); // 释放控制器资源 + super.dispose(); + } + + // 搜索变化处理函数 + void _onSearchChanged() { + setState(() { + searchQuery = searchController.text; + _filterPlayers(); // 过滤玩家列表 + }); + } + + // 过滤玩家列表 + void _filterPlayers() { + if (searchQuery.isEmpty) { + filteredPlayers = List.from(players); // 如果搜索为空,显示所有玩家 + } else { + filteredPlayers = players + .where((player) => + player.name.toLowerCase().contains(searchQuery.toLowerCase())) + .toList(); // 根据名称过滤玩家 + } } @override @@ -60,7 +92,22 @@ class _RoomPageState extends State { return Scaffold( appBar: AppBar( title: const Text('房间成员'), - // 移除布局切换按钮 + actions: [ + // 添加搜索按钮 + IconButton( + icon: const Icon(Icons.search), + onPressed: () { + showSearch( + context: context, + delegate: _PlayerSearchDelegate( + players: players, + colorScheme: colorScheme, + buildPlayerListItem: _buildPlayerListItem, + ), + ); + }, + ), + ], ), body: Consumer( builder: (context, km, child) { @@ -190,6 +237,9 @@ class _RoomPageState extends State { packetLossRate = double.parse(packetLossRate.toStringAsFixed(1)); } + // 获取NAT类型 + String natType = _mapNatType(node.nat); + // 创建PlayerInfo对象 nodePlayerInfos.add( PlayerInfo( @@ -203,6 +253,7 @@ class _RoomPageState extends State { receivedPackets: receivedPackets, packetLossRate: packetLossRate, etVersion: node.version, // 获取版本信息 + natType: natType, // 添加NAT类型 ), ); } @@ -211,6 +262,7 @@ class _RoomPageState extends State { setState(() { players = nodePlayerInfos; + _filterPlayers(); // 更新过滤后的玩家列表 isLoading = false; }); } catch (e) { @@ -223,176 +275,6 @@ class _RoomPageState extends State { } } - // 构建玩家信息卡片 - Widget _buildPlayerCard(PlayerInfo player, ColorScheme colorScheme) { - // 根据延迟值确定颜色 - Color latencyColor = _getLatencyColor(player.latency); - - // 根据连接类型选择图标 - IconData connectionIcon = _getConnectionIcon(player.connectionType); - - return FloatingCard( - colorScheme: colorScheme, - maxWidth: 600, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 玩家名称和连接类型 - Row( - children: [ - Icon(Icons.person, color: colorScheme.primary, size: 22), - const SizedBox(width: 8), - Expanded( - child: Text( - player.name, - style: const TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: _getConnectionTypeColor( - player.connectionType, colorScheme), - borderRadius: BorderRadius.circular(12), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - connectionIcon, - size: 14, - color: Colors.white, - ), - const SizedBox(width: 4), - Text( - player.connectionType, - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 16), - - // IP地址 - _buildInfoRow( - Icons.lan, - 'IP地址', - player.ip, - colorScheme, - showCopyButton: true, - ), - const SizedBox(height: 12), - - // 延迟信息 - _buildInfoRow( - Icons.speed, - '延迟', - '${player.latency} ms', - colorScheme, - valueColor: latencyColor, - ), - const SizedBox(height: 12), - - // ET版本 - _buildInfoRow( - Icons.memory, - 'ET版本', - player.etVersion, - colorScheme, - ), - - const Divider(height: 24), - - // 网络数据部分标题 - Row( - children: [ - Icon(Icons.data_usage, color: colorScheme.primary, size: 18), - const SizedBox(width: 8), - Text( - '网络数据', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: colorScheme.primary, - ), - ), - ], - ), - const SizedBox(height: 12), - - // 网络数据信息 - 优化对齐方式 - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Column( - children: [ - Row( - children: [ - Expanded( - child: _buildNetworkDataItemAligned( - '上传', - '${player.uploadSpeed} KB/s', - Icons.upload, - colorScheme.primary, - ), - ), - Expanded( - child: _buildNetworkDataItemAligned( - '下载', - '${player.downloadSpeed} KB/s', - Icons.download, - colorScheme.secondary, - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: _buildNetworkDataItemAligned( - '发送包', - '${player.sentPackets}', - Icons.send, - colorScheme.primary, - ), - ), - Expanded( - child: _buildNetworkDataItemAligned( - '接收包', - '${player.receivedPackets}', - Icons.call_received, - colorScheme.secondary, - ), - ), - ], - ), - ], - ), - ), - - const Divider(height: 24), - - // 丢包率信息 - _buildInfoRow( - Icons.error_outline, - '丢包率', - '${player.packetLossRate}%', - colorScheme, - valueColor: _getPacketLossColor(player.packetLossRate), - ), - ], - ), - ); - } - // 构建列表项视图 Widget _buildPlayerListItem(PlayerInfo player, ColorScheme colorScheme) { // 根据延迟值确定颜色 @@ -492,6 +374,15 @@ class _RoomPageState extends State { colorScheme, ), const SizedBox(height: 8), + // NAT类型 + _buildInfoRow( + _getNatTypeIcon(player.natType), + 'NAT类型', + player.natType, + colorScheme, + valueColor: _getNatTypeColor(player.natType), + ), + const SizedBox(height: 8), // 丢包率信息 _buildInfoRow( @@ -632,6 +523,16 @@ class _RoomPageState extends State { player.etVersion, colorScheme, ), + const SizedBox(height: 8), + + // NAT类型 + _buildInfoRow( + _getNatTypeIcon(player.natType), + 'NAT类型', + player.natType, + colorScheme, + valueColor: _getNatTypeColor(player.natType), + ), ], ), ), @@ -799,38 +700,6 @@ class _RoomPageState extends State { ); } - // 构建对齐的网络数据项 - Widget _buildNetworkDataItemAligned( - String label, - String value, - IconData icon, - Color color, - ) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(icon, size: 22, color: color), - const SizedBox(height: 6), - Text( - label, - style: const TextStyle(fontSize: 13), - textAlign: TextAlign.center, - ), - const SizedBox(height: 2), - Text( - value, - style: TextStyle( - fontWeight: FontWeight.bold, - color: color, - fontSize: 14, - ), - textAlign: TextAlign.center, - ), - ], - ); - } - // 根据延迟值获取颜色 Color _getLatencyColor(int latency) { if (latency < 50) { @@ -907,4 +776,180 @@ class _RoomPageState extends State { return Colors.grey; } } + + // 将NAT类型转换为中文 + String _mapNatType(String natType) { + switch (natType) { + case 'Unknown': + return '未知'; + case 'OpenInternet': + return '开放网络'; + case 'NoPat': + return '无PAT'; + case 'FullCone': + return '全锥形'; + case 'Restricted': + return '受限锥形'; + case 'PortRestricted': + return '端口受限锥形'; + case 'Symmetric': + return '对称型'; + case 'SymUdpFirewall': + return '对称UDP防火墙'; + case 'SymmetricEasyInc': + return '对称递增型'; + case 'SymmetricEasyDec': + return '对称递减型'; + default: + return '未知'; + } + } + + // 根据NAT类型获取图标 + IconData _getNatTypeIcon(String natType) { + if (natType.contains('开放') || natType.contains('全锥形')) { + return Icons.public; + } else if (natType.contains('受限')) { + return Icons.shield; + } else if (natType.contains('端口受限')) { + return Icons.security; + } else if (natType.contains('对称')) { + return Icons.sync_alt; + } else if (natType.contains('防火墙')) { + return Icons.fireplace; + } else if (natType.contains('递增')) { + return Icons.trending_up; + } else if (natType.contains('递减')) { + return Icons.trending_down; + } else if (natType.contains('无PAT')) { + return Icons.router; + } else { + return Icons.help_outline; + } + } + + // 根据NAT类型获取颜色 + Color _getNatTypeColor(String natType) { + if (natType.contains('开放') || + natType.contains('全锥形') || + natType.contains('无PAT')) { + return Colors.green; + } else if (natType.contains('受限') || natType.contains('端口受限')) { + return Colors.orange; + } else if (natType.contains('对称') || natType.contains('防火墙')) { + return Colors.red; + } else { + return Colors.grey; + } + } +} + +// 玩家搜索委托类 +class _PlayerSearchDelegate extends SearchDelegate { + final List players; + final ColorScheme colorScheme; + final Function(PlayerInfo, ColorScheme) buildPlayerListItem; + + _PlayerSearchDelegate({ + required this.players, + required this.colorScheme, + required this.buildPlayerListItem, + }); + + @override + List buildActions(BuildContext context) { + return [ + IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + // 如果搜索框为空,直接返回 + if (query.isEmpty) { + close(context, ''); + } else { + // 否则清空搜索内容 + query = ''; + showSuggestions(context); + } + }, + ), + ]; + } + + @override + Widget buildLeading(BuildContext context) { + return IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + close(context, ''); + }, + ); + } + + @override + Widget buildResults(BuildContext context) { + return _buildSearchResults(context); + } + + @override + Widget buildSuggestions(BuildContext context) { + return _buildSearchResults(context); + } + + // 修改方法签名,添加 BuildContext 参数 + Widget _buildSearchResults(BuildContext context) { + final filteredPlayers = query.isEmpty + ? players + : players + .where((player) => + player.name.toLowerCase().contains(query.toLowerCase())) + .toList(); + + if (filteredPlayers.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: colorScheme.primary.withOpacity(0.6), + ), + const SizedBox(height: 16), + Text( + '未找到匹配的玩家', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: colorScheme.primary, + ), + ), + const SizedBox(height: 8), + Text( + '尝试使用其他搜索关键词', + style: TextStyle( + color: colorScheme.onSurface.withOpacity(0.7), + ), + ), + ], + ), + ); + } + + // 添加对屏幕宽度的检测 + return LayoutBuilder( + builder: (context, constraints) { + // 使用约束条件获取当前宽度 + return ListView.builder( + padding: const EdgeInsets.all(16.0), + itemCount: filteredPlayers.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: buildPlayerListItem(filteredPlayers[index], colorScheme), + ); + }, + ); + }, + ); + } } diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 3be3631..38b6c09 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -1,3 +1,5 @@ +import 'package:astral/utils/app_info.dart'; +import 'package:astral/utils/up.dart'; import 'package:flutter/material.dart'; import '../config/app_config.dart'; import '../utils/ping_util.dart'; @@ -11,6 +13,11 @@ class SettingsPage extends StatefulWidget { State createState() => _SettingsPageState(); } +final updateChecker = UpdateChecker( + owner: 'ldoubil', + repo: 'astral', +); + class _SettingsPageState extends State { bool _notificationsEnabled = true; double _fontSize = 16.0; @@ -221,6 +228,7 @@ class _SettingsPageState extends State { @override Widget build(BuildContext context) { + // updateChecker.checkForUpdates(context); serverIP = Provider.of(context).virtualIP; return ListView( padding: const EdgeInsets.all(16.0), @@ -347,18 +355,16 @@ class _SettingsPageState extends State { Card( child: Column( children: [ - const ListTile( + ListTile( leading: Icon(Icons.info), title: Text('应用版本'), - subtitle: Text('灰度版本'), + subtitle: Text(AppInfoUtil.getVersion()), ), ListTile( leading: const Icon(Icons.update), title: const Text('检查更新'), onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('灰度版本不支持更新')), - ); + updateChecker.checkForUpdates(context); }, ), ], diff --git a/lib/utils/up.dart b/lib/utils/up.dart new file mode 100644 index 0000000..019465b --- /dev/null +++ b/lib/utils/up.dart @@ -0,0 +1,291 @@ +import 'dart:convert'; +import 'dart:math'; +import 'package:astral/utils/app_info.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class UpdateChecker { + /// GitHub 仓库所有者 + final String owner; + + /// GitHub 仓库名称 + final String repo; + + /// 可选:指定检查的分支名称,默认为 'main' + final String branch; + + UpdateChecker({ + required this.owner, + required this.repo, + this.branch = 'main', + }); + + /// 检查更新 + Future scheckForUpdates(BuildContext context) async { + try { + final releaseInfo = await _fetchLatestRelease(); + if (releaseInfo == null) { + _showUpdateDialog( + // 添加空值处理 + context, + '检查更新失败', + '无法获取最新版本信息', + 'https://github.com/$owner/$repo/releases', + ); + return; + } + + // 获取当前应用版本 + final currentVersion = await _getCurrentVersion(); + + // 比较版本号,如果有新版本则显示更新弹窗 + if (_shouldUpdate(currentVersion, releaseInfo['tag_name'])) { + _showUpdateDialog( + context, + releaseInfo['tag_name'], + releaseInfo['body'] ?? '新版本已发布', + releaseInfo['html_url'], + ); + } + } catch (e) { + _showUpdateDialog( + context, + '更新检查失败', + '检查更新时发生错误: $e', + 'https://github.com/$owner/$repo/releases', + ); + } + } + + Future checkForUpdates(BuildContext context) async { + try { + final releaseInfo = await _fetchLatestRelease(); + if (releaseInfo == null) { + _showUpdateDialog( + // 添加空值处理 + context, + '检查更新失败', + '无法获取最新版本信息', + 'https://github.com/$owner/$repo/releases', + ); + return; + } + + // 获取当前应用版本 + final currentVersion = await _getCurrentVersion(); + + // 比较版本号,如果有新版本则显示更新弹窗 + if (_shouldUpdate(currentVersion, releaseInfo['tag_name'])) { + _showUpdateDialog( + context, + releaseInfo['tag_name'], + releaseInfo['body'] ?? '新版本已发布', + releaseInfo['html_url'], + ); + } else { + _showUpdateDialog( + context, + '当前已是最新版本', + '当前版本为: $currentVersion', + 'https://github.com/$owner/$repo/releases', + ); + } + } catch (e) { + _showUpdateDialog( + context, + '更新检查失败', + '检查更新时发生错误: $e', + 'https://github.com/$owner/$repo/releases', + ); + } + } + + /// 获取最新发布版本信息 + Future?> _fetchLatestRelease() async { + try { + // 添加异常捕获 + final response = await http.get( + Uri.parse('https://api.github.com/repos/$owner/$repo/releases'), + headers: { + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'astral', + }, + ); + + if (response.statusCode == 200) { + final List releases = json.decode(response.body); + if (releases.isEmpty) return null; + + // 获取第一个发布版本(最新版本) + return releases[0]; + } else { + debugPrint('获取最新版本失败: ${response.statusCode}'); + return { + // 返回错误信息 + 'tag_name': '错误 ${response.statusCode}', + 'body': '请求GitHub API失败', + 'html_url': 'https://github.com/$owner/$repo/releases' + }; + } + } catch (e) { + debugPrint('网络请求异常: $e'); + return null; + } + } + + /// 获取当前应用版本 + Future _getCurrentVersion() async { + try { + return AppInfoUtil.getVersion(); + } catch (e) { + debugPrint('获取版本信息失败: $e'); + return "0.0.0"; // 返回默认版本号避免后续比较崩溃 + } + } + + /// 比较版本号,判断是否需要更新 + bool _shouldUpdate(String currentVersion, String latestVersion) { + // 确保版本号格式正确(添加v前缀如果没有) + final current = currentVersion.startsWith('v') + ? currentVersion.substring(1) + : currentVersion; + final latest = latestVersion.startsWith('v') + ? latestVersion.substring(1) + : latestVersion; + + // 处理预发布版本标签(如 -alpha, -beta 等) + String currentClean = current; + String latestClean = latest; + + if (current.contains('-')) { + currentClean = current.split('-')[0]; + } + + if (latest.contains('-')) { + latestClean = latest.split('-')[0]; + } + + // 分割版本号为数组 + final currentParts = currentClean.split('.'); + final latestParts = latestClean.split('.'); + + // 比较主版本号、次版本号和修订号 + for (int i = 0; i < 3; i++) { + final currentPart = + i < currentParts.length ? int.parse(currentParts[i]) : 0; + final latestPart = i < latestParts.length ? int.parse(latestParts[i]) : 0; + + if (latestPart > currentPart) { + return true; + } else if (latestPart < currentPart) { + return false; + } + } + + // 版本号相同,检查预发布标签 + if (current.contains('-') && !latest.contains('-')) { + // 当前是预发布版本,而最新是正式版本 + return true; + } else if (!current.contains('-') && latest.contains('-')) { + // 当前是正式版本,而最新是预发布版本 + return false; + } else if (current.contains('-') && latest.contains('-')) { + // 两者都是预发布版本,比较预发布标签 + final currentPreRelease = current.split('-')[1]; + final latestPreRelease = latest.split('-')[1]; + + // 简单比较预发布标签(alpha < beta < rc) + if (currentPreRelease.startsWith('alpha') && + (latestPreRelease.startsWith('beta') || + latestPreRelease.startsWith('rc'))) { + return true; + } else if (currentPreRelease.startsWith('beta') && + latestPreRelease.startsWith('rc')) { + return true; + } else if (currentPreRelease == latestPreRelease) { + return false; + } + + // 如果预发布标签包含数字(如 beta.1, beta.2),则比较数字部分 + if (currentPreRelease.contains('.') && + latestPreRelease.contains('.') && + currentPreRelease.split('.')[0] == latestPreRelease.split('.')[0]) { + try { + final currentNum = int.parse(currentPreRelease.split('.')[1]); + final latestNum = int.parse(latestPreRelease.split('.')[1]); + return latestNum > currentNum; + } catch (e) { + // 解析失败,返回简单比较结果 + return latestPreRelease.compareTo(currentPreRelease) > 0; + } + } + + // 默认比较预发布标签的字符串 + return latestPreRelease.compareTo(currentPreRelease) > 0; + } + + return false; // 版本相同,不需要更新 + } + + /// 显示更新弹窗 + void _showUpdateDialog( + BuildContext context, + String version, + String releaseNotes, + String downloadUrl, + ) { + final isLatestVersion = version.contains("当前已是最新版本"); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(isLatestVersion ? version : '发现新版本: $version'), + content: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (!isLatestVersion) Text('更新内容:'), + if (!isLatestVersion) const SizedBox(height: 8), + Text( + releaseNotes, + style: const TextStyle(fontSize: 14), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('稍后再说'), + ), + if (!isLatestVersion) // 仅在新版本弹窗显示更新按钮 + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + _launchUrl(downloadUrl); + }, + child: const Text('立即更新'), + ), + if (isLatestVersion) // 最新版本显示确认按钮 + ElevatedButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('确定'), + ), + ], + ), + ); + } + + /// 打开浏览器跳转到下载链接 + Future _launchUrl(String url) async { + final uri = Uri.parse(url); + if (await canLaunchUrl(uri)) { + await launchUrl(uri, mode: LaunchMode.externalApplication); + } else { + debugPrint('无法打开链接: $url'); + } + } +} diff --git a/lib/widgets/window_control_buttons.dart b/lib/widgets/window_control_buttons.dart index 23ba424..4a635d1 100644 --- a/lib/widgets/window_control_buttons.dart +++ b/lib/widgets/window_control_buttons.dart @@ -1,7 +1,21 @@ import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; import '../config/app_config.dart'; -import 'package:tray_manager/tray_manager.dart'; +import 'package:windows_notification/notification_message.dart'; +import 'package:windows_notification/windows_notification.dart'; + +// Create an instance of Windows Notification with your application name +// application id must be null in packaged mode +final _winNotifyPlugin = WindowsNotification(applicationId: 'Astral'); +// create new NotificationMessage instance with id, title, body, and images +NotificationMessage message = NotificationMessage.fromPluginTemplate( + "astral_minimized", + "Astral 已最小化到托盘", + "应用程序正在后台运行,点击托盘图标可以恢复窗口", + // largeImage: "assets/images/icon.ico", + // image: file_path +); + class WindowControls extends StatelessWidget { const WindowControls({super.key}); @@ -26,17 +40,18 @@ class WindowControls extends StatelessWidget { tooltip: '最大化/还原', ), IconButton( - icon: const Icon(Icons.close), - onPressed: () async { - if (AppConfig().closeToTray) { - await windowManager.hide(); // 隐藏主窗口 - await trayManager.setToolTip('FLN2N 正在后台运行'); // 设置托盘提示 - } else { - windowManager.close(); - } - }, - tooltip: '关闭', -), + icon: const Icon(Icons.close), + onPressed: () async { + if (AppConfig().closeToTray) { + await windowManager.hide(); // 隐藏主窗口 + // 替换托盘提示为系统通知 + _winNotifyPlugin.showNotificationPluginTemplate(message); + } else { + windowManager.close(); + } + }, + tooltip: '关闭', + ), ], ); } diff --git a/lib/代办 b/lib/代办 index 84a6f20..9ef99cf 100644 --- a/lib/代办 +++ b/lib/代办 @@ -1,11 +1,12 @@ -[ ] 更换为官方服务器 -[ ] 添加服务器多选 -[ ] 移除模拟延迟(对其实不用等待2秒的那个是模拟的我给忘了)->改为检测是否成功连接 -[ ] 修复延迟计算:从peer连接获取最小延迟(μs->ms),无效则用路由延迟 +[x] 更换为官方服务器 +[x] 移除模拟延迟(对其实不用等待2秒的那个是模拟的我给忘了)->改为检测是否成功连接 +[x] 修复延迟计算:从peer连接获取最小延迟(μs->ms),无效则用路由延迟 [x] 管理员问题没权限 -[ ] 配置信息改为运行目录 -[ ] 看nat类型 +[x] 配置信息改为运行目录 +[x] 看nat类型 [x] 缩放尺寸让导航栏变为底部会导致页面状态丢失 [x] 服务器始终检测延迟,去除暂停和开始反正也不怎么影响性能多此一举还增加复杂度😜 -[x] 增加自动更新检测 和自动更新 +[x] 最小化提供通知 +[x] 可以直接搜索玩家 +[ ] 增加自动更新检测 和自动更新 [ ] 那就打包两个版本 一个便携版(配置文件随软件) 一个安装版(配置文件随软件) \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 4c931ff..5312922 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -185,6 +185,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.0.1" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.11" equatable: dependency: transitive description: @@ -259,6 +267,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: d59eeafd6df92174b1d5f68fc9d66634c97ce2e7cfe2293476236547bb19bbbd + url: "https://pub.flutter-io.cn" + source: hosted + version: "19.0.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "2569b973fc9d1f63a37410a9f7c1c552081226c597190cb359ef5d5762d1631c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "9.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: f8fc0652a601f83419d623c85723a3e82ad81f92b33eaa9bcc21ea1b94773e6e + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -532,6 +572,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.0" platform: dependency: transitive description: @@ -808,6 +856,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.7.4" + timezone: + dependency: transitive + description: + name: timezone + sha256: ffc9d5f4d1193534ef051f9254063fa53d588609418c84299956c3db9383587d + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.10.0" timing: dependency: transitive description: @@ -976,6 +1032,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.4.3" + windows_notification: + dependency: "direct main" + description: + name: windows_notification + sha256: be3e650874615f315402c9b9f3656e29af156709c4b5cc272cb4ca0ab7ba94a8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" xdg_directories: dependency: transitive description: @@ -984,6 +1048,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.5.0" yaml: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ecd6c76..8d076c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,8 @@ dependencies: url_launcher: ^6.3.1 package_info_plus: ^8.3.0 # 添加这一行 yaml: ^3.1.2 + flutter_local_notifications: ^19.0.0 + windows_notification: ^1.3.0 dev_dependencies: flutter_test: diff --git a/rust_builder/cargokit/build_tool/pubspec.lock b/rust_builder/cargokit/build_tool/pubspec.lock index 343bdd3..da34be3 100644 --- a/rust_builder/cargokit/build_tool/pubspec.lock +++ b/rust_builder/cargokit/build_tool/pubspec.lock @@ -5,56 +5,56 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 - url: "https://pub.dev" + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.flutter-io.cn" source: hosted - version: "64.0.0" + version: "67.0.0" adaptive_number: dependency: transitive description: name: adaptive_number sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" - url: "https://pub.dev" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.flutter-io.cn" source: hosted - version: "6.2.0" + version: "6.4.1" args: dependency: "direct main" description: name: args sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.2" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.2" collection: dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.18.0" convert: @@ -62,23 +62,23 @@ packages: description: name: convert sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.1" coverage: dependency: transitive description: name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" - url: "https://pub.dev" + sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.6.3" + version: "1.11.1" crypto: dependency: "direct main" description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" ed25519_edwards: @@ -86,7 +86,7 @@ packages: description: name: ed25519_edwards sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.3.1" file: @@ -94,47 +94,47 @@ packages: description: name: file sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.4" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.1.1" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.flutter-io.cn" source: hosted - version: "3.2.0" + version: "4.0.0" github: dependency: "direct main" description: name: github sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.17.0" glob: dependency: transitive description: name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.3" hex: dependency: "direct main" description: name: hex sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.0" http: @@ -142,55 +142,55 @@ packages: description: name: http sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.flutter-io.cn" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.2" io: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "1.0.5" js: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.7" + version: "0.7.2" json_annotation: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.flutter-io.cn" source: hosted - version: "4.8.1" + version: "4.9.0" lints: dependency: "direct dev" description: name: lints sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" logging: @@ -198,55 +198,55 @@ packages: description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.16" + version: "0.12.17" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.1" + version: "1.16.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "2.0.0" node_preamble: dependency: transitive description: name: node_preamble sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.2" package_config: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.2.0" path: dependency: "direct main" description: name: path sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.8.0" petitparser: @@ -254,7 +254,7 @@ packages: description: name: petitparser sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.4.0" pool: @@ -262,23 +262,23 @@ packages: description: name: pool sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.1" pub_semver: dependency: transitive description: name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.4" + version: "2.2.0" shelf: dependency: transitive description: name: shelf sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" shelf_packages_handler: @@ -286,168 +286,184 @@ packages: description: name: shelf_packages_handler sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" shelf_static: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "3.0.0" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.2" source_maps: dependency: transitive description: name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.10.12" + version: "0.10.13" source_span: dependency: "direct main" description: name: source_span sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.1" + version: "1.2.2" test: dependency: "direct dev" description: name: test - sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" - url: "https://pub.dev" + sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.24.6" + version: "1.25.15" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" - url: "https://pub.dev" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.1" + version: "0.7.4" test_core: dependency: transitive description: name: test_core - sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" - url: "https://pub.dev" + sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.6" + version: "0.6.8" toml: dependency: "direct main" description: name: toml sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.14.0" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.2" + version: "1.4.0" version: dependency: "direct main" description: name: version sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" vm_service: dependency: transitive description: name: vm_service - sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" - url: "https://pub.dev" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.flutter-io.cn" source: hosted - version: "11.9.0" + version: "15.0.0" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.1.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" + sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.0" + version: "3.0.2" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" - url: "https://pub.dev" + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.2.1" yaml: dependency: "direct main" description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index fde4259..02b2494 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,7 @@ #include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( @@ -23,4 +24,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowManagerPlugin")); + WindowsNotificationPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowsNotificationPluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 5724c92..916bd21 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -8,9 +8,11 @@ list(APPEND FLUTTER_PLUGIN_LIST tray_manager url_launcher_windows window_manager + windows_notification ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_local_notifications_windows rust_lib_fltier )