(1)在移动端,各个平台或UI系统的原始指针事件模型基本都是一致,即:一次完整的事件分为三个阶段:手指按下、手指移动、和手指抬起,而更高级别的手势(如点击、双击、拖动等)都是基于这些原始事件的。 当指针按下时,Flutter会对应用程序执行命中测试(Hit Test),以确定指针与屏幕接触的位置存在哪些widget。注意,只有通过命中测试的Widget才能触发事件。 (2)Flutter中可以使用Listener widget来监听原始触摸事件,它也是一个功能性widget。
Listener({ Key key, this.onPointerDown, //手指按下回调 this.onPointerMove, //手指移动回调 this.onPointerUp,//手指抬起回调 this.onPointerCancel,//触摸事件取消回调 this.behavior = HitTestBehavior.deferToChild, //在命中测试期间如何表现 Widget child })(3)说说Listener behavior:
Widget getBody() { return Container( child: Listener( child: ConstrainedBox( constraints: BoxConstraints.tight(Size(double.infinity, double.infinity)), child: Center(child: Text("Box A")), ), //behavior: HitTestBehavior.opaque, onPointerDown: (event) => print("down A") ), ); }说明: (a)注释掉上面behavior时,只有点击Text("Box A")时,才会打印down A;点击Text之外区域不会打印down A。 (b)当将behavior设置为opaque时,点击Text之外区域也会打印down A。 (c)opaque是不透明、模糊的意思。 (d)在命中测试的时候,将behavior设置为opaque,相当于当前Widget的整个区域都是点击区域。不然会按照deferToChild去子widget判断是否命中测试,而该例中子widget就是 Text("Box A") 。 (e)若将behavior设置为deferToChild时,子widget会一个接一个的进行命中测试,如果子Widget中有测试通过的,则当前Widget通过,这就意味着,如果指针事件作用于子Widget上时,其父(祖先)Widget也肯定可以收到该事件。 (4)忽略PointerEvent 假如我们不想让某个子树响应PointerEvent的话,我们可以使用IgnorePointer和AbsorbPointer,这两个Widget都能阻止子树接收指针事件,不同之处在于AbsorbPointer本身会参与命中测试,而IgnorePointer本身不会参与,这就意味着AbsorbPointer本身是可以接收指针事件的(但其子树不行),而IgnorePointer不可以。
Widget getBody() { return Container( child: Listener( child: AbsorbPointer( child: Listener( child: Container( color: Colors.green, ), onPointerDown: (event)=>print("in"), ), ), onPointerDown: (event)=>print("up"), ), ); }说明: (a)上面代码点击只会输出up。这是因为AbsorbPointer自己会进行命中测试,而子widget会被阻止。 (b)将AbsorbPointer改为IgnorePointer时,则什么都不会输出。 (c)将AbsorbPointer改为Container时,则先输出in,后输出up。