You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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 of int.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

火山引擎 最新活动