Flutter如何阻止Stack布局内两个组件同时响应触摸事件
Flutter的手势命中测试遵循组件树从上到下的分发逻辑,上层组件未完全消费的手势事件,会继续透传给下层命中的组件。当前Stack布局中,ListView滑动过程中产生的部分手势事件没有被完全拦截,就会传递到底层的Map组件,触发Map的滑动响应。
方案1:上层组件拦截手势(适配需要保留Map交互、仅在ListView区域滑动时不触发Map的场景)
在ListView外层包裹GestureDetector,注册对应滑动方向的手势回调,主动持有手势响应权,避免事件透传:Stack( children: <Widget>[ Map(), GestureDetector( // 纵向滑动相关回调,传入空实现即可拦截事件 onVerticalDragStart: (_) {}, onVerticalDragUpdate: (_) {}, onVerticalDragEnd: (_) {}, child: ListView( // 原有ListView的所有配置不变 ), ) ], )如果ListView支持横向滑动,补充
onHorizontalDrag*系列对应回调即可。方案2:直接阻断下层Map的手势接收(适配ListView展示阶段Map完全不需要响应操作的场景)
给Map组件包裹AbsorbPointer组件,直接拦截所有传递到Map的手势事件,从根源上避免Map响应滑动:Stack( children: <Widget>[ AbsorbPointer( child: Map(), ), ListView( // 原有ListView的所有配置不变 ) ], )也可以用
IgnorePointer替换AbsorbPointer,两者都能阻断子组件接收手势,区别是前者会自身参与手势命中、后者会直接跳过自身和子节点的命中测试,这个场景下两者效果一致。排查点:确认ListView布局尺寸符合预期
如果上述方案无效,优先用Flutter Inspector检查ListView的实际渲染边界:如果ListView设置的高度不足、或者physics属性配置为NeverScrollableScrollPhysics导致本身不响应滑动,触摸事件会直接穿透到下层Map,这种情况先修正ListView的布局和滚动配置即可。
内容的提问来源于stack exchange,提问作者Yodalam

