Flutter限定TextField输入内容

问题:

Android中EditText限定输入法录入的内容。可以使用它的InputType属性。
而在Flutter中,发现只有keyboardType来限定键盘格式,假如我们想录入数字,但使用第三方键盘时,仍然可以切换为字符键盘,并且可以录入字符。

分析:

TextField(
    ...
         controller: myController
         keyboardType: TextInputType.numberWithOptions(decimal: true),//设置键盘为可录入小数的数字
         inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],//设置只能录入数字[0-9]
)

通过网上查找了解到使用TextField =》inputFormatters属性来设置录入格式,而这个属性是:
List inputFormatters,

abstract class TextInputFormatter {
  /// Called when text is being typed or cut/copy/pasted in the [EditableText].
  ///
  /// You can override the resulting text based on the previous text value and
  /// the incoming new text value.
  ///
  /// When formatters are chained, `oldValue` reflects the initial value of
  /// [TextEditingValue] at the beginning of the chain.
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  );

  /// A shorthand to creating a custom [TextInputFormatter] which formats
  /// incoming text input changes with the given function.
  static TextInputFormatter withFunction(
    TextInputFormatFunction formatFunction,
  ) {
    return _SimpleTextInputFormatter(formatFunction);
  }
}

我们来看TextInputFormatter,有个formatEditUpdate方法,在你录入文字或剪切、复制、粘贴时,会调用这个方法,调用时机在当前TextField相应controller.addListener()之前。

//官方也指出可以继承这个方法,实现自定义效果。
//已经提供两种方式
 Concrete implementations [BlacklistingTextInputFormatter], which removes
 blacklisted characters upon edit commit, and
 [WhitelistingTextInputFormatter], which only allows entries of whitelisted
 characters, are provided.

BlacklistingTextInputFormatter黑名单式:BlacklistingTextInputFormatter.singleLineFormatter

WhitelistingTextInputFormatter白名单式:WhitelistingTextInputFormatter.digitsOnly
都是通过正则来匹配过滤

/// A [WhitelistingTextInputFormatter] that takes in digits `[0-9]` only.
  static final WhitelistingTextInputFormatter digitsOnly
      = WhitelistingTextInputFormatter(RegExp(r'\d+'));

预期的实现:

需实现:只能录入数字,并且录入的数字最多保留三位小数。

新建TestFormat继承TextInputFormatter,来自定义它的formatEditUpdate方法

class TestFormat extends TextInputFormatter{
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
   // 注意:改变newValue时还要加入对光标的控制。
    return null;
  }

}

为了方便使用,加入构造参数decimalRange——保留小数位,给默认值3

TestFormat({this.decimalRange = 3})
      : assert(decimalRange == null || decimalRange > 0);

  final int decimalRange;//小数点保留位数

通过对每次录入的newValue进行正则校验,留下我们需要的。完整代码如下:

class TestFormat extends TextInputFormatter {

  TestFormat({this.decimalRange = 3})
      : assert(decimalRange == null || decimalRange > 0);

  final int decimalRange;

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue,
      TextEditingValue newValue) {
    // 拿到录入后的字符
    String nValue = newValue.text;
    //当前所选择的文字区域
    TextSelection nSelection = newValue.selection;

    // 先来一波过滤,过滤出数字及小数点
    // 匹配包含数字和小数点的字符
    Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)');
    nValue = p.allMatches(nValue)
        .map((Match match) => match.group(0))
        .join();

    // 用匹配完的字符判断
    if (nValue.startsWith('.')) { //如果小数点开头,我们给他补个0
      nValue = '0.';
    } else if (nValue.contains('.')) {
      //来验证小数点位置
      if (nValue.substring(nValue.indexOf('.') + 1).length > decimalRange) {
        nValue = oldValue.text;
      } else {
        if (nValue.split('.').length > 2) { //多个小数点,去掉后面的
          List split = nValue.split('.');
          nValue = split[0] + '.' + split[1];
        }
      }
    }

    //使光标定位到最后一个字符后面
    nSelection = newValue.selection.copyWith(
      baseOffset: math.min(nValue.length, nValue.length + 1),
      extentOffset: math.min(nValue.length, nValue.length + 1),
    );

    return TextEditingValue(
        text: nValue,
        selection: nSelection,
        composing: TextRange.empty
    );
  }
}

这样就可以直接用了。

TextField(
    ...
         controller: myController
         keyboardType: TextInputType.numberWithOptions(decimal: true),
         inputFormatters: [TestFormat()],
)

效果如下

效果展示.gif

你可能感兴趣的:(Flutter限定TextField输入内容)