Flutter GestureDetector无法获取点击事件的问题
· 阅读需 3 分钟
哪些情况
在有TextField
的表单界面中,点击空白部分隐藏键盘是基本功能。但是如果GestureDetector
直接包裹着TextField
是无法响应onTap
事件的,比如下面这种情况:
/// 1
GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: TextField(),
)
即便再给TextField
套一层Container
,也是没有响应:
/// 2
GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Container(
height: 300,
color: Colors.grey[200],
child: TextField(),
),
),
再或者嵌套一个Column
:
/// 3
GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Column(
children: [
TextField(),
Spacer(),
],
),
)
解决办法
- 方案一
在
Column
套一层Container
,而且必须设置颜色才会生效。
Container(
color: Colors.red,// 必须设置颜色
child: Column(
children: [
TextField(),
Spacer(),
],
),
),
- 方案二
使用
ListView
包含TextField
ListView(
children: [
TextField(),
],
),
什么原因
这个问题有人在 flutter issues提过:GestureDetector onTap doesn't work with Column widget,相关问题和解答可以看看。
下面来聊聊具体的东西。
GestureDetector
有个behavior
字段,这是个枚举类型:
/// How to behave during hit tests.
enum HitTestBehavior {
/// Targets that defer to their children receive events within their bounds
/// only if one of their children is hit by the hit test.
deferToChild,
/// Opaque targets can be hit by hit tests, causing them to both receive
/// events within their bounds and prevent targets visually behind them from
/// also receiving events.
opaque,
/// Translucent targets both receive events within their bounds and permit
/// targets visually behind them to also receive events.
translucent,
}
这三种点击事件的作用就是:
- deferToChild:child处理事件,默认
- opaque:自己处理事件
- translucent:自己和child都可以接收事件
Container
有个隐藏彩蛋,即当给Container
设置color
的时候,点击有响应。而color
为空,则点击无响应。具体原因查看: Flutter : 关于 HitTestBehavior。
基于上面两个前提条件,我们可以得出以下情况:
// 不响应onTap
GestureDetector(
behavior: HitTestBehavior.deferToChild, // 1
onTap: () => print('hit'),
child: Container(
height: 300,
//color: Colors.red, // 2
),
)
// 响应onTap
GestureDetector(
behavior: HitTestBehavior.opaque, // 1
onTap: () => print('hit'),
child: Container(
height: 300,
//color: Colors.red, // 2
),
)
// 响应onTap
GestureDetector(
behavior: HitTestBehavior.deferToChild, // 1
onTap: () => print('hit'),
child: Container(
height: 300,
color: Colors.red, // 2
),
)