TextField 是一个文本输入组件,一开始做登录页就用到了。但是,TextField 有一些坑官方还没有填,那么就只能通过一些俏皮的方案来解决了。当然,如果够强的话,也是可以写一个新的 widget 控件的 ~
1、限定了可输入长度,超过长度后的内容虽然不展示出来,但是点击删除键时,还是要删除已输入但是看不见的那部分
2、限定了输入类型(比如数字),虽然会弹出相应类型的键盘(比如数字键盘),但是还是能输入其他类型(比如字母、汉字)
以上两种坑合并解决方案如下,当然,如果想自己画一个数字键盘,也是可以解决输入类型问题 ~
主要是在 TextField 输入后回调 onChanged 方法时,对输入内容进行判断,如果符合判断条件就采用并缓存下来,如果不符合,就通过 TextEditingController 进行“回退”:
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class WidgetTestPage extends StatefulWidget {
WidgetTestPage({Key key, this.title}) : super(key: key);
final String title;
@override
State<StatefulWidget> createState() => _WidgetTestPageState();
}
class _WidgetTestPageState extends State<WidgetTestPage> {
TextEditingController _textFieldController = new TextEditingController();
String _phone = "";
bool _isNumberAndCount(String str) {
return str.length <= 11 && new RegExp("^[0-9][0-9]*\$").hasMatch(str);
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: TextField(
keyboardType: TextInputType.number,
controller: _textFieldController,
decoration: InputDecoration(
hintText: "请输入手机号码",
hintStyle: TextStyle(color: Color(0xffbfbfbf), fontSize: 16),
counterText: "",
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xffebebeb), width: 1),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xffebebeb), width: 1),
),
),
onChanged: (v) {
if (_isNumberAndCount(v) || v == "") {
setState(() {
_phone = v;
});
} else {
_textFieldController.text = _phone;
_textFieldController.selection = TextSelection.fromPosition(
// 保持光标在最后
TextPosition(
affinity: TextAffinity.downstream, offset: _phone.length),
);
}
},
),
);
}
@override
void dispose() {
super.dispose();
}
}
抽取成一个 UI Widget:
NumberTextField(
limitCount: 11,
controller: TextEditingController(),
decoration: InputDecoration(
hintText: "请输入手机号码",
hintStyle: TextStyle(color: Color(0xffbfbfbf), fontSize: 16),
counterText: "",
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xffebebeb), width: 1),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xffebebeb), width: 1),
),
),
onChanged: (v) {
... ...
},
),
import 'package:flutter/material.dart';
/// 自定义数字输入框(字母)
/// [limitCount] 限制可输入长度
/// [canInputLetter] 能否输入英文字母
class NumberTextField extends StatefulWidget {
final int limitCount;
final bool canInputLetter;
final TextStyle style;
final TextEditingController controller;
final InputDecoration decoration;
final ValueChanged<String> onChanged;
NumberTextField({
Key key,
this.limitCount = 0,
this.canInputLetter = false,
this.style,
this.controller,
this.decoration = const InputDecoration(),
this.onChanged,
}) : assert(controller != null),
super(key: key);
@override
State<StatefulWidget> createState() => _NumberTextFieldState(limitCount, canInputLetter, controller);
}
class _NumberTextFieldState extends State<NumberTextField> {
final int _limitCount;
final bool _canInputLetter;
final TextEditingController _textFieldController;
String _number = '';
_NumberTextFieldState(this._limitCount, this._canInputLetter, this._textFieldController);
bool _isNumberAndCount(String str) {
return (_limitCount <= 0 || str.length <= _limitCount) &&
(_canInputLetter ? RegExp("^[a-z0-9A-Z]*\$").hasMatch(str) : RegExp("^[0-9]*\$").hasMatch(str));
}
@override
Widget build(BuildContext context) {
return TextField(
style: widget.style,
keyboardType: TextInputType.number,
controller: _textFieldController,
decoration: widget.decoration,
/*inputFormatters: [FilteringTextInputFormatter.deny(
// 过滤表情包,好像_isNumberAndCount已经过滤了,不行才放开
RegExp("[^\\u0020-\\u007E\\u00A0-\\u00BE\\u2E80-\\uA4CF\\uF900-\\uFAFF\\uFE30-\\uFE4F\\uFF00-\\uFFEF\\u0080-\\u009F\\u2000-\\u201f\r\n]"))],*/
onChanged: (v) {
if (_isNumberAndCount(v) || v == "") {
setState(() {
_number = v;
});
if (widget.onChanged != null) {
widget.onChanged(v);
}
} else {
_textFieldController.text = _number;
_textFieldController.selection = TextSelection.fromPosition(
// 保持光标在最后
TextPosition(affinity: TextAffinity.downstream, offset: _number.length),
);
}
},
);
}
@override
void dispose() {
_textFieldController.dispose();
super.dispose();
}
}