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

Flutter中如何在用户输入时实时将TextField的双精度数值按印度次大陆编号格式格式化?

Flutter中如何在用户输入时实时将TextField的双精度数值按印度次大陆编号格式格式化?

我来帮你解决这个问题!你遇到的输入到第五位就显示0的问题,主要是因为实时格式化时没处理好光标位置,而且直接用intl的默认印度格式可能和你的需求不完全匹配。下面是完整的解决方案:

问题根源分析

你之前的代码大概率是直接用NumberFormat格式化后就更新输入框文本,但没维护光标位置——输入过程中光标会跳转到错误位置,导致输入的字符被错误替换,就出现了显示0的异常。另外,我们需要完全贴合你要的印度次大陆编号规则:从右往左先取3位,之后每2位加逗号,剩余的前缀部分(crore及以上)直接保留,不再加逗号。

完整实现方案

我们自定义格式化逻辑,同时在输入监听里准确维护光标位置,保证输入流畅:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('印度格式数字输入')),
        body: const IndianNumberInput(),
      ),
    );
  }
}

class IndianNumberInput extends StatefulWidget {
  const IndianNumberInput({super.key});

  @override
  State<IndianNumberInput> createState() => _IndianNumberInputState();
}

class _IndianNumberInputState extends State<IndianNumberInput> {
  final TextEditingController _controller = TextEditingController();
  int? _previousCursorPosition;

  @override
  void initState() {
    super.initState();
    // 添加输入监听,实时格式化
    _controller.addListener(_formatInput);
  }

  @override
  void dispose() {
    _controller.removeListener(_formatInput);
    _controller.dispose();
    super.dispose();
  }

  void _formatInput() {
    String currentText = _controller.text;
    int cursorPosition = _controller.selection.base.offset;

    // 初始化记录光标位置
    if (_previousCursorPosition == null) {
      _previousCursorPosition = cursorPosition;
      return;
    }

    // 避免重复格式化循环
    String formattedText = formatIndianNumber(currentText);
    if (currentText == formattedText) {
      _previousCursorPosition = cursorPosition;
      return;
    }

    // 计算光标偏移量:根据新增/删除的逗号数量调整
    int commaCountCurrent = currentText.split(',').length - 1;
    int commaCountFormatted = formattedText.split(',').length - 1;
    int deltaCommas = commaCountFormatted - commaCountCurrent;

    // 根据输入/删除操作,计算新光标位置
    int newCursorPosition;
    if (cursorPosition > _previousCursorPosition!) {
      // 用户正在输入字符,光标位置加上逗号增量
      newCursorPosition = cursorPosition + deltaCommas;
    } else {
      // 用户正在删除字符,光标位置减去逗号增量
      newCursorPosition = cursorPosition - deltaCommas;
    }

    // 确保光标位置在合法范围内
    newCursorPosition = newCursorPosition.clamp(0, formattedText.length);

    // 更新输入框文本和光标位置
    _controller.value = TextEditingValue(
      text: formattedText,
      selection: TextSelection.collapsed(offset: newCursorPosition),
    );

    _previousCursorPosition = newCursorPosition;
  }

  // 自定义印度次大陆数字格式化函数
  String formatIndianNumber(String input) {
    // 清理输入:只保留数字和小数点
    String cleaned = input.replaceAll(RegExp(r'[^0-9.]'), '');
    
    // 分割整数和小数部分
    List<String> parts = cleaned.split('.');
    String integerPart = parts[0];
    String decimalPart = parts.length > 1 ? '.${parts[1]}' : '';
    
    if (integerPart.isEmpty) {
      return decimalPart.isNotEmpty ? decimalPart : '';
    }
    
    // 数字长度<=3时直接返回
    if (integerPart.length <= 3) {
      return '$integerPart$decimalPart';
    }
    
    // 反转整数部分,方便从右往左分组
    String reversed = integerPart.split('').reversed.join();
    List<String> chunks = [];
    
    // 先取最后3位
    chunks.add(reversed.substring(0, 3));
    reversed = reversed.substring(3);
    
    // 剩余部分每2位一组
    while (reversed.isNotEmpty) {
      int take = reversed.length >= 2 ? 2 : reversed.length;
      chunks.add(reversed.substring(0, take));
      reversed = reversed.substring(take);
    }
    
    // 反转每组并拼接成最终格式
    String formattedInteger = chunks
        .map((chunk) => chunk.split('').reversed.join())
        .toList()
        .reversed
        .join(',');
    
    return '$formattedInteger$decimalPart';
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: TextField(
        controller: _controller,
        keyboardType: TextInputType.numberWithOptions(decimal: true),
        decoration: const InputDecoration(
          labelText: '输入数字',
          border: OutlineInputBorder(),
        ),
      ),
    );
  }
}

效果测试

  • 输入123456789012.1234会自动格式化为12345,67,89,012.1234,完全符合你的要求
  • 实时输入过程:输入123→123,输入4→1,234,输入5→12,345,输入6→1,23,456,不会出现显示0的错误

备注:内容来源于stack exchange,提问作者rusty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 10:08:08