import 'package:flutter/material.dart'; /// 编辑器文字输入覆盖层 /// 当用户选择文字工具时,在画布上叠加一个 TextField class TextInputOverlay extends StatefulWidget { final void Function(String text, double fontSize, String fontColor) onConfirmed; final VoidCallback onCancelled; const TextInputOverlay({ super.key, required this.onConfirmed, required this.onCancelled, }); @override State createState() => _TextInputOverlayState(); } class _TextInputOverlayState extends State { final _controller = TextEditingController(); final _focusNode = FocusNode(); double _fontSize = 18.0; String _fontColor = '#2D2420'; // 字号选项:小(14)/中(18)/大(24) static const _fontSizes = [14.0, 18.0, 24.0]; static const _fontSizeLabels = ['小', '中', '大']; // 颜色选项 static const _colors = [ '#2D2420', // 主文字色 '#E07A5F', // 珊瑚色 '#81B29A', // 鼠尾草绿 '#2C7DA0', // 蓝色 '#D4A5A5', // 玫瑰粉 '#F2CC8F', // 暖金 '#9B5DE5', // 紫色 '#F15BB5', // 粉色 ]; @override void initState() { super.initState(); // 自动弹出键盘 Future.microtask(() => _focusNode.requestFocus()); } @override void dispose() { _controller.dispose(); _focusNode.dispose(); super.dispose(); } void _confirm() { final text = _controller.text.trim(); if (text.isEmpty) { widget.onCancelled(); return; } widget.onConfirmed(text, _fontSize, _fontColor); } @override Widget build(BuildContext context) { return Container( color: Colors.black26, child: Center( child: Container( width: MediaQuery.of(context).size.width * 0.85, constraints: const BoxConstraints(maxHeight: 280), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.15), blurRadius: 20, offset: const Offset(0, 4), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 标题行 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '添加文字', style: Theme.of(context).textTheme.titleSmall, ), IconButton( icon: const Icon(Icons.close, size: 20), onPressed: widget.onCancelled, padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ], ), const SizedBox(height: 12), // 文字输入框 TextField( controller: _controller, focusNode: _focusNode, maxLines: 4, minLines: 1, textInputAction: TextInputAction.done, onSubmitted: (_) => _confirm(), style: TextStyle(fontSize: _fontSize), decoration: InputDecoration( hintText: '在这里输入文字...', border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( color: Theme.of(context).colorScheme.primary, width: 2, ), ), ), ), const SizedBox(height: 12), // 字号选择 Row( children: [ Text('字号', style: Theme.of(context).textTheme.bodySmall), const SizedBox(width: 8), ...List.generate(_fontSizes.length, (i) { final selected = _fontSize == _fontSizes[i]; return Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(_fontSizeLabels[i]), selected: selected, onSelected: (_) { setState(() => _fontSize = _fontSizes[i]); }, visualDensity: VisualDensity.compact, ), ); }), ], ), const SizedBox(height: 8), // 颜色选择 Row( children: [ Text('颜色', style: Theme.of(context).textTheme.bodySmall), const SizedBox(width: 8), ..._colors.map((hex) { final selected = _fontColor == hex; final color = _parseHexColor(hex); return GestureDetector( onTap: () => setState(() => _fontColor = hex), child: Container( width: 28, height: 28, margin: const EdgeInsets.only(right: 6), decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: selected ? Border.all(color: Colors.black87, width: 2.5) : Border.all( color: Colors.grey.shade300, width: 1), ), ), ); }), ], ), const SizedBox(height: 12), // 确认按钮 SizedBox( width: double.infinity, height: 44, child: FilledButton( onPressed: _confirm, child: const Text('添加到日记'), ), ), ], ), ), ), ); } Color _parseHexColor(String hex) { final code = hex.replaceAll('#', ''); return Color(int.parse('FF$code', radix: 16)); } }