Flutter如何实现类似PayPal金额输入的TextField格式化效果
Flutter 定制占位填充式金额输入框实现方案
直接通过自定义TextEditingController实现即可,不需要依赖第三方包,可控性最高,能完美对齐PayPal输入交互、支持光标移动导航。
核心实现逻辑
- 固定展示模板为
--,--,逗号位置固定不随输入改动,-作为占位符,输入数字从右往左逐位替换占位符,首次输入数字1时即展示为--,-1,和需求完全匹配 - 所有文本更新、光标位置计算、输入校验逻辑全部封装在控制器内部,避免外部刷新导致的光标跳位问题
- 仅允许数字输入,退格时自动将对应位置还原为占位符,禁止修改逗号位置
- 控制器对外直接输出整数类型的实际输入值,不需要额外解析展示文本
具体实现代码
1. 自定义输入控制器
class PlaceholderAmountController extends TextEditingController { // 固定展示模板,可按需修改长度适配更大金额输入 static const String _template = '--,--'; static const int _maxInputLength = 4; int _realValue = 0; // 对外获取实际输入的整数值 int get realInputValue => _realValue; @override set value(TextEditingValue newValue) { // 初始化默认值 if (text.isEmpty && newValue.text == _template) { super.value = newValue.copyWith( text: _template, selection: const TextSelection.collapsed(offset: _template.length), ); return; } // 提取纯数字内容 final int oldDigitCount = text.replaceAll(RegExp(r'[^0-9]'), '').length; final int newDigitCount = newValue.text.replaceAll(RegExp(r'[^0-9]'), '').length; String rawDigits = newValue.text.replaceAll(RegExp(r'[^0-9]'), ''); // 处理退格场景:删除最后一位有效数字 if (newDigitCount < oldDigitCount) { rawDigits = rawDigits.isEmpty ? '' : rawDigits.substring(0, rawDigits.length - 1); } // 超出最大长度截断 if (rawDigits.length > _maxInputLength) { rawDigits = rawDigits.substring(rawDigits.length - _maxInputLength); } _realValue = rawDigits.isEmpty ? 0 : int.parse(rawDigits); // 从右往左逐位替换占位符生成展示文本 String displayText = _template; int digitIndex = rawDigits.length - 1; for (int i = _template.length - 1; i >= 0; i--) { if (_template[i] == ',') continue; if (digitIndex >= 0) { displayText = displayText.replaceRange(i, i+1, rawDigits[digitIndex]); digitIndex--; } else { break; } } // 计算光标位置,支持用户手动移动光标后的输入适配 int cursorOffset = displayText.length; for (int i = 0; i < displayText.length; i++) { if (displayText[i] == '-') { // 优先保留用户手动选择的光标位置,自动跳过逗号位置 if (newValue.selection.baseOffset < i) { cursorOffset = newValue.selection.baseOffset; if (cursorOffset == 2) cursorOffset = 1; } else { cursorOffset = i; } break; } } // 输入满额时光标自动定位到末尾 if (rawDigits.length == _maxInputLength) { cursorOffset = displayText.length; } super.value = TextEditingValue( text: displayText, selection: TextSelection.collapsed(offset: cursorOffset), composing: TextRange.empty, ); } }
2. 组件接入配置
// 初始化控制器 final amountController = PlaceholderAmountController()..text = '--,--'; TextField( controller: amountController, keyboardType: TextInputType.number, enableInteractiveSelection: true, // 开启光标移动选择能力 showCursor: true, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[0-9,-]')), ], style: const TextStyle(fontSize: 26, letterSpacing: 3), decoration: const InputDecoration( border: InputBorder.none, prefixText: '¥ ', // 可按需配置货币符号对齐PayPal样式 prefixStyle: TextStyle(fontSize: 26), ), )
交互适配说明
- 光标移动:控制器自动做了位置校准,用户手动拖动光标到任意可输入位置时,输入数字会直接替换对应位置的占位符,不会出现光标跳位
- 退格逻辑:无论光标在哪个位置,按退格键只会删除光标左侧最近的一位有效数字,对应位置自动还原为
-占位符,符合常规输入习惯 - 扩展修改:需要支持更长金额输入时,只需修改
_template和_maxInputLength即可;需要支持小数的场景,在模板中增加小数点位置、控制器内补充跳过逻辑即可快速适配
内容的提问来源于stack exchange,提问作者Colin Schneider
相关产品推荐
相关产品推荐

