Files
astral/lib/screens/首页.dart
T
2025-03-15 23:25:22 +08:00

923 lines
30 KiB
Dart

// 导入必要的包
import 'dart:convert';
import 'package:ASTRAL/src/rust/api/simple.dart';
import 'package:ASTRAL/utils/%E7%8A%B6%E6%80%81.dart';
import 'package:ASTRAL/utils/app_info.dart';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'dart:async';
import 'package:provider/provider.dart';
import '../widgets/卡片.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import '../utils/runin.dart';
enum ConnectionState { notStarted, connecting, connected }
Runin parseRunin(String jsonString) {
final Map<String, dynamic> jsonMap = json.decode(jsonString);
return Runin.fromJson(jsonMap);
}
// 辅助方法:将整数形式的IP转换为字符串
String _intToIpv4String(int addr) {
return [
(addr >> 24) & 0xFF,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF,
].join('.');
}
/// 首页组件
/// 用于显示应用的主页面,包含主题切换和问候功能
class HomePage extends StatefulWidget {
// 主题模式切换回调函数
final Function toggleThemeMode;
// 主题色更改回调函数
final Function(Color) changeSeedColor;
// 当前主题模式
final ThemeMode currentThemeMode;
/// 构造函数
const HomePage({
super.key,
required this.toggleThemeMode,
required this.changeSeedColor,
required this.currentThemeMode,
});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// 定义状态枚举
// 当前连接状态
ConnectionState _connectionState = ConnectionState.notStarted;
bool isRunning = false; // 只用于控制启动/暂停状态
Duration runningTime = Duration.zero;
Timer? timer;
// 模拟数据
double uploadSpeed = 0; // MB/s
double downloadSpeed = 0; // MB/s
String publicIP = "";
// 房间名
String roomName = "";
// 房间密码
String roomPassword = "";
// 用户名
String username = "";
bool _isAutoIP = true; // 只用于控制IP自动/手动模式
String Serverip = "";
int _uploadBytes = 0;
int _downloadBytes = 0;
int _lastUploadBytes = 0;
int _lastDownloadBytes = 0;
// 定义卡片列表
late final List<Widget Function(ColorScheme)> _cardBuilders;
// 添加 TextEditingController
late final TextEditingController _roomNameController;
late final TextEditingController _roomPasswordController;
late final TextEditingController _usernameController;
late final TextEditingController _virtualIPController;
@override
void initState() {
super.initState();
// 初始化 TextEditingController
_roomNameController = TextEditingController(text: roomName);
_roomPasswordController = TextEditingController(text: roomPassword);
_usernameController = TextEditingController(text: username);
_virtualIPController = TextEditingController(text: publicIP);
// 修改卡片构建器列表,添加版本信息卡片
_cardBuilders = [
_buildNetworkStatusCard, // 网络状态卡片
_buildUserInfoCard, // 用户信息卡片
_buildRoomInfoCard, // 房间信息卡片
_buildVersionInfoCard, // 新增版本信息卡片
];
}
void toggleRunning() {
final km = Provider.of<KM>(context, listen: false);
setState(() {
isRunning = !isRunning;
if (isRunning) {
// 切换到连接中状态
_connectionState = ConnectionState.connecting;
createServer(
username: username,
enableDhcp: _isAutoIP,
specifiedIp: publicIP,
roomName: roomName,
roomPassword: roomPassword,
severurl: Serverip);
// 模拟连接过程,2秒后连接成功
Future.delayed(const Duration(seconds: 2), () {
if (isRunning) {
// 确保用户没有在连接过程中取消
setState(() {
_connectionState = ConnectionState.connected;
// 连接成功后开始计时
timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
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;
}
}
// print("- 用户名: ${info?.myNodeInfo?.hostname}");
// print("- 虚拟IPv4: ${info?.myNodeInfo?.virtualIpv4?.address}");
// print("- version: ${info?.myNodeInfo?.version}");
// print("- 本地IP: ${info.myNodeInfo.}");
setState(() {
runningTime += const Duration(seconds: 1);
});
});
});
}
});
} else {
// 停止时重置状态
_connectionState = ConnectionState.notStarted;
closeAllServer();
timer?.cancel();
runningTime = Duration.zero;
// 重置网络统计数据
_uploadBytes = 0;
_downloadBytes = 0;
_lastUploadBytes = 0;
_lastDownloadBytes = 0;
uploadSpeed = 0;
downloadSpeed = 0;
}
});
}
// 添加网络统计数据更新方法
void _updateNetworkStats(dynamic nodes) {
if (nodes == null || nodes.isEmpty) return;
int totalUploadBytes = 0;
int totalDownloadBytes = 0;
String myIP = Provider.of<KM>(context, listen: false).virtualIP;
// 查找本机节点
for (var node in nodes) {
if (node.ipv4 == myIP) {
// 找到本机节点,计算上传下载总量
if (node.connections != null && node.connections.isNotEmpty) {
for (var conn in node.connections) {
totalUploadBytes += (conn.txBytes as BigInt).toInt();
totalDownloadBytes += (conn.rxBytes as BigInt).toInt();
}
}
break;
}
}
// 计算速度 (字节/秒 转换为 MB/秒)
setState(() {
_uploadBytes = totalUploadBytes;
_downloadBytes = totalDownloadBytes;
// 计算速度差值
if (_lastUploadBytes > 0) {
uploadSpeed =
(_uploadBytes - _lastUploadBytes) / (1024 * 1024); // 转换为MB/s
uploadSpeed = double.parse(uploadSpeed.toStringAsFixed(2)); // 保留两位小数
}
if (_lastDownloadBytes > 0) {
downloadSpeed =
(_downloadBytes - _lastDownloadBytes) / (1024 * 1024); // 转换为MB/s
downloadSpeed =
double.parse(downloadSpeed.toStringAsFixed(2)); // 保留两位小数
}
// 更新上次的值
_lastUploadBytes = _uploadBytes;
_lastDownloadBytes = _downloadBytes;
});
}
// 根据屏幕宽度计算列数
int _getColumnCount(BuildContext context) {
final width = MediaQuery.of(context).size.width;
if (width < 600) {
return 1; // 手机屏幕显示1列
} else if (width < 900) {
return 2; // 平板或小屏幕显示2列
} else {
return 3; // 大屏幕显示3列
}
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String hours = twoDigits(duration.inHours);
String minutes = twoDigits(duration.inMinutes.remainder(60));
String seconds = twoDigits(duration.inSeconds.remainder(60));
return '$hours:$minutes:$seconds';
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
final km = Provider.of<KM>(context);
_roomNameController.value = TextEditingValue(
text: km.roomName,
selection: _roomNameController.selection,
);
_roomPasswordController.value = TextEditingValue(
text: km.roomPassword,
selection: _roomPasswordController.selection,
);
_usernameController.value = TextEditingValue(
text: km.username,
selection: _usernameController.selection,
);
_virtualIPController.value = TextEditingValue(
text: km.virtualIP,
selection: _virtualIPController.selection,
);
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
publicIP = Provider.of<KM>(context).virtualIP;
_isAutoIP = Provider.of<KM>(context).dynamicIP; // 更新自动IP状态
//我的房间
roomName = Provider.of<KM>(context).roomName;
//我的密码
roomPassword = Provider.of<KM>(context).roomPassword;
//我的用户名
username = Provider.of<KM>(context).username;
Serverip = Provider.of<KM>(context).serverIP;
// 使用 SliverPadding 包裹 SliverList
return Scaffold(
body: CustomScrollView(
// 添加这个属性来控制滚动行为
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
// 替换原有的 SliverList 为 SliverPadding + SliverGrid
SliverPadding(
padding: const EdgeInsets.all(16.0),
sliver: SliverMasonryGrid.count(
crossAxisCount: _getColumnCount(context), // 根据屏幕宽度动态设置列数
mainAxisSpacing: 16, // 主轴间距
crossAxisSpacing: 16, // 交叉轴间距
childCount: _cardBuilders.length,
itemBuilder: (context, index) {
// 直接从列表中获取构建函数并调用
return ConstrainedBox(
constraints: const BoxConstraints(minHeight: 100),
child: _cardBuilders[index](colorScheme),
);
},
),
),
],
),
floatingActionButton: AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeOutCubic,
width: _connectionState != ConnectionState.notStarted ? 180 : 100,
height: 60,
child: FloatingActionButton.extended(
onPressed: _connectionState == ConnectionState.connecting
? null
: toggleRunning,
extendedPadding: const EdgeInsets.symmetric(horizontal: 2),
splashColor: _connectionState != ConnectionState.notStarted
? colorScheme.onTertiary.withOpacity(0.2)
: colorScheme.onPrimary.withOpacity(0.2),
highlightElevation: 6,
elevation: 2,
icon: AnimatedSwitcher(
duration: const Duration(milliseconds: 150),
switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
},
child: _getButtonIcon(_connectionState),
),
label: AnimatedSwitcher(
duration: const Duration(milliseconds: 0),
switchInCurve: Curves.easeOutQuad,
switchOutCurve: Curves.easeInQuad,
child: _getButtonLabel(_connectionState),
),
backgroundColor: _getButtonColor(_connectionState, colorScheme),
foregroundColor:
_getButtonForegroundColor(_connectionState, colorScheme),
),
),
);
}
// 根据连接状态获取按钮图标
Widget _getButtonIcon(ConnectionState state) {
switch (state) {
case ConnectionState.notStarted:
return const Icon(
Icons.play_arrow,
key: ValueKey('play'),
size: 34,
);
case ConnectionState.connecting:
return SizedBox(
key: const ValueKey('connecting'),
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.onTertiary),
),
);
case ConnectionState.connected:
return const Icon(
Icons.pause,
key: ValueKey('pause'),
size: 34,
);
}
}
// 根据连接状态获取按钮文本
Widget _getButtonLabel(ConnectionState state) {
switch (state) {
case ConnectionState.notStarted:
return const Text(
'启动',
key: ValueKey('start_text'),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 18,
),
);
case ConnectionState.connecting:
return const Text(
'连接中...',
key: ValueKey('connecting_text'),
style: TextStyle(fontWeight: FontWeight.bold),
);
case ConnectionState.connected:
return Text(
_formatDuration(runningTime),
key: const ValueKey('running_text'),
style: const TextStyle(fontWeight: FontWeight.bold),
);
}
}
// 根据连接状态获取按钮背景色
Color _getButtonColor(ConnectionState state, ColorScheme colorScheme) {
switch (state) {
case ConnectionState.notStarted:
return colorScheme.primary;
case ConnectionState.connecting:
return colorScheme.tertiary.withOpacity(0.7);
case ConnectionState.connected:
return colorScheme.tertiary;
}
}
// 根据连接状态获取按钮前景色
Color _getButtonForegroundColor(
ConnectionState state, ColorScheme colorScheme) {
switch (state) {
case ConnectionState.notStarted:
return colorScheme.onPrimary;
case ConnectionState.connecting:
case ConnectionState.connected:
return colorScheme.onTertiary;
}
}
// 修改卡片构建方法,移除多余的内边距
Widget _buildDashboardCard(ColorScheme colorScheme) {
return FloatingCard(
colorScheme: colorScheme,
maxWidth: 600, // 设置最大宽度
height: 200,
child: SizedBox(
child: PieChart(
PieChartData(
sections: [
PieChartSectionData(
value: uploadSpeed,
title: '上传',
color: colorScheme.primary,
),
PieChartSectionData(
value: downloadSpeed,
title: '下载',
color: colorScheme.secondary,
),
],
),
),
));
}
// 修改流量统计卡片,移除多余的内边距
Widget _buildTrafficCard(ColorScheme colorScheme) {
return FloatingCard(
colorScheme: colorScheme,
maxWidth: 600,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 修改标题为图标+文字组合
Row(
children: [
Icon(Icons.data_usage, color: colorScheme.primary, size: 22),
const SizedBox(width: 8),
const Text('流量统计',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildTrafficInfo('上传速度', '$uploadSpeed MB/s', Icons.upload,
colorScheme.primary),
_buildTrafficInfo('下载速度', '$downloadSpeed MB/s', Icons.download,
colorScheme.secondary),
],
),
],
));
}
// 修改IP地址卡片,移除多余的内边距
Widget _buildIPCard(ColorScheme colorScheme) {
return FloatingCard(
colorScheme: colorScheme,
maxWidth: 600,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 修改标题为图标+文字组合
Row(
children: [
Icon(Icons.wifi, color: colorScheme.primary, size: 22),
const SizedBox(width: 8),
const Text('网络信息',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 16),
_buildIPInfo('虚拟 IP', publicIP, Icons.public, colorScheme),
],
),
);
}
Widget _buildTrafficInfo(
String label, String value, IconData icon, Color color) {
return Column(
children: [
Icon(icon, size: 24, color: color),
const SizedBox(height: 8),
Text(label),
Text(value,
style: TextStyle(fontWeight: FontWeight.bold, color: color)),
],
);
}
Widget _buildIPInfo(
String label, String value, IconData icon, ColorScheme colorScheme) {
return Row(
children: [
Icon(icon, size: 20, color: colorScheme.primary),
const SizedBox(width: 8),
Text('$label: ', style: const TextStyle(fontWeight: FontWeight.bold)),
Text(value, style: TextStyle(color: colorScheme.secondary)),
],
);
}
// 新增合并后的网络状态卡片(合并了流量统计和IP信息)
Widget _buildNetworkStatusCard(ColorScheme colorScheme) {
return FloatingCard(
colorScheme: colorScheme,
maxWidth: 600,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题栏带有连接状态指示器
Row(
children: [
Icon(Icons.network_check, color: colorScheme.primary, size: 22),
const SizedBox(width: 8),
const Text('网络状态',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Spacer(),
// 添加状态指示器
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _getStatusColor(_connectionState, colorScheme),
borderRadius: BorderRadius.circular(12),
),
child: Text(
_getStatusText(_connectionState),
style: TextStyle(
color: colorScheme.onPrimary,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 16),
// IP信息部分
_buildIPInfo('虚拟 IP', publicIP, Icons.public, colorScheme),
const SizedBox(height: 12),
// 流量统计部分
const Divider(),
const SizedBox(height: 8),
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),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildTrafficInfo('上传速度', '$uploadSpeed MB/s', Icons.upload,
colorScheme.primary),
_buildTrafficInfo('下载速度', '$downloadSpeed MB/s', Icons.download,
colorScheme.secondary),
],
),
// 添加运行时间显示
if (_connectionState == ConnectionState.connected) ...[
const Divider(),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.timer, size: 16, color: colorScheme.primary),
const SizedBox(width: 8),
Text(
'运行时间: ${_formatDuration(runningTime)}',
style: TextStyle(
fontWeight: FontWeight.w500,
color: colorScheme.primary,
),
),
],
),
],
],
),
);
}
// 获取状态文本
String _getStatusText(ConnectionState state) {
switch (state) {
case ConnectionState.notStarted:
return '未连接';
case ConnectionState.connecting:
return '连接中';
case ConnectionState.connected:
return '已连接';
}
}
// 获取状态颜色
Color _getStatusColor(ConnectionState state, ColorScheme colorScheme) {
switch (state) {
case ConnectionState.notStarted:
return Colors.grey;
case ConnectionState.connecting:
return Colors.orange;
case ConnectionState.connected:
return Colors.green;
}
}
// 优化用户信息卡片
Widget _buildUserInfoCard(ColorScheme colorScheme) {
final km = Provider.of<KM>(context, listen: false);
final isValidIP = _isAutoIP || _isValidIPv4(km.virtualIP);
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),
const Text('用户信息',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Spacer(),
// 添加编辑状态指示器
if (_connectionState == ConnectionState.connected)
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'已锁定',
style: TextStyle(
color: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 16),
// 用户名输入框
TextField(
controller: _usernameController,
enabled: _connectionState != ConnectionState.connected,
onChanged: (value) {
km.username = value;
},
decoration: InputDecoration(
labelText: '用户名',
border: const OutlineInputBorder(),
prefixIcon: Icon(Icons.person, color: colorScheme.primary),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
),
const SizedBox(height: 12),
// IP设置部分
Row(
children: [
Expanded(
child: TextField(
controller: _virtualIPController,
enabled: !_isAutoIP &&
_connectionState != ConnectionState.connected,
onChanged: (value) {
if (!_isAutoIP) {
setState(() {});
km.virtualIP = value;
}
},
decoration: InputDecoration(
labelText: '虚拟网IP',
border: const OutlineInputBorder(),
prefixIcon: Icon(Icons.lan, color: colorScheme.primary),
floatingLabelBehavior: FloatingLabelBehavior.always,
errorText: !isValidIP && !_isAutoIP ? '请输入有效的IPv4地址' : null,
),
),
),
const SizedBox(width: 8),
Column(
children: [
Switch(
value: _isAutoIP,
onChanged: _connectionState != ConnectionState.connected
? (value) {
setState(() {
_isAutoIP = value;
});
km.dynamicIP = value;
}
: null,
),
Text(
_isAutoIP ? "自动" : "手动",
style: const TextStyle(fontSize: 12),
),
],
),
],
),
if (_isAutoIP)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'系统将自动分配虚拟网IP',
style: TextStyle(
color: colorScheme.secondary,
fontSize: 12,
),
),
),
],
),
);
}
// 优化房间信息卡片
Widget _buildRoomInfoCard(ColorScheme colorScheme) {
final km = Provider.of<KM>(context, listen: false);
return FloatingCard(
colorScheme: colorScheme,
maxWidth: 600,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题栏
Row(
children: [
Icon(Icons.meeting_room, color: colorScheme.primary, size: 22),
const SizedBox(width: 8),
const Text('房间信息',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Spacer(),
// 添加编辑状态指示器
if (_connectionState == ConnectionState.connected)
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'已锁定',
style: TextStyle(
color: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 16),
// 房间名称输入框
TextField(
controller: _roomNameController,
enabled: _connectionState != ConnectionState.connected,
onChanged: (value) {
km.roomName = value;
},
decoration: InputDecoration(
labelText: '房间名称',
border: const OutlineInputBorder(),
prefixIcon: Icon(Icons.meeting_room, color: colorScheme.primary),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
),
const SizedBox(height: 12),
// 房间密码输入框
TextField(
controller: _roomPasswordController,
enabled: _connectionState != ConnectionState.connected,
onChanged: (value) {
km.roomPassword = value;
},
obscureText: true,
decoration: InputDecoration(
labelText: '房间密码',
border: const OutlineInputBorder(),
prefixIcon: Icon(Icons.lock, color: colorScheme.primary),
floatingLabelBehavior: FloatingLabelBehavior.always,
// helperText: '留空表示无密码',
),
),
],
),
);
}
// 添加IPv4地址验证方法
bool _isValidIPv4(String ip) {
if (ip.isEmpty) return false;
// 使用正则表达式验证IPv4地址格式
final ipv4Pattern = RegExp(
r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$');
return ipv4Pattern.hasMatch(ip);
}
}
// 添加版本信息卡片
Widget _buildVersionInfoCard(ColorScheme colorScheme) {
// 这里可以从配置或API获取实际版本号
final String appVersion = AppInfoUtil.getFullVersion();
return FloatingCard(
colorScheme: colorScheme,
maxWidth: 600,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题栏
Row(
children: [
Icon(Icons.info_outline, color: colorScheme.primary, size: 22),
const SizedBox(width: 8),
const Text('版本信息',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 16),
// 版本信息列表
_buildVersionItem('软件版本', appVersion, Icons.apps, colorScheme),
const SizedBox(height: 12),
// 使用FutureBuilder处理异步获取的版本信息
FutureBuilder<String>(
future: easytierVersion(),
builder: (context, snapshot) {
String version = snapshot.hasData ? snapshot.data! : "加载中...";
return _buildVersionItem(
'ET内核版本', version, Icons.memory, colorScheme);
},
),
],
),
);
}
// 版本信息项构建方法
Widget _buildVersionItem(
String label, String version, IconData icon, ColorScheme colorScheme) {
return Row(
children: [
Icon(icon, size: 20, color: colorScheme.primary),
const SizedBox(width: 12),
Text('$label:',
style: const TextStyle(
fontWeight: FontWeight.bold,
)),
const SizedBox(width: 8),
Text(
version,
style: TextStyle(
color: colorScheme.secondary,
fontWeight: FontWeight.w500,
),
),
],
);
}