监听键盘弹出是很常见的需求,但是键盘的状态并不由 Flutter 来控制,所以 Flutter 没有提供直接的方法来获取键盘的状态,不过我们可以通过其他方式来实现。
WidgetsBindingObserver
WidgetsBindingObserver
是一个比较常用的 mixin
类,通过名字就可以看出,这个类主要是用于监听 Widget 的各种状态,而键盘弹出会导致应用页面发生变化,所以我们可以使用 didChangeMetrics
这个方法来监听底部高度的变化。
使用实例
首先定义一个枚举类,表示键盘打开的状态。
enum KeyboardState { unknown, opening, closing, closed }
之后定义监听的逻辑,一般情况下都会选择监听 MediaQuery.viewInsetsOf(context).bottom
来计算底部的距离,因为底部的距离正好是键盘的高度,但是键盘的高度是一个变化的过程,并且就算是同一个键盘,最大高度也有可能发生变化,所以说无法知道键盘的最大高度。最好的方法是,监听高度的变化过程,如果是递增变化,那么就是正在打开的状态,如果是降序变化,那么就是正在关闭的状态,当底部距离变为 0 并且同时不是已经关闭的状态,那么就是关闭了。之所以不能单纯判断底部距离为 0 就是关闭状态是因为前面提到,键盘的高度是一个变化的过程,在键盘打开的前几帧,获取到的高度也可能是 0 。
有了逻辑就很容易了,在需要监听的类中混入 WidgetsBindingObserver
后重写 didChangeMetrics
方法。
List<double> heightList = [];
@override
void didChangeMetrics() {
WidgetsBinding.instance.addPostFrameCallback((_) {
var height = MediaQuery.viewInsetsOf(Get.context!).bottom;
if (heightList.isNotEmpty && height != heightList.last) {
if (height > heightList.last && state.keyboardState != KeyboardState.opening) {
state.keyboardState = KeyboardState.opening;
//正在打开
} else if (height < heightList.last && state.keyboardState != KeyboardState.closing) {
state.keyboardState = KeyboardState.closing;
//正在关闭
}
}
// 只在高度变化时记录高度
if (heightList.isEmpty || height != heightList.last) {
heightList.add(height);
}
// 当高度为0且键盘经历了开启关闭过程时,认为键盘已完全关闭
if (height == 0 && state.keyboardState != KeyboardState.closed) {
state.keyboardState = KeyboardState.closed;
heightList.clear();
//已经关闭
}
});
super.didChangeMetrics();
}
我这里使用了 getx
获取 context
。注意不要忘记了 WidgetsBinding.instance.addObserver(this)
以及及时销毁 WidgetsBinding.instance.removeObserver(this)
。