Flutter 手势系列教程---Listener

Listener介绍

Listener它是主要的功能是用来监听屏幕触摸事件,取决于它的子组件区域范围,比如按下、移动、抬起、取消等操作时可以添加监听。

我们知道Flutter组件只有按钮才会有事件,那么如果我需要在文字或者某个容器上添加事件那我就需要借助Listener

视频教程地址

手势系列视频教程地址

在什么情况下使用Listener?

Listener常用于当手指滑动屏幕时进行隐藏键盘或者下拉刷新、上拉加载时进行事件监听。

一般在实际的开发过程中我们很少会用到Listener来监听手势,一般都是通过GestureDetector来进行监听或者使用MouseRegion来监听鼠标的事件,而MouseRegion常用于web开发中,GestureDetector常用于app。

Listener原理

  • 当指针按下时,Flutter会对应用程序执行命中测试(Hit Test),以确定指针与屏幕接触的位置存在哪些组件(widget)
  • 指针按下事件(以及该指针的后续事件)然后被分发到由命中测试发现的最内部的组件
  • 事件会沿着最内部的组件向组件树的根冒泡分发
  • 没有机制取消或停止“冒泡”过程

Listener构造函数

我们经常使用的回调函数主要有三个

  • onPointerDown()
  • onPointerMove()
  • onpointUp()
const Listener({
    Key key,
    this.onPointerDown,
    this.onPointerMove,
    // We have to ignore the lint rule here in order to use deprecated
    // parameters and keep backward compatibility.
    // TODO(tongmu): After it goes stable, remove these 3 parameters from Listener
    // and Listener should no longer need an intermediate class _PointerListener.
    // https://github.com/flutter/flutter/issues/36085
    @Deprecated(
      'Use MouseRegion.onEnter instead. See MouseRegion.opaque for behavioral difference. '
      'This feature was deprecated after v1.10.14.'
    )
    this.onPointerEnter,
    @Deprecated(
      'Use MouseRegion.onExit instead. See MouseRegion.opaque for behavioral difference. '
      'This feature was deprecated after v1.10.14.'
    )
    this.onPointerExit,
    @Deprecated(
      'Use MouseRegion.onHover instead. See MouseRegion.opaque for behavioral difference. '
      'This feature was deprecated after v1.10.14.'
    )
    this.onPointerHover,
    this.onPointerUp,
    this.onPointerCancel,
    this.onPointerSignal,
    this.behavior = HitTestBehavior.deferToChild,
    Widget child,
  }) : assert(behavior != null),
       _child = child,
       super(key: key);

Listener属性和说明

字段 属性 描述
onPointerDown PointerDownEventListener 指针按下时触发回调
onPointerMove PointerMoveEventListener 指针移动时触发回调
onPointerUp PointerUpEventListener 指针移开时触发回调
onPointerSignal PointerSignalEventListener 当指针信号出现时调用
onPointerCancel PointerCancelEventListener 指针取消时触发回调
onPointerEnter PointerEnterEventListener 当指针进入区域时回调(已废弃)
onPointerExit PointerExitEventListener 当指针移出区域时回调(已废弃)
onPointerHover PointerHoverEventListener 当没有触发 [onPointerDown] 的指针改变时调用
behavior HitTestBehavior 在命中测试期间如何表现
child Widget 子组件

Listener基本使用

我们这里主要是针对onPointerDownonPointerMoveonPointerUp 进行演示,因为我们在平时的开发过程中最常用到的属性就是这三个,而且其他的属性也都被废弃掉了。

import 'package:flutter/material.dart';


class ListenerExample extends StatefulWidget {
  @override
  _ListenerExampleState createState() => _ListenerExampleState();
}

class _ListenerExampleState extends State<ListenerExample> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Listener"),
      ),
      body: Center(
        child: Stack(
          children: [
            Listener(
              onPointerDown: (event) {
                print("onPointerDown----$event");
              },
              onPointerMove: (event) {
                print("onPointerMove----$event");
              },
              onPointerUp: (event) {
                print("onPointerUp----$event");
              },
              // onPointerSignal: (event) {
              //   print("onPointerSignal----$event");
              // },
              // onPointerCancel: (event) {
              //   print("onPointerCancel----$event");
              // },
              // onPointerEnter: (event) {
              //   print("onPointerEnter----$event");
              // },
              // onPointerExit: (event) {
              //   print("onPointerExit----$event");
              // },
              // onPointerHover: (event) {
              //   print("onPointerHover----$event");
              // },
              child: Container(
                color: Colors.pink,
                child: Text("Jimi",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 30
                  ),
                ),
              ),
            ),
            Positioned(
              child: Listener(
                onPointerDown: (event) {
                  print("red---- $event");
                },
                child: Container(
                  width: 100,
                  height: 100,
                  color: Colors.red,
                  child: Text("Jimi"),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

控制台输出

我们这里先点击橙色容器,在点击一次红色容器,他们打印的结果如下。

flutter: onPointerDown----PointerDownEvent#128be(position: Offset(204.5, 403.5))
flutter: onPointerUp----PointerUpEvent#a9558(position: Offset(204.5, 403.5))
flutter: red---- PointerDownEvent#8ffdf(position: Offset(140.5, 317.0))

PointerEvent介绍

PointerEvent是触摸、手写笔、鼠标事件的基类。

在上文中,我们知道了什么是Listener并写了一个简单的案例,在使用案例的过程中我们的事件里面都带了一个event参数,而所有的事件最终都是继承自PointerEvent,那我们接下来看看event的参数有什么作用。

PointerEvent构造函数

const PointerEvent({
  this.embedderId = 0,
  this.timeStamp = Duration.zero,
  this.pointer = 0,
  this.kind = PointerDeviceKind.touch,
  this.device = 0,
  this.position = Offset.zero,
  Offset? localPosition,
  this.delta = Offset.zero,
  Offset? localDelta,
  this.buttons = 0,
  this.down = false,
  this.obscured = false,
  this.pressure = 1.0,
  this.pressureMin = 1.0,
  this.pressureMax = 1.0,
  this.distance = 0.0,
  this.distanceMax = 0.0,
  this.size = 0.0,
  this.radiusMajor = 0.0,
  this.radiusMinor = 0.0,
  this.radiusMin = 0.0,
  this.radiusMax = 0.0,
  this.orientation = 0.0,
  this.tilt = 0.0,
  this.platformData = 0,
  this.synthesized = false,
  this.transform,
  this.original,
}) : localPosition = localPosition ?? position,
     localDelta = localDelta ?? delta;

PointerEvent属性和说明

PointerEvent的属性非常多,但在我们实际的开发过程中很少会使用到,只有在特定的情景下才会使用对应的属性。

如需要做一个全局悬浮的按钮我们会使用到position

如需要做绘图软件我们需要用到buttonskind

所以大家可以根据实际的应用场景来使用对应的属性即可,下面是我对PointerEvent的属性进行的一个详细描述。

总共29个属性

字段 属性 描述
embedderId int 标识平台事件ID
timeStamp Duration 事件调度时间
pointer int 指针唯一标识符,每一次点击都会是一个新的,不会重复
kind PointerDeviceKind 指针事件的输入设备类型
device int 设备唯一标识符,在交互中会重复使用
position Offset 指针相对于全局坐标的偏移
localPosition Offset 指针相对于当前容器坐标的偏移
delta Offset 两次指针移动事件的距离
localDelta Offset 两次指针移动事件的距离(当前容器)
buttons int 它是*button常量,经常与kind配合使用,做绘图的画笔软件是经常用到该属性,如果它的值为6,kind是PointerDeviceKind.invertedStylus,那么这表示一个倒置触控笔
down bool 设置当前指针是否按下
obscured bool 是否遮挡应用程序的窗口,该属性官方还没实现
pressure double 按压力度,压力传感器(如iPhone的3D Touch)中使用,取值0.0-1.0
pressureMin double 按压力度最小值
pressureMax double 按压力度最大值
distance double 检测物体与输入表面的距离
distanceMin double 限制检测物体与输入表面的距离最小值
distanceMax double 限制检测物体与输入表面的距离最大值
size double 被按下屏幕的区域大小
radiusMajor double 接触椭圆沿主轴的半径,以逻辑像素为单位
radiusMinor double 接触椭圆沿短轴的半径,以逻辑像素为单位
radiusMin double radiusMajor和radiusMinor报告的最小值
radiusMax double radiusMajor和radiusMinor报告的最大值
orientation double 检测到的物体的方向(指针移动方向),以弧度为单位
tilt double 检测到的物体的倾斜角度,以弧度为单位
platformData int 与事件关联的不透明平台特定数据
synthesized bool 设置事件是否由 Flutter 合成。
transform Matrix4 用于从全局坐标转换此事件的转换
original PointerEvent 在任何transform之前的原始未转换PointerEvent事件

behavior属性

behavior属性,它决定子组件如何响应命中测试,它的值类型为HitTestBehavior,这是一个枚举类,有三个枚举值

HitTestBehavior.deferToChild

对子组件一个接一个的进行命中测试,如果子组件中有测试通过的,则当前组件通过,这就意味着,如果指针事件作用于子组件上时,其父级组件也肯定可以收到该事件。

HitTestBehavior.opaque

在命中测试时,将当前组件当成不透明处理(即使本身是透明的),最终的效果相当于当前Widget的整个区域都是点击区域

HitTestBehavior.translucent

点击组件透明区域时,可以对自身边界内及底部可视区域都进行命中测试,这意味着点击顶部组件透明区域时,顶部组件和底部组件都可以接收到事件

代码演示

import 'package:flutter/material.dart';


class ListenerSimpleExample extends StatefulWidget {
  @override
  _ListenerSimpleExampleState createState() => _ListenerSimpleExampleState();
}

class _ListenerSimpleExampleState extends State<ListenerSimpleExample> with AutomaticKeepAliveClientMixin {

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("Listener"),
      ),
      body: Center(
        child: Stack(
          children: [
            Listener(
              child: ConstrainedBox(
                  constraints: BoxConstraints.tight(Size(400, 200)),
                  child: Container(
                    color: Colors.greenAccent,
                  )
              ),
              onPointerDown: (event) => print("绿色盒子被点击了"),
            ),
            Listener(
              child: ConstrainedBox(
                constraints: BoxConstraints.tight(Size(400, 200)),
                child: Center(child: Text("点击文字", style: TextStyle(
                  color: Colors.white,
                  fontSize: 30
                ),)),
              ),
              onPointerDown: (event) => print("文字点击事件回调"),
              behavior: HitTestBehavior.deferToChild,
              // behavior: HitTestBehavior.opaque,
              // behavior: HitTestBehavior.translucent,
            )
          ],
        ),
      ),
    );
  }
}

当属性设置为HitTestBehavior.deferToChild控制台输出结果

我们这里演示每次都是先点击绿色盒子在点击文字,以便大家能更好的分辨出这三个属性的使用区别

flutter: 绿色盒子被点击了
flutter: 文字点击事件回调

当属性设置为HitTestBehavior.opaque控制台输出结果

flutter: 文字点击事件回调
flutter: 文字点击事件回调

当属性设置为HitTestBehavior.translucent控制台输出结果

flutter: 文字点击事件回调
flutter: 绿色盒子被点击了
flutter: 文字点击事件回调

总结

ListenerFlutter中比较重要的功能性组件,它主要的功能是用来监听屏幕触摸事件,事件回调可以获取对应的属性来个性化定制app功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容