​切换官方服务器

更换为官方服务器,提升稳定性。

​移除模拟延迟
改为直接检测连接,优化体验。

​修复延迟计算
从 peer 获取最小延迟(μs→ms),无效时用路由延迟。

​修复管理员权限
解决管理员操作无权限问题。

​配置存储位置调整
将配置信息改为运行目录,方便管理。

​查看 NAT 类型
添加功能检测并显示 NAT 类型。

​修复页面状态丢失
解决缩放导致导航栏移动时页面状态丢失问题。

​优化延迟检测
始终检测延迟,移除暂停/开始功能,简化逻辑。

​最小化通知
应用最小化时提供通知,提醒用户状态。

​增加客户端搜索功能
添加客户端搜索功能,支持快速查找内容或设备。
This commit is contained in:
会做饭的二哈
2025-03-16 21:15:38 +08:00
parent 45b6d939da
commit 567b3518e8
14 changed files with 903 additions and 413 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

+12
View File
@@ -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<MyApp> {
// 初始化系统托盘
initSystemTray();
// 添加版本检查(在下一帧执行确保context可用)
Future.microtask(() {
final updateChecker = UpdateChecker(
owner: 'ldoubil',
repo: 'astral',
);
if (mounted) {
updateChecker.scheckForUpdates(context);
}
});
}
// 初始化系统托盘
+1 -1
View File
@@ -116,7 +116,7 @@ class _InfoPageState extends State<InfoPage>
);
},
child: Text(
AppInfoUtil.getFullVersion(),
AppInfoUtil.getVersion(),
style: Theme.of(context).textTheme.bodyLarge,
),
),
+50 -25
View File
@@ -120,17 +120,11 @@ class _HomePageState extends State<HomePage> {
roomName: roomName,
roomPassword: roomPassword,
severurl: Serverip);
// 模拟连接过程,2秒后连接成功
Future.delayed(const Duration(seconds: 2), () {
// 检查组件是否仍然挂载
if (!mounted) return;
if (isRunning) {
// 确保用户没有在连接过程中取消
setState(() {
_connectionState = ConnectionState.connected;
// 连接成功后开始计时
// 添加连接超时计数器
int connectionTimeoutCounter = 0;
// 不再使用固定延迟模拟连接成功,而是通过定时检查IP来确定连接状态
timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
// 检查组件是否仍然挂载
if (!mounted) {
@@ -139,28 +133,63 @@ class _HomePageState extends State<HomePage> {
}
final info = await getRunningInfo();
// 打印运行信息的详细内容
// print("运行信息详情:${info}");
Runin runin = parseRunin(info);
// 获取网络状态
// 获取网络状态
final networkStatus = await getNetworkStatus();
final km = Provider.of<KM>(context, listen: false);
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;
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) {
// 检查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;
}
}
}
// 再次检查组件是否仍然挂载
@@ -173,9 +202,7 @@ class _HomePageState extends State<HomePage> {
runningTime += const Duration(seconds: 1);
});
});
});
}
});
// 移除原来的Future.delayed模拟连接成功的代码
} else {
// 停止时重置状态
_connectionState = ConnectionState.notStarted;
@@ -881,7 +908,7 @@ class _HomePageState extends State<HomePage> {
// 添加版本信息卡片
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(
],
);
}
+249 -204
View File
@@ -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<RoomPage> {
List<PlayerInfo> players = [];
List<PlayerInfo> 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<RoomPage> {
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<KM>(
builder: (context, km, child) {
@@ -190,6 +237,9 @@ class _RoomPageState extends State<RoomPage> {
packetLossRate = double.parse(packetLossRate.toStringAsFixed(1));
}
// 获取NAT类型
String natType = _mapNatType(node.nat);
// 创建PlayerInfo对象
nodePlayerInfos.add(
PlayerInfo(
@@ -203,6 +253,7 @@ class _RoomPageState extends State<RoomPage> {
receivedPackets: receivedPackets,
packetLossRate: packetLossRate,
etVersion: node.version, // 获取版本信息
natType: natType, // 添加NAT类型
),
);
}
@@ -211,6 +262,7 @@ class _RoomPageState extends State<RoomPage> {
setState(() {
players = nodePlayerInfos;
_filterPlayers(); // 更新过滤后的玩家列表
isLoading = false;
});
} catch (e) {
@@ -223,176 +275,6 @@ class _RoomPageState extends State<RoomPage> {
}
}
// 构建玩家信息卡片
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<RoomPage> {
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<RoomPage> {
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<RoomPage> {
);
}
// 构建对齐的网络数据项
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<RoomPage> {
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<String> {
final List<PlayerInfo> players;
final ColorScheme colorScheme;
final Function(PlayerInfo, ColorScheme) buildPlayerListItem;
_PlayerSearchDelegate({
required this.players,
required this.colorScheme,
required this.buildPlayerListItem,
});
@override
List<Widget> 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),
);
},
);
},
);
}
}
+11 -5
View File
@@ -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<SettingsPage> createState() => _SettingsPageState();
}
final updateChecker = UpdateChecker(
owner: 'ldoubil',
repo: 'astral',
);
class _SettingsPageState extends State<SettingsPage> {
bool _notificationsEnabled = true;
double _fontSize = 16.0;
@@ -221,6 +228,7 @@ class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
// updateChecker.checkForUpdates(context);
serverIP = Provider.of<KM>(context).virtualIP;
return ListView(
padding: const EdgeInsets.all(16.0),
@@ -347,18 +355,16 @@ class _SettingsPageState extends State<SettingsPage> {
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);
},
),
],
+291
View File
@@ -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<void> 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<void> 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<Map<String, dynamic>?> _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<dynamic> 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<String> _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<void> _launchUrl(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
debugPrint('无法打开链接: $url');
}
}
}
+18 -3
View File
@@ -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});
@@ -30,13 +44,14 @@ class WindowControls extends StatelessWidget {
onPressed: () async {
if (AppConfig().closeToTray) {
await windowManager.hide(); // 隐藏主窗口
await trayManager.setToolTip('FLN2N 正在后台运行'); // 设置托盘提示
// 替换托盘提示为系统通知
_winNotifyPlugin.showNotificationPluginTemplate(message);
} else {
windowManager.close();
}
},
tooltip: '关闭',
),
),
],
);
}
+8 -7
View File
@@ -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] 可以直接搜索玩家
[ ] 增加自动更新检测 和自动更新
[ ] 那就打包两个版本 一个便携版(配置文件随软件) 一个安装版(配置文件随软件)
+72
View File
@@ -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:
+2
View File
@@ -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:
+139 -123
View File
@@ -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"
@@ -11,6 +11,7 @@
#include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h>
#include <windows_notification/windows_notification_plugin_c_api.h>
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"));
}
+2
View File
@@ -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
)