Flutter之事件处理

7,286 阅读10分钟

在学习flutter的时候突然想到,flutter既然不像其他跨平台框架那样采用系统原生渲染,那么flutter就应该拥有自己的事件处理机制。本着好奇的心理,来对flutter的事件处理机制一窥究竟。

1、flutter事件传递

事件都是由硬件收集起来的,然后传递给软件。那么在flutter中,事件的源头在哪尼?

经过分析源码可以发现。在类window中,flutter通过方法onPointerDataPacket来接收硬件传递过来的事件,也就是说该方法是flutter接收事件的源头,而该方法是在类GestureBinding初始化的时候设置的。所以在类GestureBinding的方法_handlePointerDataPacket中开始处理事件。

mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    //_handlePointerDataPacket(是一个私有方法)接收系统传递过来的事件,
    window.onPointerDataPacket = _handlePointerDataPacket;
  }

  ...

  //一个FIFO的双端队列,用来存储事件
  final Queue<PointerEvent> _pendingPointerEvents = Queue<PointerEvent>();

  void _handlePointerDataPacket(ui.PointerDataPacket packet) {
    //这里做了以下两个操作
    //1、将pointer数据转换为逻辑像素,从而隔离真实设备。
    //2、将转换后的数据加入到FIFO双端队列中
    _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));
    if (!locked){
      //开始处理事件
      _flushPointerEventQueue();
    }
  }

  ...

  void _flushPointerEventQueue() {
    assert(!locked);
    //如果队列中有数据,就处理
    while (_pendingPointerEvents.isNotEmpty)
      _handlePointerEvent(_pendingPointerEvents.removeFirst());
  }

  ...
  
  final Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};
  
  void _handlePointerEvent(PointerEvent event) {
    HitTestResult hitTestResult;
    //如果是Down事件
    if (event is PointerDownEvent) {
      assert(!_hitTests.containsKey(event.pointer));
      hitTestResult = HitTestResult();
      //将事件要经过的所有Widget添加到一个集合中
      hitTest(hitTestResult, event.position);
      _hitTests[event.pointer] = hitTestResult;
    } else if (event is PointerUpEvent || event is PointerCancelEvent) {
      hitTestResult = _hitTests.remove(event.pointer);
    } else if (event.down) {
      //如果是move事件
      hitTestResult = _hitTests[event.pointer];
    }
    if (hitTestResult != null ||
        event is PointerHoverEvent ||
        event is PointerAddedEvent ||
        event is PointerRemovedEvent) {
      //分发事件
      dispatchEvent(event, hitTestResult);
    }
  }

  @override // from HitTestable
  void hitTest(HitTestResult result, Offset position) {
    result.add(HitTestEntry(this));
  }

  //事件的分发
  @override // from HitTestDispatcher
  void dispatchEvent(PointerEvent event, HitTestResult hitTestResult) {
    ...
    for (HitTestEntry entry in hitTestResult.path) {
      try {
        //分发给子Widget处理
        entry.target.handleEvent(event, entry);
      } catch (exception, stack) {
        ...
      }
    }
  }
  
  //
  @override // from HitTestTarget
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    pointerRouter.route(event);
    if (event is PointerDownEvent) {
      //关闭竞技场,不允许新的成员加入
      gestureArena.close(event.pointer);
    } else if (event is PointerUpEvent) {
      //结束竞技场,调用sweep方法后,pointer所对应的竞技场已然无用了
      gestureArena.sweep(event.pointer);
    }
  }
}

上面就是处理事件分发的源码,它主要做了以下三步操作。

1.1、接收事件

由于接收硬件的数据都是与真实设备相关的,所以需要通过PointerEventConverterexpand方法来隔离设备相关性。类似Android中的pxdp

只有将数据进行设备隔离后才能放入FIFO的双端队列——ListQueue中。笔者认为这里使用队列是为了防止Widget处理不及时从而导致阻塞。如下图所示。

1.2、Down事件处理

Android一样,Down事件在flutter中也是非常重要的一个事件,它是从ListQueue中取出的第一个事件。通过该事件,flutter可以获取到目标Widget到根Widget的路径,并将路径上的所有Widget添加到一个集合List中。如下图所示。

注意: 添加到集合中的Widget必须都要继承自RenderObjectWidget

将路径上的所有Widget添加到集合后,就会遍历该集合,并将Down事件交给集合中的所有Widget处理。如下图所示。

上图中WidgetHandlerEvent函数基本上都是空实现,但Listener例外。所以基本上都是通过Listener来监听手势事件。

1.3、其他事件处理

Down事件处理完毕以后,其他事件(如moveup等)会遍历集合,并将事件传递给集合中的所有Widget处理,如下图所示。

上图中WidgetHandlerEvent函数基本上都是空实现,但Listener例外。所以基本上都是通过Listener来监听手势事件。

可以发现事件是从目标WidgetRenderView(根Widget)传递的。如果是做前端开发的,想必对这一流程比较熟悉,因为这与前端开发中浏览器的事件冒泡机制相似, 但在flutter中是没有机制来取消或停止”冒泡“过程的,而浏览器的冒泡是可以停止的。

2、flutter事件拦截

事件既然有分发,那么肯定也能够拦截,相对于Android而言,笔者认为flutter的事件拦截要简单很多。在flutter中,可以通过AbsorbPointerIgnorePointer这两个Widget来拦截事件。

2.1、AbsorbPointer

AbsorbPointer是一个Widget,它的主要作用就是拦截其子Widget响应事件。它的实现原理其实很简单,就是在响应Donw事件时,不会将其子Widget添加到集合中,这样其子Widget就无法接收到事件。如下图所示。 AbsorbPointer中可以修改absorbing的值来让子Widget响应事件。

注意:AbsorbPointer本身可以响应事件

2.2、IgnorePointer

AbsorbPointer是一个Widget,它的主要作用就是拦截其子Widget响应事件。它的实现原理其实很简单,就是在响应Donw事件时,不会将自身及其子Widget添加到集合中,这样自己及其子Widget就无法接收到事件。如下图所示。 IgnorePointer中可以修改ignoring的值来让自己及其子Widget响应事件。

注意:IgnorePointer本身无法响应事件

2.3、事件拦截的应用

AbsorbPointer为例,下面来看一下如何拦截事件。

class MyHomePage extends StatelessWidget {
  _onPointerDown(PointerDownEvent event) {
    print("_onPointerDown:" + event.toString());
  }

  _onPointerMove(PointerMoveEvent event) {
    print("_onPointerMove:" + event.toString());
  }

  _onPointerUp(PointerUpEvent event) {
    print("_onPointerUp:" + event.toString());
  }

  _onPointerDown1(PointerDownEvent event) {
    print("_onPointerDown1:" + event.toString());
  }

  _onPointerMove1(PointerMoveEvent event) {
    print("_onPointerMove1:" + event.toString());
  }

  _onPointerUp1(PointerUpEvent event) {
    print("_onPointerUp1:" + event.toString());
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
        onPointerDown: _onPointerDown,
        onPointerMove: _onPointerMove,
        onPointerUp: _onPointerUp,
        child: AbsorbPointer(
          //该值为false则下面的Listener不会打印任何信息
          //absorbing: false,
            child: Listener(
          onPointerDown: _onPointerDown1,
          onPointerMove: _onPointerMove1,
          onPointerUp: _onPointerUp1,
          child: Container(
            color: Colors.red,
            width: 200.0,
            height: 100.0,
          ),
        )));
  }
}

Listener可以监听最原始的事件,通过该Widget可以拿到事件的相关信息。通过运行上面代码可以发现第二个Listener中的相关信息都不会被打印。但如果将absorbing的值改为false。则第二个Listener的信息都会打印出来。

IgnorePointerAbsorbPointer的用法基本一致。

3、手势处理

前面介绍了flutter中的事件分发及处理,但都是基于最原始的指针信息。当如果我们想要实现点击、双击、快速滑动等功能时,通过最原始的指针信息就比较麻烦了,这时候就需要使用flutter给我们封装好了的Widget——GestureDetector

3.1、GestureDetector

GestureDetector封装了点击、双击、滑动等大量功能,使开发者可以快速使用这些基础性功能。

GestureDetector({
    Key key,
    this.child,
    this.onTapDown,
    this.onTapUp,
    this.onTap,
    this.onTapCancel,
    this.onDoubleTap,
    this.onLongPress,
    this.onLongPressUp,
    this.onLongPressDragStart,
    this.onLongPressDragUpdate,
    this.onLongPressDragUp,
    this.onVerticalDragDown,
    this.onVerticalDragStart,
    this.onVerticalDragUpdate,
    this.onVerticalDragEnd,
    this.onVerticalDragCancel,
    this.onHorizontalDragDown,
    this.onHorizontalDragStart,
    this.onHorizontalDragUpdate,
    this.onHorizontalDragEnd,
    this.onHorizontalDragCancel,
    this.onForcePressStart,
    this.onForcePressPeak,
    this.onForcePressUpdate,
    this.onForcePressEnd,
    this.onPanDown,
    this.onPanStart,
    this.onPanUpdate,
    this.onPanEnd,
    this.onPanCancel,
    this.onScaleStart,
    this.onScaleUpdate,
    this.onScaleEnd,
    this.behavior,
    this.excludeFromSemantics = false,
    this.dragStartBehavior = DragStartBehavior.down,
  }) 

从上面代码可以看出,GestureDetector的功能还是蛮丰富的。下面来看如何使用GestureDetector,以点击事件为例。

class MyHomePage extends StatelessWidget {
  _onPointerDown(PointerDownEvent event) {
    print("_onPointerDown:" + event.toString());
  }

  _onPointerMove(PointerMoveEvent event) {
    print("_onPointerMove:" + event.toString());
  }

  _onPointerUp(PointerUpEvent event) {
    print("_onPointerUp:" + event.toString());
  }

  _onPointerEnter(PointerEnterEvent event) {
    print("_onPointerEnter:" + event.toString());
  }

  _onPointerExit(PointerExitEvent event) {
    print("_onPointerExit:" + event.toString());
  }

  _onPointerHover(PointerHoverEvent event) {
    print("_onPointerHover:" + event.toString());
  }

  _onPointerDown1(PointerDownEvent event) {
    print("_onPointerDown1:" + event.toString());
  }

  _onPointerMove1(PointerMoveEvent event) {
    print("_onPointerMove1:" + event.toString());
  }

  _onPointerUp1(PointerUpEvent event) {
    print("_onPointerUp1:" + event.toString());
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
        onPointerDown: _onPointerDown,
        onPointerMove: _onPointerMove,
        onPointerUp: _onPointerUp,
        onPointerEnter: _onPointerEnter,
        onPointerExit: _onPointerExit,
        onPointerHover: _onPointerHover,
        child: Stack(
          children: <Widget>[
            GestureDetector(
              //监听点击事件
              onTap: () => {print("点击事件")},
              //监听横向滑动事件
              onVerticalDragUpdate: (DragUpdateDetails details) =>
                  {print("横向滑动:" + details.toString())},
              child: Listener(
                  onPointerDown: _onPointerDown1,
                  onPointerMove: _onPointerMove1,
                  onPointerUp: _onPointerUp1,
                  child: Center(
                    child: Container(
                      color: Colors.red,
                      width: 200.0,
                      height: 100.0,
                    ),
                  )),
            ),
          ],
        ));
  }
}

通过上面代码就可以实现Widget的点击功能。使用起来蛮简单,但实现原理还是比较复杂的,下面就来看它的实现原理及与Listener的异同。

3.2、实现原理

先来看GestureDetector的实现源码。

class GestureDetector extends StatelessWidget {
@override
  Widget build(BuildContext context) {
    final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};

    if (
      onTapDown != null ||
      onTapUp != null ||
      onTap != null ||
      onTapCancel != null ||
      onSecondaryTapDown != null ||
      onSecondaryTapUp != null ||
      onSecondaryTapCancel != null
    ) {
      //点击事件
      gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
        () => TapGestureRecognizer(debugOwner: this),
        (TapGestureRecognizer instance) {
          instance
            ..onTapDown = onTapDown
            ..onTapUp = onTapUp
            ..onTap = onTap
            ..onTapCancel = onTapCancel
            ..onSecondaryTapDown = onSecondaryTapDown
            ..onSecondaryTapUp = onSecondaryTapUp
            ..onSecondaryTapCancel = onSecondaryTapCancel;
        },
      );
    }
    
    //双击事件
    if (onDoubleTap != null) {
      gestures[DoubleTapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
        () => DoubleTapGestureRecognizer(debugOwner: this),
        (DoubleTapGestureRecognizer instance) {
          instance
            ..onDoubleTap = onDoubleTap;
        },
      );
    }
    
    //长按事件
    if (onLongPress != null ||
        onLongPressUp != null ||
        onLongPressStart != null ||
        onLongPressMoveUpdate != null ||
        onLongPressEnd != null) {
      gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
        () => LongPressGestureRecognizer(debugOwner: this),
        (LongPressGestureRecognizer instance) {
          instance
            ..onLongPress = onLongPress
            ..onLongPressStart = onLongPressStart
            ..onLongPressMoveUpdate = onLongPressMoveUpdate
            ..onLongPressEnd =onLongPressEnd
            ..onLongPressUp = onLongPressUp;
        },
      );
    }
    
    //垂直方向滑动的down事件
    if (onVerticalDragDown != null ||
        onVerticalDragStart != null ||
        onVerticalDragUpdate != null ||
        onVerticalDragEnd != null ||
        onVerticalDragCancel != null) {
      gestures[VerticalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
        () => VerticalDragGestureRecognizer(debugOwner: this),
        (VerticalDragGestureRecognizer instance) {
          instance
            ..onDown = onVerticalDragDown
            ..onStart = onVerticalDragStart
            ..onUpdate = onVerticalDragUpdate
            ..onEnd = onVerticalDragEnd
            ..onCancel = onVerticalDragCancel
            ..dragStartBehavior = dragStartBehavior;
        },
      );
    }
    
    //水平方向的down事件
    if (onHorizontalDragDown != null ||
        onHorizontalDragStart != null ||
        onHorizontalDragUpdate != null ||
        onHorizontalDragEnd != null ||
        onHorizontalDragCancel != null) {
      gestures[HorizontalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
        () => HorizontalDragGestureRecognizer(debugOwner: this),
        (HorizontalDragGestureRecognizer instance) {
          instance
            ..onDown = onHorizontalDragDown
            ..onStart = onHorizontalDragStart
            ..onUpdate = onHorizontalDragUpdate
            ..onEnd = onHorizontalDragEnd
            ..onCancel = onHorizontalDragCancel
            ..dragStartBehavior = dragStartBehavior;
        },
      );
    }

    //识别水平和垂直方向的移动的down事件
    if (onPanDown != null ||
        onPanStart != null ||
        onPanUpdate != null ||
        onPanEnd != null ||
        onPanCancel != null) {
      gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
        () => PanGestureRecognizer(debugOwner: this),
        (PanGestureRecognizer instance) {
          instance
            ..onDown = onPanDown
            ..onStart = onPanStart
            ..onUpdate = onPanUpdate
            ..onEnd = onPanEnd
            ..onCancel = onPanCancel
            ..dragStartBehavior = dragStartBehavior;
        },
      );
    }
    
    //缩放的开始事件
    if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
      gestures[ScaleGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
        () => ScaleGestureRecognizer(debugOwner: this),
        (ScaleGestureRecognizer instance) {
          instance
            ..onStart = onScaleStart
            ..onUpdate = onScaleUpdate
            ..onEnd = onScaleEnd;
        },
      );
    }
    
    //识别具有力传感器的设备上的力压
    if (onForcePressStart != null ||
        onForcePressPeak != null ||
        onForcePressUpdate != null ||
        onForcePressEnd != null) {
      gestures[ForcePressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>(
        () => ForcePressGestureRecognizer(debugOwner: this),
        (ForcePressGestureRecognizer instance) {
          instance
            ..onStart = onForcePressStart
            ..onPeak = onForcePressPeak
            ..onUpdate = onForcePressUpdate
            ..onEnd = onForcePressEnd;
        },
      );
    }

    return RawGestureDetector(
      gestures: gestures,
      behavior: behavior,
      excludeFromSemantics: excludeFromSemantics,
      child: child,
    );
  }
}

class RawGestureDetector extends StatefulWidget {
  ...

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

/// State for a [RawGestureDetector].
class RawGestureDetectorState extends State<RawGestureDetector> {
  ...

  void _handlePointerDown(PointerDownEvent event) {
    for (GestureRecognizer recognizer in _recognizers.values)
      recognizer.addPointer(event);
  }


  @override
  Widget build(BuildContext context) {
    //使用Listener来监听down事件
    Widget result = Listener(
      onPointerDown: _handlePointerDown,
      behavior: widget.behavior ?? _defaultBehavior,
      child: widget.child,
    );
    if (!widget.excludeFromSemantics)
      result = _GestureSemantics(
        child: result,
        assignSemantics: _updateSemanticsForRenderObject,
      );
    return result;
  }

  ...
}

在源码中,可以发现GestureDetector最终是通过Listener来监听手势事件的。但仅监听了Down事件,那么其他事件是如何处理的尼?

来看_handlePointerDown函数中的实现,在该函数中调用了GestureRecognizeraddPointer函数。这里以PrimaryPointerGestureRecognizer为例,它主要是处理点击事件。

abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableTreeMixin {
  void addPointer(PointerDownEvent event) {
    _pointerToKind[event.pointer] = event.kind;
    if (isPointerAllowed(event)) {
      addAllowedPointer(event);
    } else {
      ...
    }
  }
@protected
  void addAllowedPointer(PointerDownEvent event) { }
}

//这里以PrimaryPointerGestureRecognizer为例。
abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecognizer {
  @override
  void addAllowedPointer(PointerDownEvent event) {
    startTrackingPointer(event.pointer, event.transform);
    if (state == GestureRecognizerState.ready) {
      state = GestureRecognizerState.possible;
      primaryPointer = event.pointer;
      initialPosition = OffsetPair(local: event.localPosition, global: event.position);
      if (deadline != null)
        _timer = Timer(deadline, () => didExceedDeadlineWithEvent(event));
    }
  }
  
  @override
  void handleEvent(PointerEvent event) {
    assert(state != GestureRecognizerState.ready);
    if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
      final bool isPreAcceptSlopPastTolerance =
          !_gestureAccepted &&
          preAcceptSlopTolerance != null &&
          _getGlobalDistance(event) > preAcceptSlopTolerance;
      final bool isPostAcceptSlopPastTolerance =
          _gestureAccepted &&
          postAcceptSlopTolerance != null &&
          _getGlobalDistance(event) > postAcceptSlopTolerance;

      if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) {
        resolve(GestureDisposition.rejected);
        stopTrackingPointer(primaryPointer);
      } else {
        handlePrimaryPointer(event);
      }
    }
    stopTrackingIfPointerNoLongerDown(event);
  }
}

abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
  @protected
  void startTrackingPointer(int pointer, [Matrix4 transform]) {
    //1、将handleEvent作为路由添加到路由表中
    GestureBinding.instance.pointerRouter.addRoute(pointer, handleEvent, transform);
    _trackedPointers.add(pointer);
    //2、将当前手势添加到竞技场
    _entries[pointer] = _addPointerToArena(pointer);
  }

  @protected
  void handleEvent(PointerEvent event);
}

通过上面代码可以发现addPointer函数最终做了以下两件事。

  • PrimaryPointerGestureRecognizer中的handleEvent函数作为一个路由并添加到路由表中。
  • 将当前手势添加到竞技场,主要是为了解决手势冲突问题。注意: 靠近目标Widget越近的手势越先加入竞技场中。

先来看将路由加入路由表中的代码实现,如下。

class PointerRouter {
  //路由表
  final Map<int, Map<PointerRoute, Matrix4>> _routeMap = <int, Map<PointerRoute, Matrix4>>{};
  final Map<PointerRoute, Matrix4> _globalRoutes = <PointerRoute, Matrix4>{};
  //注册一个route到路由表中
  void addRoute(int pointer, PointerRoute route, [Matrix4 transform]) {
    final Map<PointerRoute, Matrix4> routes = _routeMap.putIfAbsent(
      pointer,
      () => <PointerRoute, Matrix4>{},
    );
    routes[route] = transform;
  }    
}

在前面说过,事件会从最底层的WidgetRenderView传递。但其实事件传递给RenderView后,还会传递给一个GestureBinding,在该类中会对事件做最终处理。

也就是说事件开始于GestureBinding_handlePointerDataPacket方法,结束于GestureBindinghandleEvent方法。下面来看handleEvent方法的实现。

  final PointerRouter pointerRouter = PointerRouter();
  //该方法是做事件的最后处理,不会在往其他地方传递
  @override // from HitTestTarget
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    //处理路由表中的路由
    pointerRouter.route(event);
    if (event is PointerDownEvent) {
      //关闭竞技场
      gestureArena.close(event.pointer);
    } else if (event is PointerUpEvent) {
      //解决手势冲突
      gestureArena.sweep(event.pointer);
    }
  }

handleEvent方法,会调用PointerRouterroute函数,也就是在该函数中,会对路由表中注册的路由一一处理,代码如下。

class PointerRouter {
  ...
  //路由的处理
  void _dispatch(PointerEvent event, PointerRoute route, Matrix4 transform) {
    try {
      event = event.transformed(transform);
      route(event);
    } catch (exception, stack) {
      ...
    }
  }

  //根据event找到注册的路由并分发
  void route(PointerEvent event) {
    final Map<PointerRoute, Matrix4> routes = _routeMap[event.pointer];
    final Map<PointerRoute, Matrix4> copiedGlobalRoutes = Map<PointerRoute, Matrix4>.from(_globalRoutes);
    if (routes != null) {
      _dispatchEventToRoutes(
        event,
        routes,
        Map<PointerRoute, Matrix4>.from(routes),
      );
    }
    _dispatchEventToRoutes(event, _globalRoutes, copiedGlobalRoutes);
  }
  
  //路由的分发
  void _dispatchEventToRoutes(
    PointerEvent event,
    Map<PointerRoute, Matrix4> referenceRoutes,
    Map<PointerRoute, Matrix4> copiedRoutes,
  ) {
    copiedRoutes.forEach((PointerRoute route, Matrix4 transform) {
      if (referenceRoutes.containsKey(route)) {
        _dispatch(event, route, transform);
      }
    });
  }
}

在注册路由的时候说过,一个路由对应的是PrimaryPointerGestureRecognizer中的handleEvent函数,所以路由的处理最终就是调用handleEvent函数。也就是说在GestureDetector中,除了Down事件通过Listener获得,其他事件(如moveupcancel等事件)都会通过路由传递到handleEvent函数中。

以上就是GestureDetector的实现原理。由于篇幅原因,一些细节就没有深究,如果感兴趣也阅读以下源码。

可以发现,GestureDetector存在竞技场,而Listener不存在。所以当GestureDetector无法满足需求时,就可以直接使用Listener来实现,但直接使用Listener的难度又会比GestureDetector的难度要大很多,所以需要进行权衡。

由于竞技场的内容比较多,所以可以在Flutter之竞技场(Arena)原理解析一文中了解竞技场的实现原理。

4、总结

flutter的事件处理总体上来说比Android要简单一些,所以也相对而言好理解一些。本文从整体逻辑上分析了flutter的事件处理,希望能帮助大家。

【参考资料】

Pointer事件处理

Flutter中的事件流和手势简析

Flutter完整开发实战详解(十三、全面深入触摸和滑动原理)