问题:
Android中EditText限定输入法录入的内容。可以使用它的InputType属性。
而在Flutter中,发现只有keyboardType来限定键盘格式,假如我们想录入数字,但使用第三方键盘时,仍然可以切换为字符键盘,并且可以录入字符。
分析:
TextField(
...
controller: myController
keyboardType: TextInputType.numberWithOptions(decimal: true),//设置键盘为可录入小数的数字
inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],//设置只能录入数字[0-9]
)
通过网上查找了解到使用TextField =》inputFormatters属性来设置录入格式,而这个属性是:
List
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