Flutter中扩展TextInputFormatter实现数值范围限制及一位小数格式化
How to Modify CustomRangeTextInputFormatter to Support One Decimal Place & Keep Range Restrictions
Got it, let's fix this formatter so it supports one decimal place while keeping the 0 to maxValue range check. Your current code uses int.parse() which is why it can't handle decimals—let's adjust that to work with doubles, plus add logic to enforce the one-decimal rule.
First, let's outline the problems with your existing code:
- It relies on
int.parse(), which breaks as soon as a decimal point is entered - No logic to limit decimal inputs to just one digit after the point
- Doesn't handle edge cases like trailing decimals (e.g., "10.") or multiple decimal points
Here's the updated formatter with all the required functionality:
class CustomRangeTextInputFormatter extends TextInputFormatter { final double maxValue; CustomRangeTextInputFormatter({required this.maxValue}); @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue, ) { final newText = newValue.text; // Handle empty input if (newText.isEmpty) { return const TextEditingValue(); } // Block negative numbers entirely if (newText.startsWith('-')) { return const TextEditingValue(text: '0'); } // Enforce one decimal place maximum if (newText.contains('.')) { final parts = newText.split('.'); // Reject if more than one decimal point, or more than one digit after the decimal if (parts.length > 2 || parts[1].length > 1) { return oldValue; } // Handle trailing decimal (e.g., "5.") by treating it as "5.0" for validation final tempValue = double.tryParse(parts[1].isEmpty ? '${parts[0]}.0' : newText) ?? 0.0; return _validateRange(tempValue, newValue); } else { // Handle integer inputs final tempValue = double.tryParse(newText) ?? 0.0; return _validateRange(tempValue, newValue); } } // Helper to clean up range validation logic TextEditingValue _validateRange(double value, TextEditingValue newValue) { if (value < 0) { return const TextEditingValue(text: '0'); } if (value > maxValue) { // Format maxValue to always show one decimal place (e.g., 100 becomes "100.0") final formattedMax = maxValue.toStringAsFixed(1); return TextEditingValue(text: formattedMax); } return newValue; } }
Key Changes Explained:
- Empty & Negative Handling: Blocks negative inputs immediately by checking for a leading '-', and handles empty input the same way as before.
- Decimal Enforcement: Splits the input on '.' to ensure only one decimal exists, and caps digits after the decimal at one. If the user tries to type more, we revert to the last valid input.
- Safe Double Conversion: Uses
double.tryParse()instead ofint.parse()to handle both integers and decimals, and gracefully handles edge cases like trailing decimals (e.g., "7.") by treating it as "7.0" for validation. - Clean Range Check: Moved range validation to a helper method to keep the code organized, and formats the maxValue to always show one decimal place for consistency with user input.
Bonus: Smoother Input Experience
Add an optional FilteringTextInputFormatter to block non-numeric characters upfront, making the formatter work even better:
TextField( keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,1}')), // Blocks non-numeric input CustomRangeTextInputFormatter(maxValue: 99.9), ], )
内容的提问来源于stack exchange,提问作者Leonidas Yopan




