import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; void main() { // 关闭调试绘制(黄色框线) debugPaintSizeEnabled = false; debugPaintPointersEnabled = false; WidgetsFlutterBinding.ensureInitialized(); // 隐藏顶部状态栏和底部操作栏,全屏显示 SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); runApp(const MyApp()); } /// HMI 配色常量(与 Pencil 设计变量一致) class HmiColors { HmiColors._(); /// 主背景 static const bg = Color(0xFFCFD4E1); /// 状态栏/横幅背景 static const banner = Color(0xFF3D5880); static const bannerDark = Color(0xFF2C4060); static const bannerLight = Color(0xFF4E6A92); /// 卡片背景 static const white = Color(0xFFF0F0F5); /// 边框 static const border = Color(0xFFB8BED0); /// 分隔线 static const divider = Color(0xFFD5DAE5); /// 文字 static const textPrimary = Color(0xFF1A2332); static const textSecondary = Color(0xFF5A6A7A); static const textWhite = Color(0xFFFFFFFF); /// 功能色 static const success = Color(0xFF27AE60); static const warning = Color(0xFFF5A623); static const danger = Color(0xFFE74C3C); static const accent = Color(0xFF4A90D9); } /// 应用根组件 class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: '快采 HMI', debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: HmiColors.accent), useMaterial3: true, fontFamily: 'Inter', ), home: const HomePage(), ); } } /// HMI 首页控制面板 class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { final String _currentTime = '2026-03-12 14:35:28'; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: HmiColors.bg, body: FittedBox( fit: BoxFit.contain, alignment: Alignment.center, child: SizedBox( width: 1280, height: 800, child: Column( children: [ _buildStatusBar(), Expanded(child: _buildMainBody()), ], ), ), ), ); } /// 顶部状态栏 (48px 高度) Widget _buildStatusBar() { return Container( height: 48, padding: const EdgeInsets.symmetric(horizontal: 20), decoration: const BoxDecoration( color: HmiColors.banner, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // 左侧:设备图标 + 设备名称 Row( children: [ const Icon( Icons.science_outlined, color: HmiColors.textWhite, size: 18, ), const SizedBox(width: 10), const Text( '污水毒品前处理一体机', style: TextStyle( color: HmiColors.textWhite, fontSize: 18, fontWeight: FontWeight.w600, ), ), ], ), // 中间:导航项 Row( children: [ _buildNavItem('首页', isActive: true), const SizedBox(width: 8), _buildNavItem('程序管理'), const SizedBox(width: 8), _buildNavItem('系统设置'), ], ), // 右侧:时钟 + 状态 + 照明按钮 Row( children: [ Text( _currentTime, style: const TextStyle( color: Color(0xFFFFFFCC), fontSize: 15, fontWeight: FontWeight.normal, ), ), const SizedBox(width: 12), Container( width: 8, height: 8, decoration: const BoxDecoration( color: HmiColors.success, shape: BoxShape.circle, ), ), const SizedBox(width: 4), const Text( '运行中', style: TextStyle( color: HmiColors.success, fontSize: 15, fontWeight: FontWeight.w500, ), ), const SizedBox(width: 12), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), decoration: BoxDecoration( color: HmiColors.bannerLight, borderRadius: BorderRadius.circular(4), ), child: const Row( children: [ Icon( Icons.light_mode, color: HmiColors.warning, size: 14, ), SizedBox(width: 8), Text( '照明', style: TextStyle( color: Color(0xFFFFFFCC), fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ), ], ), ], ), ); } /// 导航项 Widget _buildNavItem(String title, {bool isActive = false}) { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: isActive ? HmiColors.bannerDark : Colors.transparent, borderRadius: BorderRadius.circular(4), ), child: Text( title, style: TextStyle( color: isActive ? HmiColors.textWhite : const Color(0xFFFFFFAA), fontSize: 16, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, ), ), ); } /// 主内容区域 Widget _buildMainBody() { return Padding( padding: const EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 左侧面板 (flex) Expanded( flex: 1, child: _buildLeftPanel(), ), const SizedBox(width: 16), // 右侧面板 (固定 480px) SizedBox( width: 480, child: _buildRightPanel(), ), ], ), ); } /// 左侧面板:设备预览 + 程序列表 Widget _buildLeftPanel() { return Column( children: [ _buildDevicePreview(), const SizedBox(height: 12), Expanded(child: _buildProgramListSection()), ], ); } /// 设备预览卡片 (320px 高度) Widget _buildDevicePreview() { return Container( height: 320, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFF1A2332), Color(0xFF2C4060), Color(0xFF3D5880), ], ), border: Border.all( color: HmiColors.border, width: 1, ), ), clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ // 顶部行:设备信息 + 状态徽章 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '设备信息', style: TextStyle( color: HmiColors.textWhite, fontSize: 16, fontWeight: FontWeight.w600, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: HmiColors.success.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(12), ), child: const Row( children: [ Icon(Icons.check_circle, color: HmiColors.success, size: 14), SizedBox(width: 6), Text( '在线', style: TextStyle( color: HmiColors.success, fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ), ], ), const Spacer(), // 底部状态信息栏 Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10), decoration: BoxDecoration( color: const Color(0x11FFFFFF), borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildInfoItem('步骤', '3/8'), _buildInfoDivider(), _buildInfoItem('剩余时间', '12:35'), _buildInfoDivider(), _buildInfoItem('程序', '标准净化'), ], ), ), ], ), ), ); } /// 信息项 Widget _buildInfoItem(String label, String value) { return Row( children: [ Text( label, style: const TextStyle( color: Color(0xFFFFFFAA), fontSize: 13, ), ), const SizedBox(width: 5), Text( value, style: const TextStyle( color: HmiColors.textWhite, fontSize: 13, fontWeight: FontWeight.w600, ), ), ], ); } /// 信息分隔线 Widget _buildInfoDivider() { return Container( width: 1, height: 14, margin: const EdgeInsets.symmetric(horizontal: 16), color: const Color(0x33FFFFFF), ); } /// 程序列表区域 Widget _buildProgramListSection() { return Container( decoration: BoxDecoration( color: HmiColors.white, borderRadius: BorderRadius.circular(8), border: Border.all( color: HmiColors.border, width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 头部 Padding( padding: const EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '程序列表', style: TextStyle( color: HmiColors.textPrimary, fontSize: 20, fontWeight: FontWeight.w600, ), ), const Text( '共 5 个可用程序', style: TextStyle( color: HmiColors.textSecondary, fontSize: 15, ), ), ], ), ), // 程序卡片列表 Expanded( child: ListView( padding: const EdgeInsets.symmetric(horizontal: 16), children: [ _buildProgramCard('标准净化程序', '水样预处理流程', isSelected: true), const SizedBox(height: 8), _buildProgramCard('快速检测程序', '快速筛查模式', isSelected: false), const SizedBox(height: 8), _buildProgramCard('固相萃取程序', 'SPE 固相萃取', isSelected: false), const SizedBox(height: 8), _buildProgramCard('液液萃取程序', 'LLE 液液萃取', isSelected: false), const SizedBox(height: 8), _buildProgramCard('浓缩定容程序', '样品浓缩处理', isSelected: false), ], ), ), // 底部链接 Padding( padding: const EdgeInsets.all(16), child: InkWell( onTap: () {}, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( '查看详情', style: TextStyle( color: HmiColors.accent, fontSize: 16, fontWeight: FontWeight.w500, ), ), const SizedBox(width: 10), const Icon( Icons.chevron_right, color: HmiColors.accent, size: 16, ), ], ), ), ), ], ), ); } /// 程序卡片 Widget _buildProgramCard(String title, String desc, {bool isSelected = false}) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: HmiColors.white, borderRadius: BorderRadius.circular(8), border: Border.all( color: isSelected ? HmiColors.accent : HmiColors.border, width: isSelected ? 2 : 1, ), ), child: Row( children: [ Container( width: 4, height: 32, decoration: BoxDecoration( color: isSelected ? HmiColors.accent : Colors.transparent, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( color: isSelected ? HmiColors.textPrimary : HmiColors.textSecondary, fontSize: 16, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, ), ), const SizedBox(height: 4), Text( desc, style: const TextStyle( color: HmiColors.textSecondary, fontSize: 13, ), ), ], ), ), Icon( Icons.chevron_right, color: isSelected ? HmiColors.accent : HmiColors.border, size: 20, ), ], ), ); } /// 右侧面板:运行控制 Widget _buildRightPanel() { return Column( children: [ // 当前选择提示 Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: HmiColors.white, borderRadius: BorderRadius.circular(8), border: Border.all( color: HmiColors.border, width: 1, ), ), child: Row( children: [ Icon( Icons.file_present_outlined, color: HmiColors.textSecondary, size: 16, ), const SizedBox(width: 10), const Text( '当前选择: ', style: TextStyle( color: HmiColors.textSecondary, fontSize: 16, ), ), const Text( '标准净化程序', style: TextStyle( color: HmiColors.textPrimary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ), const SizedBox(height: 12), // 运行控制按钮区域 Expanded( child: _buildRunStatusCard(), ), ], ); } /// 运行状态卡片 Widget _buildRunStatusCard() { return Container( decoration: BoxDecoration( color: HmiColors.white, borderRadius: BorderRadius.circular(8), border: Border.all( color: HmiColors.border, width: 1, ), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 运行控制按钮 _buildRunButtons(), const SizedBox(height: 16), Divider(color: HmiColors.divider, height: 1), const SizedBox(height: 16), // 运行状态信息 _buildRunStatusInfo(), const SizedBox(height: 16), Divider(color: HmiColors.divider, height: 1), const SizedBox(height: 16), // 进度条 _buildProgressSection(), const SizedBox(height: 16), Divider(color: HmiColors.divider, height: 1), const SizedBox(height: 16), // 槽位可视化 _buildSlotVisualization(), ], ), ), ); } /// 运行/暂停/停止按钮 Widget _buildRunButtons() { return Row( children: [ Expanded( child: SizedBox( height: 52, child: ElevatedButton.icon( onPressed: () {}, icon: const Icon(Icons.play_arrow, size: 20), label: const Text( '运 行', style: TextStyle( fontSize: 22, fontWeight: FontWeight.w700, ), ), style: ElevatedButton.styleFrom( backgroundColor: HmiColors.success, foregroundColor: HmiColors.textWhite, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), ), const SizedBox(width: 8), SizedBox( width: 100, height: 52, child: ElevatedButton.icon( onPressed: () {}, icon: const Icon(Icons.pause, size: 16), label: const Text( '暂停', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), style: ElevatedButton.styleFrom( backgroundColor: HmiColors.warning, foregroundColor: HmiColors.textWhite, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), const SizedBox(width: 8), SizedBox( width: 100, height: 52, child: ElevatedButton.icon( onPressed: () {}, icon: const Icon(Icons.stop, size: 16), label: const Text( '停止', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), style: ElevatedButton.styleFrom( backgroundColor: HmiColors.danger, foregroundColor: HmiColors.textWhite, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), ], ); } /// 运行状态信息 Widget _buildRunStatusInfo() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '运行状态', style: TextStyle( color: HmiColors.textPrimary, fontSize: 16, fontWeight: FontWeight.w600, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 8, ), decoration: BoxDecoration( color: HmiColors.success, borderRadius: BorderRadius.circular(10), ), child: const Row( children: [ Icon(Icons.autorenew, color: HmiColors.textWhite, size: 14), SizedBox(width: 8), Text( '运行中 - 步骤 3/8', style: TextStyle( color: HmiColors.textWhite, fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ), ], ), const SizedBox(height: 12), // 信息网格 Row( children: [ Expanded( child: _buildInfoGridItem('运行时间', '02:15:30'), ), const SizedBox(width: 8), Expanded( child: _buildInfoGridItem('当前温度', '25.3°C'), ), const SizedBox(width: 8), Expanded( child: _buildInfoGridItem('流速', '1.2 mL/min'), ), ], ), const SizedBox(height: 8), Row( children: [ Expanded( child: _buildInfoGridItem('压力', '0.8 MPa'), ), const SizedBox(width: 8), Expanded( child: _buildInfoGridItem('pH 值', '7.2'), ), const SizedBox(width: 8), Expanded( child: _buildInfoGridItem('电导率', '125 μS/cm'), ), ], ), ], ); } /// 信息网格项 Widget _buildInfoGridItem(String label, String value) { return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: HmiColors.bg, borderRadius: BorderRadius.circular(6), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( color: HmiColors.textSecondary, fontSize: 12, ), ), const SizedBox(height: 4), Text( value, style: const TextStyle( color: HmiColors.textPrimary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ); } /// 进度条区域 Widget _buildProgressSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '程序进度', style: TextStyle( color: HmiColors.textPrimary, fontSize: 16, fontWeight: FontWeight.w600, ), ), const Text( '37.5%', style: TextStyle( color: HmiColors.accent, fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), const SizedBox(height: 10), ClipRRect( borderRadius: BorderRadius.circular(6), child: LinearProgressIndicator( value: 0.375, backgroundColor: HmiColors.border.withValues(alpha: 0.3), valueColor: const AlwaysStoppedAnimation(HmiColors.accent), minHeight: 12, ), ), ], ); } /// 槽位可视化 Widget _buildSlotVisualization() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '样品槽位', style: TextStyle( color: HmiColors.textPrimary, fontSize: 16, fontWeight: FontWeight.w600, ), ), Text( '8/12 已装载', style: const TextStyle( color: HmiColors.textSecondary, fontSize: 14, ), ), ], ), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: List.generate(12, (index) { final isActive = index < 8; return Container( width: 48, height: 32, decoration: BoxDecoration( color: isActive ? HmiColors.success.withValues(alpha: 0.2) : HmiColors.bg, borderRadius: BorderRadius.circular(4), border: Border.all( color: isActive ? HmiColors.success : HmiColors.border, width: 1, ), ), alignment: Alignment.center, child: Text( '${index + 1}', style: TextStyle( color: isActive ? HmiColors.success : HmiColors.textSecondary, fontSize: 14, fontWeight: FontWeight.w500, ), ), ); }), ), ], ); } }