import 'dart:ui'; import 'dart:io' as io show Platform; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:platform_info/platform_info.dart'; import 'package:flutter_udid/flutter_udid.dart'; import '../../../constants/app_constants.dart'; /// 时间: 2026-03-26 /// 功能: 应用信息页面 /// 介绍: 展示应用版本、技术栈、构建信息、设备信息等 /// 最新变化: 添加 UDID 显示 class AppInfoPage extends StatefulWidget { const AppInfoPage({super.key}); @override State createState() => _AppInfoPageState(); } class _AppInfoPageState extends State { String _udid = '获取中...'; @override void initState() { super.initState(); _loadUdid(); } Future _loadUdid() async { try { final String udid = await FlutterUdid.udid; if (mounted) { setState(() { _udid = udid; }); } } catch (e) { if (mounted) { setState(() { _udid = '获取失败'; }); } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: AppBar( title: Text( '应用信息', style: TextStyle( color: AppConstants.primaryColor, fontWeight: FontWeight.bold, ), ), backgroundColor: Colors.white, elevation: 0, centerTitle: true, leading: IconButton( icon: Icon(Icons.arrow_back, color: AppConstants.primaryColor), onPressed: () => Navigator.of(context).pop(), ), ), body: ListView( padding: const EdgeInsets.all(16), children: [ _buildHeaderCard(), const SizedBox(height: 16), _buildTechStackCard(), const SizedBox(height: 16), _buildBuildInfoCard(context), const SizedBox(height: 16), _buildServerInfoCard(), const SizedBox(height: 16), _buildDeviceInfoCard(context), const SizedBox(height: 16), _buildUpdateLogCard(), const SizedBox(height: 16), _buildDesignStyleCard(), const SizedBox(height: 24), _buildBottomIndicator(), ], ), ); } Widget _buildHeaderCard() { 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: [ AppConstants.primaryColor.withValues(alpha: 0.66), AppConstants.primaryColor.withValues(alpha: 0.7), AppConstants.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: AppConstants.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), const Text( '框架 1.3', style: TextStyle(fontSize: 12, color: Colors.white), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4), ), child: const Text( '软件版本 1.5', style: TextStyle( fontSize: 10, color: Colors.white, ), ), ), ], ), ), ], ), ), ], ), ), ), ); } Widget _buildTechStackCard() { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: 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), const Text( '技术栈', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ), const Divider(height: 1), // 2x2 网格布局展示技术栈 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( 'Flutter', '跨平台UI框架', Icons.flutter_dash, Colors.blue, ), _buildTechStackItem('Dart', '编程语言', Icons.code, Colors.teal), _buildTechStackItem('SP', '本地存储', Icons.storage, Colors.orange), _buildTechStackItem( 'dio', '网络处理', Icons.network_check, Colors.purple, ), ], ), ), ], ), ); } Widget _buildBuildInfoCard(BuildContext context) { 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: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: 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.green.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(Icons.build, color: Colors.green[700], size: 20), ), const SizedBox(width: 12), const Text( '构建信息', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ), const Divider(height: 1), _buildCopyableItem(context, '版本号', '1.2', Icons.verified), _buildCopyableItem( context, 'Builder version', '26d03a26', Icons.developer_mode, ), _buildInfoItem( '打包时间', DateTime.now().toString().split(' ').first, Icons.schedule, ), _buildInfoItem('Build SDK', buildSdk, Icons.new_releases), _buildLicenseItem(context), ], ), ); } Widget _buildLicenseItem(BuildContext context) { return InkWell( onTap: () => _showFlutterLicense(context), borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ Icon(Icons.gavel, size: 20, color: Colors.grey[600]), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '开源框架', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500), ), const SizedBox(height: 2), Text( 'Flutter SDK', style: TextStyle(fontSize: 12, color: Colors.grey[500]), ), ], ), ), Icon(Icons.chevron_right, size: 20, color: Colors.grey[400]), ], ), ), ); } void _showFlutterLicense(BuildContext context) { 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': 'Platform Info', 'license': 'MIT'}, {'name': 'flutter_udid', 'license': 'MIT'}, ]; showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: [ Icon(Icons.description, color: AppConstants.primaryColor), const SizedBox(width: 8), const Text('开源框架'), ], ), 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: Colors.grey[50], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.withValues(alpha: 0.2)), ), child: Row( children: [ Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: AppConstants.primaryColor.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(6), ), child: Icon( Icons.widgets, size: 16, color: AppConstants.primaryColor, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item['name']!, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 2), Text( item['license']!, style: TextStyle( fontSize: 12, color: Colors.grey[500], ), ), ], ), ), ], ), ); }).toList(), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text( '关闭', style: TextStyle(color: AppConstants.primaryColor), ), ), ], ), ); } Widget _buildServerInfoCard() { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: 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), const Text( '后端服务', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ), const Divider(height: 1), _buildInfoItem('后端语言', 'PHP', Icons.php), _buildInfoItem('Web服务器', 'Nginx', Icons.web), ], ), ); } Widget _buildDeviceInfoCard(BuildContext context) { 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( 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( 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( vm: () => 'VM (Dart)', js: () => 'JS (Web)', orElse: () => null, ) ?? 'Unknown'; return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: 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.purple.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.devices, color: Colors.purple[700], size: 20, ), ), const SizedBox(width: 12), const Text( '设备信息', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ), const Divider(height: 1), _buildInfoItem('操作系统', platformName, Icons.phone_iphone), if (!isHarmonyOS) ...[ _buildInfoItem('设计风格', designStyle, Icons.palette), ], _buildInfoItem('设备类型', deviceType, Icons.devices), _buildInfoItem('构建模式', buildMode, Icons.build), _buildInfoItem('运行环境', runtimeEnv, Icons.code), _buildCopyableItem( context, 'Flutter UDID', _udid, Icons.perm_identity, ), Padding( padding: const EdgeInsets.all(16), child: Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.withValues(alpha: 0.2)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.info_outline, size: 16, color: Colors.grey[600], ), const SizedBox(width: 8), Text( '设备详细信息', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: 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: Colors.grey[600]), ), const SizedBox(height: 4), Text( '像素密度: ${MediaQuery.of(context).devicePixelRatio.toStringAsFixed(2)} (越大越好)', style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ], ), ), ), ], ), ); } Widget _buildUpdateLogCard() { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: 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), const Text( '软件更新日志', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ), const Divider(height: 1), Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildUpdateItem('版本 1.2.40', '2026-03-27', [ '新增:主题个性化页面', '新增:设计风格卡片', '优化:界面布局和响应式设计', ]), const SizedBox(height: 16), _buildUpdateItem('版本 1.2.39', '2026-03-27', [ '修复:引导页滑动问题', '优化:左侧进度条位置', '新增:协议内容焦点功能', ]), const SizedBox(height: 16), _buildUpdateItem('版本 1.2.38', '2026-03-27', [ '优化:引导页滑动逻辑', '新增:页面进度指示器', '更新:滑动提示文本', ]), ], ), ), ], ), ); } Widget _buildUpdateItem(String version, String date, List changes) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.withValues(alpha: 0.2)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( version, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, ), ), Text( date, style: TextStyle(fontSize: 12, color: 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), const Text('• ', style: TextStyle(color: Colors.blue)), Expanded( child: Text( change, style: TextStyle(fontSize: 12, color: Colors.grey[700]), ), ), ], ), ); }).toList(), ), ], ), ); } Widget _buildDesignStyleCard() { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: []), ); } // 2x2 网格技术栈项 Widget _buildTechStackItem( String title, String value, IconData icon, Color color, ) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withAlpha(20), borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withAlpha(50), width: 1), ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: color.withAlpha(30), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, size: 20, color: color), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( title, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: color, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( value, style: TextStyle(fontSize: 11, color: Colors.grey[600]), overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ); } Widget _buildInfoItem(String title, String value, IconData icon) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ Icon(icon, size: 20, color: Colors.grey[600]), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 2), Text( value, style: TextStyle(fontSize: 12, color: Colors.grey[500]), ), ], ), ), ], ), ); } Widget _buildCopyableItem( BuildContext context, String title, String value, IconData icon, ) { 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: Colors.grey[600]), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 2), Text( value, style: TextStyle(fontSize: 12, color: Colors.grey[500]), ), ], ), ), Icon( Icons.content_copy, size: 16, color: AppConstants.primaryColor.withValues(alpha: 0.6), ), ], ), ), ); } void _copyToClipboard(BuildContext context, String text) { Clipboard.setData(ClipboardData(text: text)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.check_circle, color: Colors.white, size: 20), const SizedBox(width: 8), Text('$text 已复制到剪贴板'), ], ), backgroundColor: AppConstants.primaryColor, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), duration: const Duration(seconds: 2), ), ); } Widget _buildBottomIndicator() { return Container( padding: const EdgeInsets.symmetric(vertical: 24), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container(width: 10, height: 1, color: Colors.grey[300]), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( '若有卡顿闪退信息,请将此页面长截图发给开发者', style: TextStyle(fontSize: 12, color: Colors.grey[400]), ), ), Container(width: 10, height: 1, color: Colors.grey[300]), ], ), ); } }