diff --git a/app/lib/features/editor/widgets/text_input_overlay.dart b/app/lib/features/editor/widgets/text_input_overlay.dart new file mode 100644 index 0000000..cef7b0c --- /dev/null +++ b/app/lib/features/editor/widgets/text_input_overlay.dart @@ -0,0 +1,205 @@ +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)); + } +}