Flutter 如何实现金额输入框支持输入并格式化带美分的小数金额
问题根因定位
现有代码无法支持带美分的金额输入,核心是三个配置逻辑的错位:
- 输入拦截规则过严:
inputFormatters中配置的WhitelistingTextInputFormatter.digitsOnly会直接拦截小数点字符,从输入层就阻断了小数录入通道 - 格式化规则缺失小数占位:自定义类和初始化用到的
NumberFormat模板都是纯整数格式(#,##0/###,###,###),哪怕能录入小数,格式化时也会把小数部分全部舍去 - 格式化逻辑没有边界校验:现有自定义格式化类直接把输入文本整体转成double做格式化,没有处理小数点数量、小数位长度的限制,容易出现格式错乱、解析报错的问题
注:WhitelistingTextInputFormatter目前已被Flutter官方标记为废弃,推荐替换为FilteringTextInputFormatter
分步修正实现
1. 调整输入过滤规则
替换原有的纯数字拦截器,改为允许数字和单个小数点输入的过滤规则,从输入层放开小数录入权限:
// 仅允许数字和小数点,替换原有的digitsOnly规则 FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
2. 修正金额格式化模板
把原来的整数格式模板改成固定保留两位小数的美元格式,确保所有金额展示都带美分位:
// 规范的两位小数美元格式,注意locale为规范写法en_US而非小写en_us final money = NumberFormat('#,##0.00', 'en_US');
3. 重写自定义CurrencyFormat类
补全边界处理逻辑,解决小数点重复输入、小数位过长、解析报错的问题,修正后的完整类实现如下:
class CurrencyFormat extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { // 空输入直接返回 if (newValue.text.isEmpty) { return newValue; } // 限制小数点只能出现1个,多余的直接截断 final dotIndex = newValue.text.indexOf('.'); String processedText = newValue.text; if (dotIndex != -1) { // 截取第一个小数点前的内容 + 小数点 + 剔除后续重复的小数点 processedText = newValue.text.substring(0, dotIndex + 1) + newValue.text.substring(dotIndex + 1).replaceAll('.', ''); // 限制小数位最多2位 final decimalPart = processedText.split('.')[1]; if (decimalPart.length > 2) { processedText = processedText.substring(0, processedText.length - (decimalPart.length - 2)); } } // 异常值兜底,避免非数值输入导致崩溃 double value = 0; try { value = double.parse(processedText); } catch (e) { return oldValue; } // 用带两位小数的格式做千分位格式化 final money = NumberFormat('#,##0.00', 'en_US'); String newText = money.format(value); // 保持光标在文本末尾,符合输入交互习惯 return newValue.copyWith( text: newText, selection: TextSelection.collapsed(offset: newText.length), ); } }
4. 调整金额输入框的配置
替换原有的inputFormatters列表,同时配置支持小数点的数字键盘,确保拦截规则和自定义格式化逻辑配合生效:
TextFormField( keyboardType: const TextInputType.numberWithOptions(decimal: true), // 唤起带小数点的数字键盘 inputFormatters: [ // 替换原有的digitsOnly规则 FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')), CurrencyFormat() ], onSaved: (e) => payment = e, decoration: InputDecoration( labelText: 'AMOUNT', enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Color(0xff083663), )), border: OutlineInputBorder()), )
可选优化:通用支付场景输入体验
如果需要适配主流支付产品的输入逻辑(用户无需手动输入小数点,系统自动将输入的最后两位识别为美分,比如输入79424自动展示为794.24,输入123自动展示为1.23),可以把CurrencyFormat里的数值计算逻辑做调整:把输入的纯数字字符串转成整数后除以100,再走同样的格式化流程即可。
内容的提问来源于stack exchange,提问作者flutter run
相关产品推荐
相关产品推荐

