Day12 - Flutter - 事件监听

概述

  • 事件监听
  • 跨组件事件
一、事件监听
  • 1.1、在Flutter中,手势的两个不同的层次

    • 第一层:原始指针事件(Pointer Events):描述了屏幕上由触摸板、鼠标、指示笔等触发的指针的位置和移动。
    • 第二层:手势识别(Gesture Detector):这个是在原始事件上的一种封装。
      • 比如我们要监听用户长按,如果自己封装原始事件我们需要监听从用户按下到抬起的时间来判断是否是一次长按事件;
      • 比如我们需要监听用户双击事件,我们需要自己封装监听用户两次按下抬起的时间间隔;
      • 幸运的是各个平台几乎都对它们进行了封装,而Flutter中的手势识别就是对原始指针事件的封装;
      • 包括哪些手势呢?比如点击双击长按拖动
  • 1.2、指针事件 Pointer
    Pointer 代表的是人机界面交互的原始数据。一共有四种指针事件:

    • PointerDownEvent 指针在特定位置与屏幕接触
    • PointerMoveEvent 指针从屏幕的一个位置移动到另外一个位置
    • PointerUpEvent 指针与屏幕停止接触
    • PointerCancelEvent 指针因为一些特殊情况被取消,电话等等

    Pointer的原理是什么呢?

    • 在指针落下时,框架做了一个 hit test 的操作,确定与屏幕发生接触的位置上有哪些Widget以及分发给最内部的组件去响应;
    • 事件会沿着最内部的组件向组件树的根冒泡分发;
    • 并且不存在用于取消或者停止指针事件进一步分发的机制;

    原始指针事件使用Listener来监听:

    Center(
       child: Listener(
          onPointerDown: (event) {
            print('指针按下:$event'); 
          },
          onPointerMove: (event) {
            print('指针移动:$event');
          },
          onPointerUp: (event) {
            print('指针松开:$event');
          },
          onPointerCancel: (event) {
            print('指针打断取消:$event');
          },
          child: Container(
            width: 200,
            height: 200,
            color: Colors.green,
          ),
       )
    );
    

    提示:我们如果想拿到点击的位置,可以如下

    • 1、event.position 在整个界面内的位置
    • 2、event.localPosition 在自己内部的位置
  • 1.3、手势识别Gesture
    Gesture是对一系列Pointer的封装,官方建议开发中尽可能使用Gesture,而不是Pointer。
    Gesture分层非常多的种类:

    • 点击:
      • onTapDown:用户发生手指按下的操作
      • onTapUp:用户发生手指抬起的操作
      • onTap:用户点击事件完成
      • onTapCancel:事件按下过程中被取消
    • 双击:
      • onDoubleTap:快速点击了两次
    • 长按:
      • onLongPress:在屏幕上保持了一段时间
    • 纵向拖拽:
      • onVerticalDragStart:指针和屏幕产生接触并可能开始纵向移动;
      • onVerticalDragUpdate:指针和屏幕产生接触,在纵向上发生移动并保持移动;
      • onVerticalDragEnd:指针和屏幕产生接触结束;
    • 横线拖拽:
      • onHorizontalDragStart:指针和屏幕产生接触并可能开始横向移动;
      • onHorizontalDragUpdate:指针和屏幕产生接触,在横向上发生移动并保持移动;
      • onHorizontalDragEnd:指针和屏幕产生接触结束;
    • 移动:
      • onPanStart:指针和屏幕产生接触并可能开始横向移动或者纵向移动。如果设置了 onHorizontalDragStart 或者 onVerticalDragStart,该回调方法会引发崩溃;
        onPanUpdate:指针和屏幕产生接触,在横向或者纵向上发生移动并保持移动。如果设置了 onHorizontalDragUpdate 或者 onVerticalDragUpdate,该回调方法会引发崩溃。
      • onPanEnd:指针先前和屏幕产生了接触,并且以特定速度移动,此后不再在屏幕接触上发生移动。如果设置了 onHorizontalDragEnd 或者 onVerticalDragEnd,该回调方法会引发崩溃。

    从Widget的层面来监听手势,我们需要使用:GestureDetector

    • 当然,我们也可以使用RaisedButton、FlatButton、InkWell等来监听手势

    • globalPosition用于获取相对于屏幕的位置信息

    • localPosition用于获取相对于当前Widget的位置信息

      Center(
         child: GestureDetector(
              onTapDown: (detail) {
                  print('手指按下');
              },
              onTapUp: (detail) {
                  print('手指抬起');
              },
              onTapCancel: () {
                  print('手势取消');
              },
              onTap: () {
                  print('手势点击');
              },
              onDoubleTap: () {
                  print('手势双击');
              },
              onLongPress: () {
                  print('长按手势');
              },
              child: Container(
                  width: 200,
                  height: 200,
                  color: Colors.green,
              ),
         )
      );
      
  • 1.4、看下面的一个问题,点击小的Widget,大的Widget也会响应

    Center(
       child: GestureDetector(
         onTapDown: (detail) {
            print('点击---大---的Widget');
         },
         child: Container(
            alignment: Alignment.center,
            width: 200,
            height: 200,
            color: Colors.brown,
            child: GestureDetector(
               onTapDown: (detail) {
                 print('点击---小---的Widget');
               },
               child: Container(
                 width: 100,
                 height: 100,
                 color: Colors.yellow,
               ),
            ),
         ),
      ),
    );
    

    解决问题,我们用 Stack 包括两个 GestureDetector 如下

    Center(
       child: Stack(
          alignment: Alignment.center,
          children: [
             GestureDetector(
                onTapDown: (detail) {
                   print('点击---大---的Widget');
                },
               child: Container(
                  alignment: Alignment.center,
                  width: 200,
                  height: 200,
                  color: Colors.brown,
               ),
             ),
             GestureDetector(
               onTapDown: (detail) {
                 print('点击---小---的Widget');
               },
               child: Container(
                 width: 100,
                 height: 100,
                 color: Colors.yellow,
               ),
             ),
          ],
       ),
    );
    

    假如我们想点击小的没反应,传递给大的 Widget 怎么办呢?那么我们就需要阻塞小的Widget,那么我们就需要给小的 Widget 包裹一个 IgnorePointer,让它手势事件,如下

    IgnorePointer(
        child: GestureDetector(
            onTapDown: (detail) {
              print('点击---小---的Widget');
            },
            child: Container(
              width: 100,
              height: 100,
              color: Colors.yellow,
            ),
        ),
    ),
    
二、跨组件事件(事件的传递)

在组件之间如果有事件需要传递,一方面可以一层层来传递,另一方面我们也可以使用一个EventBus工具来完成。
其实EventBus在Vue、React中都是一种非常常见的跨组件通信的方式:
EventBus相当于是一种订阅者模式,通过一个全局的对象来管理;
这个EventBus我们可以自己实现,也可以使用第三方的EventBus;
这里我们直接选择第三方的 Event Bus

dependencies:
   event_bus: ^2.0.0
  • 第一:我们需要定义一个希望在组件之间传递的对象:我们可以称之为一个事件对象,也可以是我们平时开发中用的模型对象(model)

    class UserInfo {
       String nickname;
       int level;
    
       UserInfo(this.nickname, this.level);
    }
    
  • 第二:创建一个全局的EventBus对象

    final eventBus = EventBus();
    
  • 第三:在某个Widget中,发出事件:

    class HYButton extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
            return RaisedButton(
               child: Text("HYButton"),
               onPressed: () {
                   final info = UserInfo("why", 18);
                   eventBus.fire(info);
               },
            );
         }
    }
    
  • 第四:在某个Widget中,监听事件

    class JKText extends StatefulWidget {
       @override
       _JKTextState createState() => _JKTextState();
    }
    
    class _JKTextState extends State<JKText> {
       String _message = 'Hello World!';
    
       @override
       void initState() {
           // TODO: implement initState
           super.initState();
    
           eventBus.on<UserModel>().listen((data) {
              setState(() {
                  _message = '${data.nickname} ${data.age}';
              });
    
           });
       }
    
       @override
       Widget build(BuildContext context) {
          return Text('$_message', style: TextStyle(fontSize: 22, color: Colors.green),);
       }
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350