flutter 手势控制

flutter 手势控制

事件监听
1. 指针事件Point
2 手势识别Gesture
3.跨组件事件
在大前端的开发中,必然存在着各种各样和用户交互的情况: 比如手指点击,手指滑动、双击、长按等

在Flutter中,手势有两个不同的层次:

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

PointerDownEvent 指针在特定位置与屏幕接触
PointerMoveEvent 指针从屏幕的一个位置移动到另外一个位置
PointerUpEvent 指针与屏幕停止接触
PointerCancelEvent 指针因为一些特殊情况被取消
Pointer的原理是什么呢?

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Center(
              child: Listener(
                onPointerDown: (event) {
                  print("指针按下:event=====${event}");
                },
                onPointerMove: (event) {
                  print("指针移动:event=======$event");
                },
                onPointerUp: (event) {
                  print("指针抬起:event=====$event");
                },
                child: Container(
                  width: 200,
                  height: 200,
                  color: Colors.red,
                ),
              ),
            )
        )
    );
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


2 手势识别Gesture
Gesture是对一系列Pointer的封装,官方建议开发中尽可能使用Gesture,而不是Pointer

Gesture分层非常多的种类:

点击:

onTapDown:用户手指按下操作
onTapUp: 用户手指抬起操作
onTap:用户点击事件完成
onTapCancel:事件按下发现过程中被取消
双击:
onDoubleTap:快速点击了两次
长按:

onLongPress:在屏幕上保持了一段时间


纵向拖拽:

onVerticalDragStart:指针和屏幕产生接触并可能开始纵向移动
onVerticalDragUpdate:指针和屏幕产生接触,在纵向上发生移动并保持移动
onVerticalDragEnd:指针和屏幕产生接触结束
横向拖拽:

onHorizontalDragStart:指针和屏幕产生接触并可能开始横向移动
onHorizontalDragUpdate:指针和屏幕产生接触,在横向上发生移动并保持移动
onHorizontalDragEnd:指针和屏幕产生接触结束
移动:

onPanStart:指针和屏幕产生接触并可能开始横向移动或者纵向移动,如果设置了onHorizontalDragStart或则onVerticalDragStart,指针移动具体方向,回调具体方向的监听方法,如果没有设置监听,就回直接回调onPanStart这个方法
onPanUpdate:指针和屏幕产生接触,在横向或者纵向上发生移动并保持移动。如果设置了onHorizontalDragUpdate或则onVerticalDragUpdate,指针移动具体方向,回调具体方向的监听方法,如果没有设置监听,就回直接回调onPanUpdate这个方法
onPanEnd:指针先前和屏幕产生了接触,并且以特定速度移动,此后不再在屏幕接触上发生移动。如果设置了onHorizontalDragEnd或则onVerticalDragEnd,指针移动具体方向,回调具体方向的监听方法,如果没有设置监听,就回直接回调onPanEnd这个方法


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

当然我们也可以使用RaisedButton、FlatButton、InkWell等来监听手势
globalPosition用于获取相对于屏幕的位置信息
localPosition:用于获取相对于当前Widget的位置信息
如果手势存在嵌套关系的时候, 手势时间可能会传递到外面,而且没有办法拦截这种传递
案例: 手势具有一个穿透效果, 现在我只想监听黄色区域的手势, 不想监听红色区域的手势

当我们直接使用两个Contain嵌套发现,第二个Contain的大小设置不管作用,会直接扩充到第一个Contain的大小,这个时候我们有两种方式来解决:

第一种:设置外层Contain的alignment属性,该属性会给里层的widget自动包括一层Alignd widget,这样就不是Contain的直接嵌套,里层的Contain设置的宽高就起作用了

class GYConten extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return GestureDetector(
      onTapDown: (details){
        print("外层Contain手势点击事件=======");
      },
      child: Container(
        width: 200,
        height: 200,
        color: Colors.red,
        //获则设置alignment属性
        alignment: Alignment.center,
        child: GestureDetector(
          onTapDown: (details) {
            print("里层Contain手势点击事件--------");
          },
          child: Container(
            //如果外层包裹的是一层Contain并且设置了大小,那么当前Contain设置大小没有作用,会直接扩从到跟外层的Contain一样的大小
            width: 100,
            height: 100,
            color: Colors.orange,
          ),
        )
      ),
    );
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29


由执行结果可知, 不段点击里层的Contain,调用里层的onTapDown方法,偶尔会传递到外层Contain的onTapDown的方法调用, 如何解决这个问题了 , 我们可以使用IgnorePointer来包裹里层的GestureDetector对象,忽略该层手势的事件,间接的组织了消息往外传递



第二种: 使用stack来解决这种问题, 因为stack 也可以重叠 ,可以比较好点 解决这种问题

@override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Center(
      child: GestureDetector(
        child: Stack(
          alignment: Alignment.center,
            children: [
          Container(
            width: 200,
            height: 200,
            color: Colors.red,
          ),
          Container(
            width: 100,
            height: 100,
            color: Colors.orange,
          )
        ]),
      ),
    )));
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23


3.跨组件事件
在组件之间如果有事件需要传递,一方面可以一层层传递,另一方面我们也可以使用一个EventBus工具来完成

其实EventBus在Vue、React中都是一种非常常见的跨组件通信的方式:

EventBus相当于是一种订阅者模式,通过一个全局的对象来管理;
这个EventBus我们可以自己实现,也可以使用第三方的EventBus
这里我们直接选择第三方的EventBus:


dependencies:
  event_bus: ^2.0.0
1
2
3
如何传递消息, 该三方库建议我们在传递消息的时候,见一个事件模型:


event_bus的使用
// 1.创建一个事件对象
class UserInfoEvent {
  String name;
  String level;

  UserInfoEvent(this.name, this.level);
}

//2.创建一个全局的eventBus对象
final eventBus = EventBus();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Center(
              child:Column(
                mainAxisAlignment: MainAxisAlignment.center ,
                children: [
                  GYButton(),
                  GYText()
                ],
              ),
            )));
  }
}

class GYButton extends StatelessWidget {
  const GYButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: ElevatedButton(
        child: Text("发出事件"),
        onPressed: (){
          print("按钮点击事件------");
          //3. 发出消息
          final userInfo = UserInfoEvent("zhangsan", "事件传递");
          eventBus.fire(userInfo);
        },
      ),
    );
  }
}

class GYText extends StatefulWidget {
  const GYText({Key? key}) : super(key: key);

  @override
  _GYTextState createState() => _GYTextState();
}

class _GYTextState extends State<GYText> {
  String message = "初始化信息";
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    //监听事件,收取消息
   eventBus.on<UserInfoEvent>().listen((event) {
     setState(() {
       message = "${event.name}-${event.level}";
     });
   });
  }
  @override
  Widget build(BuildContext context) {

    return Container(
      child: Text(message),
    );
  }
}
————————————————
版权声明:本文为CSDN博主「GY-93」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yong_19930826/article/details/119956502
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值