likes
comments
collection
share

从源码分析Flutter中的事件分发

作者站长头像
站长
· 阅读数 71

事件的传递与竞技响应

通过如下这段代码,一起来探究一下flutter中的事件分发吧

GestureDetector(
  onTap: () {
    print("tap_red");
  },
  onTapDown: (detail){
    print("tap_red_down");
  },
  onTapUp: (detail){
    print("tap_red_up");
  },
  child: Container(
      color: Colors.red,
      width: 200,
      height: 200,

  ),
)

从源码分析Flutter中的事件分发 首先,我们知道,用户的触摸行为,一定是在原生设备进行(如上图所示),我们的事件分发肯定是从Java层传递到了C++,最终传递至Dart这个过程。在Dart部分,我们注意到经过zone.runUnaryGuarded方法之后会调用到window.onPointDataPacket方法处,查看GestureBinding初始化的过程得知这个方法会执行_handlePointerDataPacket.

从源码分析Flutter中的事件分发 通过断点我们可以也可以看到,执行一次点击事件会执行GestureBinding中的_handlePointerDataPacket->_flushPointerEventQueue->handlePointerEvent->_handlePointerEventImmediately 下面我们具体来看一下_handlePointerEventImmediately中的具体内容


void _handlePointerEventImmediately(PointerEvent event) {
  HitTestResult? hitTestResult;
  if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) {
    assert(!_hitTests.containsKey(event.pointer));
    hitTestResult = HitTestResult();
    hitTest(hitTestResult, event.position);
    if (event is PointerDownEvent) {
      _hitTests[event.pointer] = hitTestResult;
    }
    assert(() {
      if (debugPrintHitTestResults)
        debugPrint('$event: $hitTestResult');
      return true;
    }());
  } else if (event is PointerUpEvent || event is PointerCancelEvent) {
    hitTestResult = _hitTests.remove(event.pointer);
  } else if (event.down) {
    // Because events that occur with the pointer down (like
    // [PointerMoveEvent]s) should be dispatched to the same place that their
    // initial PointerDownEvent was, we want to re-use the path we found when
    // the pointer went down, rather than do hit detection each time we get
    // such an event.
    hitTestResult = _hitTests[event.pointer];
  }
  assert(() {
    if (debugPrintMouseHoverEvents && event is PointerHoverEvent)
      debugPrint('$event');
    return true;
  }());
  if (hitTestResult != null ||
      event is PointerAddedEvent ||
      event is PointerRemovedEvent) {
    assert(event.position != null);
    dispatchEvent(event, hitTestResult);
  }
}

我们知道,一次点击响应可以看做是Down+Up事件,这里首先走的应该是PointerDownEvent,在这里初始化了hitTestResult,然后对执行hitTest方法,来初始化hitTestResult内的target

  • HitTestResult HitTestResult内有一个List< HitTestEntry > _path, HitTestEntry中有一个HitTestTarget target,这个target对象拥有handleEvent的能力.
abstract class HitTestTarget {
  // This class is intended to be used as an interface, and should not be
  // extended directly; this constructor prevents instantiation and extension.
  HitTestTarget._();

  /// Override this method to receive events.
  void handleEvent(PointerEvent event, HitTestEntry entry);
}

hitTest方法

从源码分析Flutter中的事件分发 查看源码,我们看到这个hitTest方法在源码中有这样的关系,按照执行的先后顺序我们来看一下具体的代码

RendererBinding.hitTest
@override
void hitTest(HitTestResult result, Offset position) {
  assert(renderView != null);
  assert(result != null);
  assert(position != null);
  renderView.hitTest(result, position: position);
  super.hitTest(result, position);
}

主要是调用了renderView.hitTest和super.hitTest(GestureBinding.hitTest)

RenderView.hitTest

(RenderView是绘制树的根节点,是所有Widget的祖先)

bool hitTest(HitTestResult result, { required Offset position }) {
  if (child != null)
    child!.hitTest(BoxHitTestResult.wrap(result), position: position);
  result.add(HitTestEntry(this));
  return true;
}

先是执行,child!.hitTest(RenderBox.hitTest),之后将自己加入result

RenderBox.hitTest
bool hitTest(BoxHitTestResult result, { required Offset position }) {
  ...
  if (_size!.contains(position)) {
    if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
      result.add(BoxHitTestEntry(this, position));
      return true;
    }
  }
  return false;
}

先是判断触摸点是否属于widget的size范围,在范围内执行hitTestChildrenhitTestSelf是两个抽象方法(因为Widget可能有一个child或者多个child),查看具体实现其实逻辑和RenderBox.hitTest一样,也是先判断自己是否在这次点击的postion范围内,然后递归调用子Widget的hitTest。观察这个方法结构,我们知道,如果一个Widget越深,则越先被添加进HitTestResult中。这样整个流程执行下来,HitTestResult就得到了这次点击事件坐标上所有能响应的控件集合。RendererBinding.hitTest执行super.hitTest(result, position);,即GestureBinding.hitTest

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

最后把自己添加到HitTestResult的结尾;下一步关键就是对事件进行分发了

dispatchEvent

按照上面的逻辑,首先也是先走RendererBinding.dispathchEvent,event.kind是PointerDeviceKind.touch,就直接调用了GestureBinding.dispatchEvent,我们直接来看

GestureBinding.dispatchEvent
@override // from HitTestDispatcher
@pragma('vm:notify-debugger-on-exception')
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
  ...
  for (final HitTestEntry entry in hitTestResult.path) {
      entry.target.handleEvent(event.transformed(entry.transform), entry);
  }
}

其中最重要的一句是循环调用hitTestResult集合中每一个对象的handleEvent(event.transformed(entry.transform), entry)方法,大部分时候只有RenderPointerListener会处理这个方法,RenderPointerListener被嵌套在RawGestureDetector中。

RenderPointerListener.handleEvent
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
  assert(debugHandleEvent(event, entry));
  if (event is PointerDownEvent)
    return onPointerDown?.call(event);
  if (event is PointerMoveEvent)
    return onPointerMove?.call(event);
  if (event is PointerUpEvent)
    return onPointerUp?.call(event);
  if (event is PointerHoverEvent)
    return onPointerHover?.call(event);
  if (event is PointerCancelEvent)
    return onPointerCancel?.call(event);
  if (event is PointerSignalEvent)
    return onPointerSignal?.call(event);
}

这个方法就是将event按照类型,触发RawGestureDetector中的不同处理。Flutter中几乎所有的手势处理都是这个类的包装(InkWell的结构,在最里层返回的是一个RenderPointerListener),handleEvent会根据不同的事件类型,回调到RawGestureDetector的相关手势处理中。 从源码分析Flutter中的事件分发 对照我们例子的执行过程: 从源码分析Flutter中的事件分发 执行recognizer.addPointer内部方法的是BaseTapGestureRecognizer类,最终会将GestureRecognizer加入到竞技场内参与竞技.HitTestResult中的每一个RawGestureDetector会将自己添加到GestureArenaManager中. 从源码分析Flutter中的事件分发 不要忘了GestureBinding被添加在HitTestResult的最后,GestureBinding也要执行handleEvent。

GestureBinding.handleEvent
@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);
  } else if (event is PointerSignalEvent) {
    pointerSignalResolver.resolve(event);
  }
}

小结

从源码分析Flutter中的事件分发

竞技响应

一个点击事件肯定是从down事件开始,GestureBinding.handleEvent方法时必定会先走

gestureArena.close(event.pointer);

void close(int pointer) {
  final _GestureArena? state = _arenas[pointer];
  if (state == null)
    return; // This arena either never existed or has been resolved.
  state.isOpen = false;
  assert(_debugLogDiagnostic(pointer, 'Closing', state));
  _tryToResolveArena(pointer, state);
}

在本例中,竞技场只有一个竞争者,会直接执行_resolveByDefault方法


void _tryToResolveArena(int pointer, _GestureArena state) {
  if (state.members.length == 1) {
    scheduleMicrotask(() => _resolveByDefault(pointer, state));
  } else if (state.members.isEmpty) {
    _arenas.remove(pointer);
    assert(_debugLogDiagnostic(pointer, 'Arena empty.'));
  } else if (state.eagerWinner != null) {
    assert(_debugLogDiagnostic(pointer, 'Eager winner: ${state.eagerWinner}'));
    _resolveInFavorOf(pointer, state, state.eagerWinner!);
  }
}

void _resolveByDefault(int pointer, _GestureArena state) {
  if (!_arenas.containsKey(pointer))
    return; // Already resolved earlier.
  assert(_arenas[pointer] == state);
  assert(!state.isOpen);
  final List<GestureArenaMember> members = state.members;
  assert(members.length == 1);
  _arenas.remove(pointer);
  assert(_debugLogDiagnostic(pointer, 'Default winner: ${state.members.first}'));
  state.members.first.acceptGesture(pointer);
}

从源码分析Flutter中的事件分发 可以看出,最终执行的是

BaseTapGestureRecognizer.acceptGesture

@override
void acceptGesture(int pointer) {
  super.acceptGesture(pointer);
  if (pointer == primaryPointer) {
    _checkDown();
    _wonArenaForPrimaryPointer = true;
    _checkUp();
  }
}

@override
void rejectGesture(int pointer) {
  super.rejectGesture(pointer);
  if (pointer == primaryPointer) {
    // Another gesture won the arena.
    assert(state != GestureRecognizerState.possible);
    if (_sentTapDown)
      _checkCancel(null, 'forced');
    _reset();
  }
}

在本例中的只有一个竞技者,响应点击为BaseTapGestureRecognizer.acceptGesture(int pointer),会先进行down事件的消费;_checkUp()此时的_up为null,这个_up的会在PrimaryPointerGestureRecognizer.handleEvent的时候调用handlePrimaryPointer赋值,所以即使当竞技场,只有一个竞技者的时候在Down事件也不会被识别为一个完整的点击动作。

void _checkDown() {
  if (_sentTapDown) {
    return;
  }
  handleTapDown(down: _down!);
  _sentTapDown = true;
}

void _checkUp() {
  if (!_wonArenaForPrimaryPointer || _up == null) {
    return;
  }
  assert(_up!.pointer == _down!.pointer);
  handleTapUp(down: _down!, up: _up!);
  _reset();
}

以上,是区域内只有一个竞争者的响应流程,在down事件中就已经决出胜负;up的时候,竞技场已经打扫干净了,不会做任何操作.

如果有多个响应者呢?

GestureDetector(
  onTap: () {
    print("tap_red");
  },
  onTapDown: (detail){
    print("tap_red_down");
  },
  onTapUp: (detail){
    print("tap_red_up");
  },
  child: Container(
      color: Colors.red,
      width: 200,
      height: 200,
      child: Center(
        child: GestureDetector(
          onTap: () {
            print("tap_green");
          },
          onTapDown: (detail){
            print("tap_green_down");
          },
          onTapUp: (detail){
            print("tap_green_up");
          },
          child: Container(
            color: Colors.green,
            width: 100,
            height: 100,
          ),
        ),
      ),
  ),
)

当我点击内部绿色区域的时候,在竞技场内就不只一个响应者了,在down事件中,竞技场就无法直接决出胜负;需要在up事件中sweep竞技场的时候,选择第一个(嵌套结构最内层)member作为响应者acceptGesture,其他memeber.rejectGesture

GestureArenaManager.sweep

void sweep(int pointer) {
  final _GestureArena? state = _arenas[pointer];
  if (state == null)
    return; // This arena either never existed or has been resolved.
  assert(!state.isOpen);
  if (state.isHeld) {
    state.hasPendingSweep = true;
    assert(_debugLogDiagnostic(pointer, 'Delaying sweep', state));
    return; // This arena is being held for a long-lived member.
  }
  assert(_debugLogDiagnostic(pointer, 'Sweeping', state));
  _arenas.remove(pointer);
  if (state.members.isNotEmpty) {
    // First member wins.
    assert(_debugLogDiagnostic(pointer, 'Winner: ${state.members.first}'));
    state.members.first.acceptGesture(pointer);
    // Give all the other members the bad news.
    for (int i = 1; i < state.members.length; i++)
      state.members[i].rejectGesture(pointer);
  }
}

从源码分析Flutter中的事件分发

滑动事件

根据上述流程,我们分析一下滑动事件的传递

ListView.builder(itemBuilder: (context, index) {
  return GestureDetector(
      onTap: () {
        print("tap_red");
      },
      onTapDown: (detail) {
        print("tap_red_down");
      },
      onTapUp: (detail) {
        print("tap_red_up");
      },
      child: Container(
        height: 100,
        child: Text("$index"),
      ));
})

首先,滑动过程是down+move事件

down的执行流程如下

从源码分析Flutter中的事件分发 此时在down的流程中,竞技场内不只有一个竞技者,无法直接决出胜负;

第一次move流程

DragGestureRecognizer.handleEvent

@override
void handleEvent(PointerEvent event) {
  assert(_state != _DragState.ready);
  if (!event.synthesized
      && (event is PointerDownEvent || event is PointerMoveEvent)) {
    final VelocityTracker tracker = _velocityTrackers[event.pointer]!;
    assert(tracker != null);
    tracker.addPosition(event.timeStamp, event.localPosition);
  }

  if (event is PointerMoveEvent) {
    if (event.buttons != _initialButtons) {
      _giveUpPointer(event.pointer);
      return;
    }
    if (_state == _DragState.accepted) {
      _checkUpdate(
        sourceTimeStamp: event.timeStamp,
        delta: _getDeltaForDetails(event.localDelta),
        primaryDelta: _getPrimaryValueFromOffset(event.localDelta),
        globalPosition: event.position,
        localPosition: event.localPosition,
      );
    } else {
      _pendingDragOffset += OffsetPair(local: event.localDelta, global: event.delta);
      _lastPendingEventTimestamp = event.timeStamp;
      _lastTransform = event.transform;
      final Offset movedLocally = _getDeltaForDetails(event.localDelta);
      final Matrix4? localToGlobalTransform = event.transform == null ? null : Matrix4.tryInvert(event.transform!);
      _globalDistanceMoved += PointerEvent.transformDeltaViaPositions(
        transform: localToGlobalTransform,
        untransformedDelta: movedLocally,
        untransformedEndPosition: event.localPosition,
      ).distance * (_getPrimaryValueFromOffset(movedLocally) ?? 1).sign;
      if (_hasSufficientGlobalDistanceToAccept(event.kind))
        resolve(GestureDisposition.accepted);
    }
  }
  if (event is PointerUpEvent || event is PointerCancelEvent) {
    _giveUpPointer(event.pointer);
  }
}

在第一次Move事件阶段,_state == _DragState.accepted肯定为false,走下面的流程,最下方有一个判断_hasSufficientGlobalDistanceToAccept,这个判断里对比了手指在屏幕上的滑动距离,如果大于18逻辑像素则认为是一次滑动,调用resolve(GestureDisposition.accepted),这个方法,最终会调用到GestureArenaManager._resolve中,决出胜利者,执行acceptGesture

从源码分析Flutter中的事件分发 VerticalDragGestureRecognizer._hasSufficientGlobalDistanceToAccept

@override
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind) {
  return _globalDistanceMoved.abs() > computeHitSlop(pointerDeviceKind);//> 18
}

GestureArenaManager._resolve

/// Reject or accept a gesture recognizer.
///
/// This is called by calling [GestureArenaEntry.resolve] on the object returned from [add].
void _resolve(int pointer, GestureArenaMember member, GestureDisposition disposition) {
  final _GestureArena? state = _arenas[pointer];
  if (state == null)
    return; // This arena has already resolved.
  assert(_debugLogDiagnostic(pointer, '${ disposition == GestureDisposition.accepted ? "Accepting" : "Rejecting" }: $member'));
  assert(state.members.contains(member));
  if (disposition == GestureDisposition.rejected) {
    state.members.remove(member);
    member.rejectGesture(pointer);
    if (!state.isOpen)
      _tryToResolveArena(pointer, state);
  } else {
    assert(disposition == GestureDisposition.accepted);
    if (state.isOpen) {
      state.eagerWinner ??= member;
    } else {
      assert(_debugLogDiagnostic(pointer, 'Self-declared winner: $member'));
      _resolveInFavorOf(pointer, state, member);
    }
  }
}

void _resolveInFavorOf(int pointer, _GestureArena state, GestureArenaMember member) {
  assert(state == _arenas[pointer]);
  assert(state != null);
  assert(state.eagerWinner == null || state.eagerWinner == member);
  assert(!state.isOpen);
  _arenas.remove(pointer);
  for (final GestureArenaMember rejectedMember in state.members) {
    if (rejectedMember != member)
      rejectedMember.rejectGesture(pointer);
  }
  member.acceptGesture(pointer);
}

DragGestureRecognizer.acceptGesture

@override
void acceptGesture(int pointer) {
  assert(!_acceptedActivePointers.contains(pointer));
  _acceptedActivePointers.add(pointer);
  if (_state != _DragState.accepted) {
    _state = _DragState.accepted;
    final OffsetPair delta = _pendingDragOffset;
    final Duration timestamp = _lastPendingEventTimestamp!;
    final Matrix4? transform = _lastTransform;
    final Offset localUpdateDelta;
    switch (dragStartBehavior) {
      case DragStartBehavior.start:
        _initialPosition = _initialPosition + delta;
        localUpdateDelta = Offset.zero;
        break;
      case DragStartBehavior.down:
        localUpdateDelta = _getDeltaForDetails(delta.local);
        break;
    }
    _pendingDragOffset = OffsetPair.zero;
    _lastPendingEventTimestamp = null;
    _lastTransform = null;
    _checkStart(timestamp, pointer);
    if (localUpdateDelta != Offset.zero && onUpdate != null) {
      final Matrix4? localToGlobal = transform != null ? Matrix4.tryInvert(transform) : null;
      final Offset correctedLocalPosition = _initialPosition.local + localUpdateDelta;
      final Offset globalUpdateDelta = PointerEvent.transformDeltaViaPositions(
        untransformedEndPosition: correctedLocalPosition,
        untransformedDelta: localUpdateDelta,
        transform: localToGlobal,
      );
      final OffsetPair updateDelta = OffsetPair(local: localUpdateDelta, global: globalUpdateDelta);
      final OffsetPair correctedPosition = _initialPosition + updateDelta; // Only adds delta for down behaviour
      _checkUpdate(
        sourceTimeStamp: timestamp,
        delta: localUpdateDelta,
        primaryDelta: _getPrimaryValueFromOffset(localUpdateDelta),
        globalPosition: correctedPosition.global,
        localPosition: correctedPosition.local,
      );
    }
    // This acceptGesture might have been called only for one pointer, instead
    // of all pointers. Resolve all pointers to `accepted`. This won't cause
    // infinite recursion because an accepted pointer won't be accepted again.
    resolve(GestureDisposition.accepted);
  }
}

其中调用 _checkStart(timestamp)

DragGestureRecognizer._checkStart

void _checkStart(Duration timestamp, int pointer) {
  assert(_initialButtons == kPrimaryButton);
  if (onStart != null) {
    final DragStartDetails details = DragStartDetails(
      sourceTimeStamp: timestamp,
      globalPosition: _initialPosition.global,
      localPosition: _initialPosition.local,
      kind: getKindForPointer(pointer),
    );
    invokeCallback<void>('onStart', () => onStart!(details));
  }
}

这里先封装了一个DragStartDetails对象,之后调用的onStart(details)是外界传入的一个变量。我们返回看Scrollable的 setCanDrag(bool canDrag)方法

Scrollable.setCanDrag


@override
@protected
void setCanDrag(bool canDrag) {
  if (canDrag == _lastCanDrag && (!canDrag || widget.axis == _lastAxisDirection))
    return;
  if (!canDrag) {
    _gestureRecognizers = const <Type, GestureRecognizerFactory>{};
    // Cancel the active hold/drag (if any) because the gesture recognizers
    // will soon be disposed by our RawGestureDetector, and we won't be
    // receiving pointer up events to cancel the hold/drag.
    _handleDragCancel();
  } else {
    switch (widget.axis) {
      case Axis.vertical:
        _gestureRecognizers = <Type, GestureRecognizerFactory>{
          VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
            () => VerticalDragGestureRecognizer(supportedDevices: _configuration.dragDevices),
            (VerticalDragGestureRecognizer instance) {
              instance
                ..onDown = _handleDragDown
                ..onStart = _handleDragStart
                ..onUpdate = _handleDragUpdate
                ..onEnd = _handleDragEnd
                ..onCancel = _handleDragCancel
                ..minFlingDistance = _physics?.minFlingDistance
                ..minFlingVelocity = _physics?.minFlingVelocity
                ..maxFlingVelocity = _physics?.maxFlingVelocity
                ..velocityTrackerBuilder = _configuration.velocityTrackerBuilder(context)
                ..dragStartBehavior = widget.dragStartBehavior;
            },
          ),
        };
        break;
      case Axis.horizontal:
        _gestureRecognizers = <Type, GestureRecognizerFactory>{
          HorizontalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
            () => HorizontalDragGestureRecognizer(supportedDevices: _configuration.dragDevices),
            (HorizontalDragGestureRecognizer instance) {
              instance
                ..onDown = _handleDragDown
                ..onStart = _handleDragStart
                ..onUpdate = _handleDragUpdate
                ..onEnd = _handleDragEnd
                ..onCancel = _handleDragCancel
                ..minFlingDistance = _physics?.minFlingDistance
                ..minFlingVelocity = _physics?.minFlingVelocity
                ..maxFlingVelocity = _physics?.maxFlingVelocity
                ..velocityTrackerBuilder = _configuration.velocityTrackerBuilder(context)
                ..dragStartBehavior = widget.dragStartBehavior;
            },
          ),
        };
        break;
    }
  }
  _lastCanDrag = canDrag;
  _lastAxisDirection = widget.axis;
  if (_gestureDetectorKey.currentState != null)
    _gestureDetectorKey.currentState!.replaceGestureRecognizers(_gestureRecognizers);
}

ScrollableState._handleDragStart

void _handleDragStart(DragStartDetails details) {
  // It's possible for _hold to become null between _handleDragDown and
  // _handleDragStart, for example if some user code calls jumpTo or otherwise
  // triggers a new activity to begin.
  assert(_drag == null);
  _drag = position.drag(details, _disposeDrag);
  assert(_drag != null);
  assert(_hold == null);
}

这里的position就是ScrollPositionWithSingleContext

ScrollPositionWithSingleContext.drag

@override
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
  final ScrollDragController drag = ScrollDragController(
    delegate: this,
    details: details,
    onDragCanceled: dragCancelCallback,
    carriedVelocity: physics.carriedMomentum(_heldPreviousVelocity),
    motionStartDistanceThreshold: physics.dragStartDistanceMotionThreshold,
  );
  beginActivity(DragScrollActivity(this, drag));
  assert(_currentDrag == null);
  _currentDrag = drag;
  return drag;
}

beginActivity里面主要就是发起滚动的通知UserScrollNotification 从源码分析Flutter中的事件分发

后续move

从源码分析Flutter中的事件分发

ScrollableState._handleDragUpdate

void _handleDragUpdate(DragUpdateDetails details) {
  // _drag might be null if the drag activity ended and called _disposeDrag.
  assert(_hold == null || _drag == null);
  _drag?.update(details);
}

这里的_drag是ScrollDragController

ScrollDragController.update

@override
void update(DragUpdateDetails details) {
  assert(details.primaryDelta != null);
  _lastDetails = details;
  double offset = details.primaryDelta!;
  if (offset != 0.0) {
    _lastNonStationaryTimestamp = details.sourceTimeStamp;
  }
  // By default, iOS platforms carries momentum and has a start threshold
  // (configured in [BouncingScrollPhysics]). The 2 operations below are
  // no-ops on Android.
  _maybeLoseMomentum(offset, details.sourceTimeStamp);
  offset = _adjustForScrollStartThreshold(offset, details.sourceTimeStamp);
  if (offset == 0.0) {
    return;
  }
  if (_reversed) // e.g. an AxisDirection.up scrollable
    offset = -offset;
  delegate.applyUserOffset(offset);
}

这里会调用 delegate.applyUserOffset(offset),这个delegate是个ScrollActivityDelegate接口,主要实现类是ScrollPositionWithSingleContext.

ScrollPositionWithSingleContext.applyUserOffset

@override
void applyUserOffset(double delta) {
  updateUserScrollDirection(delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse);
  setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta));
}

updateUserScrollDirection会发起一个滑动方向的通知UserScrollNotification; setPixels会发起一个ScrollUpdateNotification通知并且调用notifyListeners(),通知Viewport更新偏移量已经我们自己添加到ScrollController中的方法 从源码分析Flutter中的事件分发

至此,滑动的整个流程,我们也都了解了

总结

本文仅以实例直观的分析了整个流程,作为自己的学习记录,对您有帮助的话,欢迎点点小心心~

转载自:https://juejin.cn/post/7021442906246348813
评论
请登录