Flutter关于软键盘的一些问题
Scaffold
类有个resizeToAvoidBottomInset
属性,它的作用是当弹出软键盘的时候,可以自动调节body区域的高度,撑起body的内容,使其底部高度刚好为键盘的高度,这样一来就不至于让键盘覆盖内容。
Scaffold(
/// .....
/// 当弹出软键盘的时候,是否自动调节body区域,默认为true
resizeToAvoidBottomInset: true,
/// .....
)
resizeToAvoidBottomInset
默认值为true,自动调节body的高度,设置为false则停止这一行为。当然resizeToAvoidBottomInset
为ture,自动调节高度也是需要条件的:
1、body区域不能为一个滚动区域。
如使用了SingleChildScrollView
和ListView
包裹,它就会失去自动调节的功能,道理很简单,因为一个非固定高度的组件它无法自动计算高度。
2、需要存在TextField组件
一般来说当存在TextField
组件,才会出现软键盘,resizeToAvoidBottomInset
才会有作用。
body自动调节高度会存在一些组件重叠问题,引起界面错乱。因为body的高度改变了,如果body中的组件依赖于高度,就会产生畸变。所以为了避免这个问题你可以选择将resizeToAvoidBottomInset
设置为false,只是软键盘遮挡问题仍然存在。当然,还是有解决的方法的:
这个时候你可以通过判断键盘是否弹起,给body的底部增加一个SizedBox高度,就能解决这个问题。但,这里存在三个技术问题:
1、我要怎么判断键盘是否弹起?
继承WidgetsBindingObserver
类,重写didChangeMetrics
方法,该方法可用于检测页面的变化,软键盘的弹出和收起都会触发应该事件。然后通过判断MediaQuery.of(context).viewInsets.bottom
的值来确定键盘是否弹起。当它的值大于0时,说明键盘弹起,反之为收起。
class _KeyboardDemoState extends State<KeyboardDemo>
with WidgetsBindingObserver {
// 键盘是否打开
bool isKeyboardOpen = false;
@override
void initState() {
super.initState();
// 注册监听
WidgetsBinding.instance?.addObserver(this);
}
@override
void dispose() {
super.dispose();
// 移除监听
WidgetsBinding.instance?.removeObserver(this);
}
/// 页面尺寸改变时回调
@override
didChangeMetrics() {
super.didChangeMetrics();
// 在页面重新渲染完成之后,通过MediaQuery.of(context).viewInsets.bottom获取软键盘高度
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
isKeyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0;
});
}
/// .....
}
2、我要怎么获取当前键盘的高度?
答案已经在上面说了,MediaQuery.of(context).viewInsets.bottom
就是获取当前软键盘的高度。
3、我要怎么关闭软键盘?
可能有人会说,这是个问题吗,直接点击软键盘上的三角图标不就行了。问出这个问题的人,估计是没有考虑过ios系统。
在iOS中,系统默认的软键盘是没有收起的按钮的。这个时候我们就要想个办法来处理软键盘收起问题。一般来说我们都会在键盘以外的区域增加一个单击事件,然后使用FocusManager.instance.primaryFocus?.unfocus()
将键盘关闭。
GestureDetector(
/// 点击内容包含透明区域
behavior: HitTestBehavior.opaque,
onTap: () {
/// 取消当前软键盘焦点并收起
FocusManager.instance.primaryFocus?.unfocus();
}
}
那么,单击事件使用什么组件呢?这里还是有些讲究的,可以使用InkWell
或者GestureDetector
的onTap
事件 ,但前者点击时会产生水波纹的效果 ,后者要注意点击区域,默认情况下GestureDetector的onTap
单击事件点击区域是不包括透明区域的,需要将behavior属性设为 HitTestBehavior.opaque
,包含透明区域,不然你可能在空白区域点半天,也没见键盘收起。当然,我比较推荐使用GestureDetector
来处理单击事件。
完整代码:
import 'package:flutter/material.dart';
class KeyboardDemo extends StatefulWidget {
final String inquiryCode;
const KeyboardDemo({
Key? key,
required this.inquiryCode,
}) : super(key: key);
@override
State<KeyboardDemo> createState() => _KeyboardDemoState();
}
class _KeyboardDemoState extends State<KeyboardDemo>
with WidgetsBindingObserver {
// 键盘是否打开
bool isKeyboardOpen = false;
@override
void initState() {
super.initState();
// 注册监听
WidgetsBinding.instance?.addObserver(this);
}
@override
void dispose() {
super.dispose();
// 移除监听
WidgetsBinding.instance?.removeObserver(this);
}
/// 页面尺寸改变时回调
@override
didChangeMetrics() {
super.didChangeMetrics();
// 在页面重新渲染完成之后,通过MediaQuery.of(context).viewInsets.bottom获取软键盘高度
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
isKeyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(5),
child: Container(
color: Colors.white,
),
),
/// 当弹出软键盘的时候,是否自动调节body区域,默认为true
resizeToAvoidBottomInset: false,
body: GestureDetector(
/// 点击内容包含透明区域
behavior: HitTestBehavior.opaque,
onTap: () {
/// 取消当前软键盘焦点并收起
FocusManager.instance.primaryFocus?.unfocus();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: 200,
child: Container(
margin: const EdgeInsets.all(12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(
width: 1.r,
color: Colors.black,
)),
child: const TextField(
decoration: InputDecoration(
hintText: '请输入内容',
border: InputBorder.none,
),
),
),
),
Container(
width: double.infinity,
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: ElevatedButton(
onPressed: () {},
child: const Text('确定'),
),
),
],
),
),
);
}
}
转载自:https://juejin.cn/post/7176806358346367013