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
相关产品推荐
相关产品推荐

