Files
wushu/lib/views/profile/app-info.dart
Developer cba04235c8 release
2026-04-03 03:26:06 +08:00

1489 lines
47 KiB
Dart

import 'dart:ui';
import 'dart:io' as io show Platform;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:platform_info/platform_info.dart';
import 'package:flutter_udid/flutter_udid.dart';
import '../../../config/app_config.dart';
import '../../../constants/app_constants.dart';
import '../../../models/colors/theme_colors.dart';
import '../../../controllers/shared_preferences_storage_controller.dart';
import '../../../services/get/theme_controller.dart';
/// 时间: 2026-03-26
/// 功能: 应用信息页面
/// 介绍: 展示应用版本、技术栈、构建信息、设备信息等
/// 最新变化: 添加 UDID 显示
class AppInfoPage extends StatefulWidget {
const AppInfoPage({super.key});
@override
State<AppInfoPage> createState() => _AppInfoPageState();
}
class _AppInfoPageState extends State<AppInfoPage> {
String _udid = '获取中...';
bool _isDeveloperMode = false;
int _tapCount = 0;
DateTime? _lastTapTime;
final ThemeController _themeController = Get.find<ThemeController>();
@override
void initState() {
super.initState();
_loadUdid();
_loadDeveloperMode();
}
Future<void> _loadUdid() async {
try {
final String udid = await FlutterUdid.udid;
if (mounted) {
setState(() {
_udid = udid;
});
}
} catch (e) {
if (mounted) {
setState(() {
_udid = '获取失败';
});
}
}
}
Future<void> _loadDeveloperMode() async {
final isEnabled = await SharedPreferencesStorageController.getBool(
'developer_mode_enabled',
defaultValue: false,
);
if (mounted) {
setState(() {
_isDeveloperMode = isEnabled;
});
}
}
Future<void> _saveDeveloperMode(bool enabled) async {
await SharedPreferencesStorageController.setBool(
'developer_mode_enabled',
enabled,
);
}
void _onFrameworkTap() {
final now = DateTime.now();
if (_lastTapTime != null && now.difference(_lastTapTime!).inSeconds > 2) {
_tapCount = 0;
}
_lastTapTime = now;
_tapCount++;
if (_tapCount >= 5 && !_isDeveloperMode) {
setState(() {
_isDeveloperMode = true;
});
_saveDeveloperMode(true);
_tapCount = 0;
final primaryColor = _themeController.currentThemeColor;
Get.snackbar(
'提示',
'开发者模式激活',
snackPosition: SnackPosition.BOTTOM,
colorText: primaryColor,
duration: const Duration(seconds: 2),
borderRadius: 8,
margin: const EdgeInsets.all(16),
);
}
}
@override
Widget build(BuildContext context) {
return Obx(() {
final isDark = _themeController.isDarkMode;
final primaryColor = _themeController.currentThemeColor;
return Scaffold(
backgroundColor: isDark
? const Color(0xFF1A1A1A)
: const Color(0xFFF5F5F5),
appBar: AppBar(
title: Text(
'应用信息',
style: TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
),
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: primaryColor),
onPressed: () => Navigator.of(context).pop(),
),
actions: [
if (_isDeveloperMode)
IconButton(
icon: const Icon(Icons.bug_report, color: Colors.green),
onPressed: () {
final primaryColor = _themeController.currentThemeColor;
Get.snackbar(
'提示',
'调试信息已激活',
snackPosition: SnackPosition.BOTTOM,
colorText: primaryColor,
duration: const Duration(seconds: 2),
borderRadius: 8,
margin: const EdgeInsets.all(16),
);
},
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildHeaderCard(primaryColor),
const SizedBox(height: 16),
_buildTechStackCard(isDark),
const SizedBox(height: 16),
_buildBuildInfoCard(context, isDark, primaryColor),
const SizedBox(height: 16),
_buildServerInfoCard(isDark),
const SizedBox(height: 16),
_buildDeviceInfoCard(context, isDark, primaryColor),
const SizedBox(height: 16),
_buildUpdateLogCard(isDark),
const SizedBox(height: 24),
_buildBottomIndicator(isDark),
],
),
);
});
}
Widget _buildHeaderCard(Color primaryColor) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
primaryColor.withValues(alpha: 0.66),
primaryColor.withValues(alpha: 0.7),
primaryColor.withValues(alpha: 0.99),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: primaryColor.withValues(alpha: 0.35),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.25),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
),
),
child: const Center(
child: Text('📱', style: TextStyle(fontSize: 36)),
),
),
const SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'情景诗词',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 6),
Text(
'Poes · 应用信息',
style: TextStyle(
fontSize: 13,
color: Colors.white.withValues(alpha: 0.85),
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.white.withValues(alpha: 0.25),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.info_outline,
size: 14,
color: Colors.white.withValues(alpha: 0.9),
),
const SizedBox(width: 6),
GestureDetector(
onTap: _onFrameworkTap,
child: const Text(
'框架 1.4',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: _isDeveloperMode
? Colors.green.withValues(alpha: 0.3)
: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
),
child: Obx(
() => Text(
'软件版本 ${AppConfig.appVersion}',
style: TextStyle(
fontSize: 10,
color: _isDeveloperMode
? Colors.green
: Colors.white,
fontWeight: _isDeveloperMode
? FontWeight.bold
: FontWeight.normal,
),
),
),
),
],
),
),
],
),
),
],
),
),
),
);
}
Widget _buildTechStackCard(bool isDark) {
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.code, color: Colors.blue[700], size: 20),
),
const SizedBox(width: 12),
Text(
'技术栈',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
Padding(
padding: const EdgeInsets.all(16),
child: GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 2.2,
children: [
_buildTechStackItem(
'Dart',
'编程语言',
Icons.flutter_dash,
Colors.blue,
isDark,
),
_buildTechStackItem(
'Getx',
'状态管理库',
Icons.code,
Colors.teal,
isDark,
),
_buildTechStackItem(
'SP',
'本地存储',
Icons.storage,
Colors.orange,
isDark,
),
_buildTechStackItem(
'dio',
'网络处理',
Icons.network_check,
Colors.purple,
isDark,
),
],
),
),
],
),
);
}
Widget _buildBuildInfoCard(
BuildContext context,
bool isDark,
Color primaryColor,
) {
String buildSdk = 'Unknown';
try {
final String osName = io.Platform.operatingSystem;
if (osName == 'ohos' ||
osName == 'harmonyos' ||
osName == 'openharmony') {
buildSdk = 'Deveco API 23';
} else if (io.Platform.isAndroid) {
buildSdk = 'Android Target 36';
} else if (io.Platform.isWindows) {
buildSdk = 'Win10 SDK';
} else if (io.Platform.isIOS) {
buildSdk = 'iOS 26';
} else if (io.Platform.isMacOS) {
buildSdk = 'macOS 18';
} else if (io.Platform.isLinux) {
buildSdk = 'Linux 20';
} else {
buildSdk = 'PHP 7.4';
}
} catch (e) {
buildSdk = 'PHP 7.4';
}
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.build, color: primaryColor, size: 20),
),
const SizedBox(width: 12),
Text(
'构建信息',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
Obx(
() => _buildCopyableItem(
context,
'版本号',
AppConfig.appVersion,
Icons.verified,
isDark,
),
),
Obx(
() => _buildCopyableItem(
context,
'Builder version',
AppConfig.appVersionCode.toString(),
Icons.developer_mode,
isDark,
),
),
_buildInfoItem(
'打包时间',
DateTime.now().toString().split(' ').first,
Icons.schedule,
isDark,
),
_buildInfoItem('Build SDK', buildSdk, Icons.new_releases, isDark),
_buildLicenseItem(context, isDark, primaryColor),
],
),
);
}
Widget _buildLicenseItem(
BuildContext context,
bool isDark,
Color primaryColor,
) {
return InkWell(
onTap: () => _showFlutterLicense(context, isDark, primaryColor),
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Icon(Icons.gavel, size: 20, color: primaryColor),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'开源框架',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black,
),
),
const SizedBox(height: 2),
Text(
'Flutter SDK',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[500] : Colors.grey[500],
),
),
],
),
),
Icon(
Icons.chevron_right,
size: 20,
color: isDark ? Colors.grey[600] : Colors.grey[400],
),
],
),
),
);
}
void _showFlutterLicense(
BuildContext context,
bool isDark,
Color primaryColor,
) {
final licenses = [
{'name': 'Flutter SDK', 'license': 'BSD 3-Clause'},
{'name': 'OpenHarmony SDK', 'license': 'Apache 2.0'},
{'name': 'Cupertino Icons', 'license': 'MIT'},
{'name': 'Shared Preferences', 'license': 'BSD 3-Clause'},
{'name': 'Dio', 'license': 'MIT'},
{'name': 'GetX', 'license': 'MIT'},
{'name': 'Platform Info', 'license': 'MIT'},
{'name': 'flutter_udid', 'license': 'MIT'},
];
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
title: Row(
children: [
Icon(Icons.description, color: primaryColor),
const SizedBox(width: 8),
Text(
'开源框架',
style: TextStyle(color: isDark ? Colors.white : Colors.black),
),
],
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: licenses.map((item) {
return Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark
? Colors.grey[700]!.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.2),
),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Icon(Icons.widgets, size: 16, color: primaryColor),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['name']!,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black,
),
),
const SizedBox(height: 2),
Text(
item['license']!,
style: TextStyle(
fontSize: 12,
color: isDark
? Colors.grey[400]
: Colors.grey[500],
),
),
],
),
),
],
),
);
}).toList(),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('关闭', style: TextStyle(color: primaryColor)),
),
],
),
);
}
Widget _buildServerInfoCard(bool isDark) {
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.dns, color: Colors.orange[700], size: 20),
),
const SizedBox(width: 12),
Text(
'后端服务',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
_buildInfoItem('后端语言', 'PHP', Icons.php, isDark),
_buildInfoItem('Web服务器', 'Nginx', Icons.web, isDark),
],
),
);
}
Widget _buildDeviceInfoCard(
BuildContext context,
bool isDark,
Color primaryColor,
) {
final platform = Platform.instance;
bool isHarmonyOS = false;
String platformName = 'Unknown';
try {
final String osName = io.Platform.operatingSystem;
final String osVersion = io.Platform.operatingSystemVersion.toLowerCase();
if (osName == 'ohos' || osName == 'harmonyos' || osName == 'harmonyos') {
platformName = 'HarmonyOS';
isHarmonyOS = true;
} else if (io.Platform.isAndroid) {
platformName = 'Android';
if (osVersion.contains('harmony') ||
osVersion.contains('ohos') ||
osVersion.contains('openharmony')) {
platformName = 'HarmonyOS';
isHarmonyOS = true;
}
} else if (io.Platform.isIOS) {
platformName = 'iOS';
} else if (io.Platform.isMacOS) {
platformName = 'macOS';
} else if (io.Platform.isWindows) {
platformName = 'Windows';
} else if (io.Platform.isLinux) {
platformName = 'Linux';
} else if (io.Platform.isFuchsia) {
platformName = 'Fuchsia';
} else {
platformName = osName[0].toUpperCase() + osName.substring(1);
}
} catch (e) {
platformName = switch (platform.operatingSystem) {
const OperatingSystem.android() => 'Android',
const OperatingSystem.fuchsia() => 'Fuchsia',
const OperatingSystem.iOS() => 'iOS',
const OperatingSystem.linux() => 'Linux',
const OperatingSystem.macOS() => 'macOS',
const OperatingSystem.windows() => 'Windows',
const OperatingSystem.unknown() || _ => 'Unknown',
};
}
final String designStyle =
platform.when<String?>(
material: () => isHarmonyOS ? 'HarmonyOS Design' : 'Material Design',
cupertino: () => 'Cupertino Design',
orElse: () => null,
) ??
'Unknown';
String deviceType = '未知设备';
if (isHarmonyOS) {
final String osName = io.Platform.operatingSystem;
if (osName == 'ohos') {
deviceType = 'OHOS';
} else if (osName == 'harmonyos') {
deviceType = 'HarmonyOS';
} else if (osName == 'openharmony') {
deviceType = 'OpenHarmony';
} else {
deviceType = 'HarmonyOS';
}
} else {
deviceType =
platform.when<String?>(
mobile: () => '移动设备',
desktop: () => '桌面设备',
js: () => 'Web浏览器',
orElse: () => null,
) ??
'未知设备';
}
final String buildMode = switch (platform.buildMode) {
BuildMode$Debug _ => 'Debug',
BuildMode$Profile _ => 'Profile',
BuildMode$Release _ => 'Release',
};
final String runtimeEnv =
platform.when<String?>(
vm: () => 'VM (Dart)',
js: () => 'JS (Web)',
orElse: () => null,
) ??
'Unknown';
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.devices, color: primaryColor, size: 20),
),
const SizedBox(width: 12),
Text(
'设备信息',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
child: GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 2.0,
children: [
_buildGridInfoItem(
'操作系统',
platformName,
Icons.phone_iphone,
isDark,
primaryColor,
),
if (!isHarmonyOS) ...[
_buildGridInfoItem(
'设计风格',
designStyle,
Icons.palette,
isDark,
primaryColor,
),
],
_buildGridInfoItem(
'设备类型',
deviceType,
Icons.devices,
isDark,
primaryColor,
),
_buildGridInfoItem(
'构建模式',
buildMode,
Icons.build,
isDark,
primaryColor,
),
_buildGridInfoItem(
'运行环境',
runtimeEnv,
Icons.code,
isDark,
primaryColor,
),
_buildGridCopyableItem(
context,
'Flutter UUID',
_udid,
Icons.perm_identity,
isDark,
primaryColor,
),
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark
? Colors.grey[700]!.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.2),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.info_outline,
size: 16,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
const SizedBox(width: 8),
Text(
'设备详细信息',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
),
],
),
const SizedBox(height: 8),
Text(
'窗口尺寸: ${MediaQuery.of(context).size.width.toStringAsFixed(0)} x ${MediaQuery.of(context).size.height.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
const SizedBox(height: 4),
Text(
'像素密度: ${MediaQuery.of(context).devicePixelRatio.toStringAsFixed(2)} (越大越好)',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
),
),
),
],
),
);
}
Widget _buildUpdateLogCard(bool isDark) {
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.new_releases,
color: Colors.blue[700],
size: 20,
),
),
const SizedBox(width: 12),
Text(
'软件更新日志',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildUpdateItem('版本 1.4.1', '2026-04-03', [
'新增:从 pubspec.yaml 动态获取版本号',
'新增:响应式版本号显示',
'优化:解决 package_info_plus 依赖冲突',
'修复:版本号不显示的问题',
], isDark),
const SizedBox(height: 16),
_buildUpdateItem('版本 1.3.59', '2026-04-03', [
'修复:出处字段被时间提示语遮挡的问题',
'修复:时间提示语布局遮挡问题',
'优化:诗词卡片内容布局',
], isDark),
],
),
),
],
),
);
}
Widget _buildUpdateItem(
String version,
String date,
List<String> changes,
bool isDark,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark
? Colors.grey[700]!.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.2),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
version,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
Text(
date,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: changes.map((change) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 8),
Text(
'',
style: TextStyle(
color: isDark ? Colors.blue[300] : Colors.blue,
),
),
Expanded(
child: Text(
change,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
),
),
],
),
);
}).toList(),
),
],
),
);
}
Widget _buildDesignStyleCard(bool isDark) {
final platform = Platform.instance;
final String designStyle =
platform.when<String?>(
material: () => 'Material Design',
cupertino: () => 'Cupertino Design',
orElse: () => null,
) ??
'Unknown';
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.pink.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.palette, color: Colors.pink[700], size: 20),
),
const SizedBox(width: 12),
Text(
'设计风格',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black,
),
),
],
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
_buildInfoItem('当前风格', designStyle, Icons.style, isDark),
_buildInfoItem('设计语言', 'Flutter', Icons.design_services, isDark),
_buildInfoItem('交互模式', '触摸优先', Icons.touch_app, isDark),
],
),
);
}
// 2x2 网格技术栈项
Widget _buildTechStackItem(
String title,
String value,
IconData icon,
Color color,
bool isDark,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withValues(alpha: 0.2)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 20),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
value,
style: TextStyle(
fontSize: 11,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
);
}
// 网格布局版本的信息项
Widget _buildGridInfoItem(
String title,
String value,
IconData icon,
bool isDark,
Color primaryColor,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDark
? Colors.grey[700]!.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.1),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, size: 18, color: primaryColor),
const SizedBox(width: 8),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: isDark ? Colors.grey[400] : Colors.grey[700],
),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 6),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
],
),
);
}
// 网格布局版本的可复制信息项
Widget _buildGridCopyableItem(
BuildContext context,
String title,
String value,
IconData icon,
bool isDark,
Color primaryColor,
) {
return InkWell(
onTap: () => _copyToClipboard(context, value),
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDark
? Colors.grey[700]!.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.1),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Icon(icon, size: 16, color: primaryColor),
const SizedBox(width: 6),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: isDark ? Colors.grey[400] : Colors.grey[700],
),
overflow: TextOverflow.ellipsis,
),
),
Icon(
Icons.content_copy,
size: 14,
color: primaryColor.withValues(alpha: 0.6),
),
],
),
const SizedBox(height: 6),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
);
}
Widget _buildInfoItem(
String title,
String value,
IconData icon,
bool isDark,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Icon(
icon,
size: 20,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black,
),
),
const SizedBox(height: 2),
Text(
value,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[500],
),
),
],
),
),
],
),
);
}
Widget _buildCopyableItem(
BuildContext context,
String title,
String value,
IconData icon,
bool isDark,
) {
return InkWell(
onTap: () => _copyToClipboard(context, value),
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Icon(
icon,
size: 20,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black,
),
),
const SizedBox(height: 2),
Text(
value,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[500],
),
),
],
),
),
Icon(
Icons.content_copy,
size: 16,
color: isDark ? Colors.grey[600] : Colors.grey[400],
),
],
),
),
);
}
void _copyToClipboard(BuildContext context, String text) {
Clipboard.setData(ClipboardData(text: text));
final primaryColor = _themeController.currentThemeColor;
Get.snackbar(
'复制成功',
'$text 已复制到剪贴板',
snackPosition: SnackPosition.BOTTOM,
colorText: primaryColor,
duration: const Duration(seconds: 2),
borderRadius: 8,
margin: const EdgeInsets.all(16),
icon: Icon(Icons.check_circle, color: primaryColor, size: 20),
);
}
Widget _buildBottomIndicator(bool isDark) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
Icon(
Icons.code,
size: 24,
color: isDark ? Colors.grey[600] : Colors.grey[400],
),
const SizedBox(height: 8),
Text(
'用心开发,只为更好的体验',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[500] : Colors.grey[500],
),
),
const SizedBox(height: 4),
Text(
'© 2026 ${AppConfig.appName}',
style: TextStyle(
fontSize: 11,
color: isDark ? Colors.grey[600] : Colors.grey[400],
),
),
],
),
);
}
}