import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; /// 属性面板组件 /// /// 弹出式属性编辑(元件值、封装、网络名) /// 实时预览修改效果 /// 输入验证与错误提示 class PropertyPanelWidget extends ConsumerStatefulWidget { /// 要编辑的属性数据 final PropertyData propertyData; /// 属性变更回调 final Function(PropertyData)? onPropertyChanged; /// 实时预览回调 final Function(PropertyData)? onPreview; /// 面板位置(底部弹出/侧边弹出) final PropertyPanelPosition position; const PropertyPanelWidget({ super.key, required this.propertyData, this.onPropertyChanged, this.onPreview, this.position = PropertyPanelPosition.bottom, }); @override ConsumerState createState() => _PropertyPanelWidgetState(); } class _PropertyPanelWidgetState extends ConsumerState { late PropertyData _editedData; final _formKey = GlobalKey(); final _refDesignatorController = TextEditingController(); final _valueController = TextEditingController(); final _footprintController = TextEditingController(); final _netNameController = TextEditingController(); // 错误信息 String? _refDesignatorError; String? _valueError; String? _footprintError; // 是否已修改 bool _hasChanges = false; @override void initState() { super.initState(); _editedData = widget.propertyData.copy(); _refDesignatorController.text = _editedData.refDesignator ?? ''; _valueController.text = _editedData.value ?? ''; _footprintController.text = _editedData.footprint ?? ''; _netNameController.text = _editedData.netName ?? ''; } @override void dispose() { _refDesignatorController.dispose(); _valueController.dispose(); _footprintController.dispose(); _netNameController.dispose(); super.dispose(); } void _validateRefDesignator(String value) { if (value.isEmpty) { setState(() => _refDesignatorError = '请输入位号'); return; } // 位号格式验证:字母 + 数字 (如 R1, C2, U3) final regex = RegExp(r'^[A-Z]+\d+$'); if (!regex.hasMatch(value)) { setState(() => _refDesignatorError = '位号格式错误 (如 R1, C2, U3)'); return; } setState(() => _refDesignatorError = null); } void _validateValue(String value) { if (value.isEmpty) { setState(() => _valueError = null); // 值可以为空 return; } // 根据元件类型验证值格式 switch (_editedData.componentType) { case ComponentType.resistor: // 电阻值:10k, 4.7M, 100R 等 final regex = RegExp(r'^\d+(\.\d+)?[kKmMgGpP]?$'); if (!regex.hasMatch(value)) { setState(() => _valueError = '电阻值格式错误 (如 10k, 4.7M, 100R)'); return; } break; case ComponentType.capacitor: // 电容值:10u, 100n, 1p 等 final regex = RegExp(r'^\d+(\.\d+)?[uUnNpPmM]?$'); if (!regex.hasMatch(value)) { setState(() => _valueError = '电容值格式错误 (如 10u, 100n, 1p)'); return; } break; default: _valueError = null; break; } setState(() => _valueError = null); } void _validateFootprint(String value) { if (value.isEmpty) { setState(() => _footprintError = null); // 封装可以为空 return; } // 封装格式验证:简单验证是否包含字母和数字 if (!RegExp(r'[A-Za-z]').hasMatch(value) || !RegExp(r'\d').hasMatch(value)) { setState(() => _footprintError = '封装格式错误 (如 0805, SOT23)'); return; } setState(() => _footprintError = null); } void _onTextChanged() { setState(() { _editedData.refDesignator = _refDesignatorController.text; _editedData.value = _valueController.text; _editedData.footprint = _footprintController.text; _editedData.netName = _netNameController.text; _hasChanges = true; }); // 实时预览 widget.onPreview?.call(_editedData); } void _saveChanges() { // 验证所有字段 _validateRefDesignator(_refDesignatorController.text); _validateValue(_valueController.text); _validateFootprint(_footprintController.text); // 如果有错误,不保存 if (_refDesignatorError != null || _valueError != null || _footprintError != null) { return; } widget.onPropertyChanged?.call(_editedData); Navigator.of(context).pop(_editedData); } void _cancelChanges() { Navigator.of(context).pop(null); } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 16, offset: const Offset(0, -4), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 顶部标题栏 _buildHeader(), const Divider(height: 1), // 属性表单 Flexible( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 元件类型信息 _buildComponentInfo(), const SizedBox(height: 16), // 位号编辑 _buildRefDesignatorField(), const SizedBox(height: 16), // 值编辑 _buildValueField(), const SizedBox(height: 16), // 封装编辑 _buildFootprintField(), const SizedBox(height: 16), // 网络名编辑(只读,显示连接的网络) _buildNetNameField(), const SizedBox(height: 16), // 旋转和镜像 _buildRotationMirror(), ], ), ), ), ), const Divider(height: 1), // 底部操作按钮 _buildActionButtons(), ], ), ); } Widget _buildHeader() { return Container( padding: const EdgeInsets.all(16), child: Row( children: [ const Icon(Icons.edit_attributes, size: 24), const SizedBox(width: 12), const Text( '属性编辑', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const Spacer(), if (_hasChanges) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Text( '未保存', style: TextStyle( fontSize: 12, color: Colors.orange[700], ), ), ), IconButton( icon: const Icon(Icons.close), onPressed: _cancelChanges, ), ], ), ); } Widget _buildComponentInfo() { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.05), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( _getComponentIcon(widget.propertyData.componentType), color: Theme.of(context).primaryColor, size: 32, ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _getComponentTypeName(widget.propertyData.componentType), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), Text( widget.propertyData.symbolName ?? '默认符号', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ], ), ); } Widget _buildRefDesignatorField() { return TextFormField( controller: _refDesignatorController, decoration: InputDecoration( labelText: '位号', hintText: '如 R1, C2, U3', prefixIcon: const Icon(Icons.tag), errorText: _refDesignatorError, border: const OutlineInputBorder(), helperText: '元件的唯一标识符', ), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, onChanged: (value) { _validateRefDesignator(value); _onTextChanged(); }, validator: (value) { if (value == null || value.isEmpty) { return '请输入位号'; } return _refDesignatorError; }, ); } Widget _buildValueField() { return TextFormField( controller: _valueController, decoration: InputDecoration( labelText: '值', hintText: _getValueHint(), prefixIcon: const Icon(Icons.memory), errorText: _valueError, border: const OutlineInputBorder(), helperText: _getValueHelper(), ), textInputAction: TextInputAction.next, onChanged: (value) { _validateValue(value); _onTextChanged(); }, validator: (value) => _valueError, ); } Widget _buildFootprintField() { return TextFormField( controller: _footprintController, decoration: InputDecoration( labelText: '封装', hintText: '如 0805, SOT23, SOIC8', prefixIcon: const Icon(Icons.grid_view), errorText: _footprintError, border: const OutlineInputBorder(), helperText: 'PCB 封装型号', ), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, onChanged: (value) { _validateFootprint(value); _onTextChanged(); }, validator: (value) => _footprintError, ); } Widget _buildNetNameField() { return TextFormField( controller: _netNameController, decoration: InputDecoration( labelText: '网络名', hintText: '自动或手动命名', prefixIcon: const Icon(Icons.link), border: const OutlineInputBorder(), helperText: '元件引脚连接的网络', ), readOnly: true, textInputAction: TextInputAction.done, ); } Widget _buildRotationMirror() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '旋转与镜像', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Row( children: [ // 旋转按钮 Expanded( child: _buildRotationButton(0), ), const SizedBox(width: 8), Expanded( child: _buildRotationButton(90), ), const SizedBox(width: 8), Expanded( child: _buildRotationButton(180), ), const SizedBox(width: 8), Expanded( child: _buildRotationButton(270), ), ], ), const SizedBox(height: 8), Row( children: [ // 镜像按钮 Expanded( child: OutlinedButton.icon( onPressed: () { setState(() { _editedData.mirrorX = !_editedData.mirrorX; _onTextChanged(); }); }, icon: Icon( _editedData.mirrorX ? Icons.flip : Icons.flip_outlined, ), label: const Text('水平'), style: OutlinedButton.styleFrom( foregroundColor: _editedData.mirrorX ? Theme.of(context).primaryColor : Colors.grey[700], ), ), ), const SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: () { setState(() { _editedData.mirrorY = !_editedData.mirrorY; _onTextChanged(); }); }, icon: Icon( Icons.flip, color: _editedData.mirrorY ? Theme.of(context).primaryColor : Colors.grey[700], ), label: const Text('垂直'), style: OutlinedButton.styleFrom( foregroundColor: _editedData.mirrorY ? Theme.of(context).primaryColor : Colors.grey[700], ), ), ), ], ), ], ); } Widget _buildRotationButton(int degrees) { final isSelected = _editedData.rotation == degrees; return OutlinedButton( onPressed: () { setState(() { _editedData.rotation = degrees; _onTextChanged(); }); }, style: OutlinedButton.styleFrom( backgroundColor: isSelected ? Theme.of(context).primaryColor.withOpacity(0.1) : Colors.transparent, foregroundColor: isSelected ? Theme.of(context).primaryColor : Colors.grey[700], ), child: Transform.rotate( angle: degrees * 3.14159 / 180, child: const Icon(Icons.rotate_right, size: 20), ), ); } Widget _buildActionButtons() { return Padding( padding: const EdgeInsets.all(16), child: Row( children: [ Expanded( child: OutlinedButton( onPressed: _cancelChanges, style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), child: const Text('取消'), ), ), const SizedBox(width: 16), Expanded( child: ElevatedButton( onPressed: _hasChanges ? _saveChanges : null, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), child: const Text('保存'), ), ), ], ), ); } String _getComponentTypeName(ComponentType type) { switch (type) { case ComponentType.resistor: return '电阻'; case ComponentType.capacitor: return '电容'; case ComponentType.inductor: return '电感'; case ComponentType.diode: return '二极管'; case ComponentType.transistor: return '三极管'; case ComponentType.ic: return '集成电路'; case ComponentType.connector: return '连接器'; case ComponentType.other: return '其他'; } } IconData _getComponentIcon(ComponentType type) { switch (type) { case ComponentType.resistor: return Icons.rectangle_outlined; case ComponentType.capacitor: return Icons.battery_full; case ComponentType.inductor: return Icons.waves; case ComponentType.diode: return Icons.arrow_right_alt; case ComponentType.transistor: return Icons.memory; case ComponentType.ic: return Icons.dns; case ComponentType.connector: return Icons.usb; case ComponentType.other: return Icons.category; } } String _getValueHint() { switch (widget.propertyData.componentType) { case ComponentType.resistor: return '如 10k, 4.7M, 100R'; case ComponentType.capacitor: return '如 10u, 100n, 1p'; case ComponentType.inductor: return '如 10uH, 1mH'; default: return '元件值'; } } String _getValueHelper() { switch (widget.propertyData.componentType) { case ComponentType.resistor: return '电阻值 (欧姆)'; case ComponentType.capacitor: return '电容值 (法拉)'; case ComponentType.inductor: return '电感值 (亨利)'; default: return ''; } } } /// 属性数据结构 class PropertyData { String? refDesignator; // 位号 String? value; // 值 String? footprint; // 封装 String? netName; // 网络名 ComponentType componentType; // 元件类型 String? symbolName; // 符号名称 int rotation; // 旋转角度 (0, 90, 180, 270) bool mirrorX; // 水平镜像 bool mirrorY; // 垂直镜像 PropertyData({ this.refDesignator, this.value, this.footprint, this.netName, required this.componentType, this.symbolName, this.rotation = 0, this.mirrorX = false, this.mirrorY = false, }); PropertyData copy() { return PropertyData( refDesignator: refDesignator, value: value, footprint: footprint, netName: netName, componentType: componentType, symbolName: symbolName, rotation: rotation, mirrorX: mirrorX, mirrorY: mirrorY, ); } } /// 元件类型枚举 enum ComponentType { resistor, // 电阻 capacitor, // 电容 inductor, // 电感 diode, // 二极管 transistor, // 三极管 ic, // 集成电路 connector, // 连接器 other, // 其他 } /// 面板位置枚举 enum PropertyPanelPosition { bottom, // 底部弹出 side, // 侧边弹出 floating, // 浮动窗口 } /// 显示属性面板的辅助函数 Future showPropertyPanel( BuildContext context, { required PropertyData propertyData, Function(PropertyData)? onPropertyChanged, Function(PropertyData)? onPreview, }) { return showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => PropertyPanelWidget( propertyData: propertyData, onPropertyChanged: onPropertyChanged, onPreview: onPreview, ), ); }