// 画笔面板 -- 底部抽屉 // 提供画笔类型/粗细/颜色/透明度设置 // 遵循 StickerPickerSheet 底部面板模式 import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; import '../bloc/editor_bloc.dart'; import 'stroke_model.dart'; /// 画笔面板 -- 底部抽屉 class BrushPanel extends StatelessWidget { final BrushType activeBrushType; final String activeColor; final double activeWidth; final double activeOpacity; final void Function(BrushType type) onBrushTypeChanged; final void Function(String color) onColorChanged; final void Function(double width) onWidthChanged; final void Function(double opacity) onOpacityChanged; const BrushPanel({ super.key, required this.activeBrushType, required this.activeColor, required this.activeWidth, required this.activeOpacity, required this.onBrushTypeChanged, required this.onColorChanged, required this.onWidthChanged, required this.onOpacityChanged, }); static const _brushTypes = [ (BrushType.pen, '钢笔', Icons.gesture_rounded), (BrushType.pencil, '铅笔', Icons.edit_rounded), (BrushType.marker, '马克笔', Icons.brush_rounded), (BrushType.eraser, '橡皮', Icons.auto_fix_high_rounded), ]; static const _colors = [ '#2D2420', '#E07A5F', '#81B29A', '#F2CC8F', '#D4A5A5', '#42A5F5', '#9C27B0', '#FFFFFF', ]; @override Widget build(BuildContext context) { return Container( height: 280, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.vertical(top: Radius.circular(22)), ), child: Column( children: [ // 拖拽指示条 Padding( padding: const EdgeInsets.only(top: 12, bottom: 8), child: Container( width: 36, height: 4, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), ), // 画笔类型行 _buildBrushTypeRow(context), // 粗细滑块 _buildSizeSlider(context), // 颜色行 _buildColorRow(context), // 透明度滑块(仅马克笔) if (activeBrushType == BrushType.marker) _buildOpacitySlider(context), ], ), ); } Widget _buildBrushTypeRow(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: _brushTypes.map((bt) { final isActive = activeBrushType == bt.$1; return GestureDetector( onTap: () => onBrushTypeChanged(bt.$1), child: Container( width: 64, height: 52, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: isActive ? Border.all(color: AppColors.accent, width: 2) : Border.all(color: Colors.transparent), color: isActive ? AppColors.accent.withValues(alpha: 0.1) : null, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( bt.$3, size: 20, color: isActive ? AppColors.accent : Colors.grey[600], ), const SizedBox(height: 2), Text( bt.$2, style: TextStyle( fontSize: 10, color: isActive ? AppColors.accent : Colors.grey[600], fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, ), ), ], ), ), ); }).toList(), ), ); } Widget _buildSizeSlider(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), child: Row( children: [ Text('粗细', style: TextStyle(fontSize: 12, color: Colors.grey[600])), Expanded( child: Slider( value: activeWidth, min: 1, max: 20, divisions: 19, activeColor: AppColors.accent, label: activeWidth.round().toString(), onChanged: onWidthChanged, ), ), Text( activeWidth.round().toString(), style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ], ), ); } Widget _buildColorRow(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: _colors.map((c) { final isActive = activeColor == c; final color = _parseHexColor(c); return GestureDetector( onTap: () => onColorChanged(c), child: Container( width: 24, height: 24, decoration: BoxDecoration( shape: BoxShape.circle, color: color, border: isActive ? Border.all(color: AppColors.accent, width: 2) : (c == '#FFFFFF' ? Border.all(color: Colors.grey[300]!) : null), ), ), ); }).toList(), ), ); } Widget _buildOpacitySlider(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), child: Row( children: [ Text('透明度', style: TextStyle(fontSize: 12, color: Colors.grey[600])), Expanded( child: Slider( value: activeOpacity, min: 0.1, max: 1.0, activeColor: AppColors.accent, onChanged: onOpacityChanged, ), ), Text( '${(activeOpacity * 100).round()}%', style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ], ), ); } Color _parseHexColor(String hex) { final code = hex.replaceFirst('#', ''); return Color(int.parse('FF$code', radix: 16)); } }