现在我们有个需求,要求输入完成后所有字母都转大写,看起来很容易,直接写个TextField,然后在onEditingComplete里处理不就行了,但是,你会发现,键盘里还有个下拉按钮,就上面那个按钮,点击它虽然键盘消失了,但根本不执行onEditingComplete,只有点击下面那个按钮才会执行,这样测试肯定不通过,那就在onChange方法里处理?也不行,因为我输入完成后还得访问后台接口,所以不能在onChange方法里处理。
背景讲完了,上代码:
1,使用扩展类extension...on...,扩展下TextField,混入class WidgetsBindingObserver来监听界面的变化,具体监听尺寸变化的方法是didChangeMetrics()
extension TextFieldExtension on TextField{
Widget getKeyboardListenerTextField(){
if(focusNode == null) return this;
return KeyboardListenerTextField(child: this);
}
}
class KeyboardListenerTextField extends StatefulWidget {
TextField child;
KeyboardListenerTextField({
this.child,
});
@override
_KeyboardListenerTextFieldState createState() => _KeyboardListenerTextFieldState();
}
class _KeyboardListenerTextFieldState extends State<KeyboardListenerTextField> with WidgetsBindingObserver {
//标记键盘是否现实中,默认不显示
bool isKeyboardActived = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
widget.child.focusNode.unfocus();
}
@override
void didChangeMetrics() {
super.didChangeMetrics();
WidgetsBinding.instance.addPostFrameCallback((_) {
// 当前是安卓系统并且在焦点聚焦的情况下
if (Platform.isAndroid && widget.child.focusNode.hasFocus) {
if (isKeyboardActived) {
isKeyboardActived = false;
// 使输入框失去焦点
widget.child.focusNode.unfocus();
return;
}
isKeyboardActived = true;
} else {
isKeyboardActived = false;
}
});
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
2,在使用时直接
TextField(
focusNode: focusNode,
onEditingComplete: () {
focusNode?.unfocus();
},
onSubmitted: (text) {
focusNode?.unfocus();
},
).getKeyboardListenerTextField();
3,当然,需要监听焦点
focusNode.addListener(() {
if (!focusNode.hasFocus) {
//失去焦点,说明输入完成,该转大写的转大写,该数据请求的数据请求
}
});
最后:这样写也有缺点,因为如果一个界面TextField很多,如果焦点直接从这个TextField到另一个TextField,那第二个 TextField当键盘隐藏时因为键盘状态未变化,所以不会失去焦点,所以这只适合界面有一个TextField的情况。
最后的最后:如果界面就是很多TextField,那可以给整个界面混入WidgetsBindingObserver,监听整个界面的尺寸,设置监听时酱紫写:
double oldBottom = 0;
@override
void didChangeMetrics() {
super.didChangeMetrics();
WidgetsBinding.instance.addPostFrameCallback((_) {
var currentBottom = MediaQuery.of(context).viewInsets.bottom;
// 如果currentBottom为0,表示键盘消失了,这时候整个界面获取焦点,那TextField自然就失去焦点了
// 为什么还要判断oldBottom,因为在界面没变化的时候,个别机型也会调用这个方法。。。
// 所以必须在oldBottom不为0的时候,才获取焦点,这里坑的我好惨。。。
if(oldBottom != 0 && currentBottom==0){
FocusScope.of(context).requestFocus(FocusNode());
}
oldBottom = currentBottom;
});
}