You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter TextField数字逗号格式化:小数输入与长度计数问题求助

Flutter TextField数字格式化解决方案

针对你遇到的两个核心问题:无法支持小数输入、逗号计入maxLength计数,以下是修改后的完整实现:

1. 改进的ThousandsFormatter(支持小数格式化)

import 'package:flutter/services.dart';
import 'package:intl/intl.dart';

class ThousandsFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    final rawText = newValue.text.replaceAll(',', '');
    
    // 空值直接返回
    if (rawText.isEmpty) {
      return const TextEditingValue(text: '');
    }

    // 拆分整数与小数部分,限制小数最多两位
    String integerPart;
    String decimalPart = '';
    if (rawText.contains('.')) {
      final parts = rawText.split('.');
      integerPart = parts[0];
      decimalPart = parts.length > 1 ? parts[1].substring(0, parts[1].length > 2 ? 2 : parts[1].length) : '';
    } else {
      integerPart = rawText;
    }

    // 格式化整数部分为千分位格式
    final formattedInteger = NumberFormat('#,##,##,###').format(int.tryParse(integerPart) ?? 0);
    
    // 拼接最终格式化文本
    final formattedText = decimalPart.isNotEmpty ? '$formattedInteger.$decimalPart' : formattedInteger;

    // 计算光标偏移位置,避免格式化后光标错位
    int selectionOffset = newValue.selection.end;
    if (rawText.contains('.') && newValue.selection.end > rawText.indexOf('.')) {
      // 光标在小数点后时,直接映射到格式化文本的对应位置
      selectionOffset = formattedText.length - (rawText.length - newValue.selection.end);
    } else {
      // 光标在整数部分时,根据新增的逗号数量调整偏移
      final commaCountAdded = formattedText.length - rawText.length;
      selectionOffset += commaCountAdded;
    }

    return TextEditingValue(
      text: formattedText,
      selection: TextSelection.collapsed(offset: selectionOffset.clamp(0, formattedText.length)),
    );
  }
}

2. 自定义长度限制Formatter(忽略逗号计数)

系统默认的LengthLimitingTextInputFormatter会把逗号算入长度配额,我们自定义一个只统计有效字符(数字+小数点)的限制器:

class CustomLengthLimitingFormatter extends TextInputFormatter {
  final int maxLength;

  CustomLengthLimitingFormatter(this.maxLength);

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    final rawText = newValue.text.replaceAll(',', '');
    if (rawText.length > maxLength) {
      // 截断超出长度的有效字符,再重新格式化
      final truncatedRaw = rawText.substring(0, maxLength);
      return ThousandsFormatter().formatEditUpdate(oldValue, TextEditingValue(text: truncatedRaw));
    }
    return newValue;
  }
}

3. TextField的正确配置

调整inputFormatters的顺序,确保先过滤非法字符、再限制长度、最后应用格式化:

TextField(
  onEditingComplete: () {
    FocusScope.of(context).requestFocus(_focusNodeY);
  },
  controller: principalController,
  decoration: const InputDecoration(
    labelText: "金额",
    isDense: true,
    counterText: "",     
  ),
  textAlign: TextAlign.start,
  maxLines: 1,
  keyboardType: const TextInputType.numberWithOptions(decimal: true),
  inputFormatters: [
    // 只允许输入数字和小数点
    FilteringTextInputFormatter.allow(RegExp(r'^[0-9]+(\.[0-9]{0,2})?$')),
    // 限制有效字符长度(比如支持8位整数+2位小数,这里设为10)
    CustomLengthLimitingFormatter(10),
    // 最后应用千分位格式化
    ThousandsFormatter(),
  ],
)

关键说明

  • 小数处理:拆分整数与小数部分分别处理,自动截断超出两位的小数,输入过程中实时保持格式
  • 光标定位:针对小数点前后的光标位置做了适配,避免格式化后光标跳至错误位置
  • 长度限制:自定义的长度限制器仅统计数字和小数点的长度,逗号不占用maxLength配额,无需因格式符号额外调整长度值

内容的提问来源于stack exchange,提问作者ocean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:10:19