import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'base_text_field.dart'; import '../../../l10n/app_localizations.dart'; /// 통화 입력 필드 위젯 /// 원화(KRW)와 달러(USD)를 지원하며 자동 포맷팅을 제공합니다. class CurrencyInputField extends StatefulWidget { final TextEditingController controller; final String currency; // 'KRW' or 'USD' final String? label; final String? hintText; final Function(double?)? onChanged; final String? Function(String?)? validator; final FocusNode? focusNode; final TextInputAction? textInputAction; final Function()? onEditingComplete; final bool enabled; const CurrencyInputField({ super.key, required this.controller, required this.currency, this.label, this.hintText, this.onChanged, this.validator, this.focusNode, this.textInputAction, this.onEditingComplete, this.enabled = true, }); @override State createState() => _CurrencyInputFieldState(); } class _CurrencyInputFieldState extends State { late FocusNode _focusNode; bool _isFormatted = false; @override void initState() { super.initState(); _focusNode = widget.focusNode ?? FocusNode(); _focusNode.addListener(_onFocusChanged); // 초기값이 있으면 포맷팅 적용 if (widget.controller.text.isNotEmpty) { final value = double.tryParse(widget.controller.text.replaceAll(',', '')); if (value != null) { widget.controller.text = _formatCurrency(value); _isFormatted = true; } } } @override void dispose() { if (widget.focusNode == null) { _focusNode.dispose(); } else { _focusNode.removeListener(_onFocusChanged); } super.dispose(); } void _onFocusChanged() { if (!_focusNode.hasFocus && widget.controller.text.isNotEmpty) { // 포커스를 잃었을 때 포맷팅 적용 final value = _parseValue(widget.controller.text); if (value != null) { setState(() { widget.controller.text = _formatCurrency(value); _isFormatted = true; }); } } else if (_focusNode.hasFocus && _isFormatted) { // 포커스를 받았을 때 포맷팅 제거 final value = _parseValue(widget.controller.text); if (value != null) { setState(() { if (widget.currency == 'KRW') { widget.controller.text = value.toInt().toString(); } else { widget.controller.text = value.toString(); } _isFormatted = false; }); // 커서를 끝으로 이동 widget.controller.selection = TextSelection.fromPosition( TextPosition(offset: widget.controller.text.length), ); } } } String _formatCurrency(double value) { if (widget.currency == 'KRW') { return NumberFormat.decimalPattern().format(value.toInt()); } else { return NumberFormat('#,##0.00').format(value); } } double? _parseValue(String text) { final cleanText = text .replaceAll(',', '') .replaceAll('₩', '') .replaceAll('\$', '') .trim(); return double.tryParse(cleanText); } String get _prefixText { return widget.currency == 'KRW' ? '₩ ' : '\$ '; } String _getDefaultHintText(BuildContext context) { return AppLocalizations.of(context).enterAmount; } @override Widget build(BuildContext context) { return BaseTextField( controller: widget.controller, focusNode: _focusNode, label: widget.label, hintText: widget.hintText ?? _getDefaultHintText(context), textInputAction: widget.textInputAction, keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow( widget.currency == 'KRW' ? RegExp(r'[0-9]') : RegExp(r'[0-9.]')), if (widget.currency == 'USD') // USD의 경우 소수점 이하 2자리까지만 허용 TextInputFormatter.withFunction((oldValue, newValue) { final text = newValue.text; if (text.isEmpty) return newValue; final parts = text.split('.'); if (parts.length > 2) { // 소수점이 2개 이상인 경우 거부 return oldValue; } if (parts.length == 2 && parts[1].length > 2) { // 소수점 이하가 2자리를 초과하는 경우 거부 return oldValue; } return newValue; }), ], prefixText: _prefixText, onEditingComplete: widget.onEditingComplete, enabled: widget.enabled, onChanged: (value) { final parsedValue = _parseValue(value); widget.onChanged?.call(parsedValue); }, validator: widget.validator ?? (value) { if (value == null || value.isEmpty) { return AppLocalizations.of(context).amountRequired; } final parsedValue = _parseValue(value); if (parsedValue == null || parsedValue <= 0) { return AppLocalizations.of(context).invalidAmount; } return null; }, ); } }