一尘不染

将水平滚动与WebView结合使用时的滚动优先级

flutter

我有一个WebView水平滚动里面的垂直滚动PageView。像这样:

PageView.builder(
  itemCount: 5,
  itemBuilder: (context, index) {
    return WebView(
      initialUrl: 'https://flutter.dev/docs',
      gestureRecognizers: [
        Factory(() => VerticalDragGestureRecognizer()),
      ].toSet(),
    );
  },
);

在Flutter(1.5.4)的先前稳定版本中,此操作按预期工作-垂直滚动将在WebView中移动内容,水平滚动将在PageView中移动。

升级到Flutter后,此操作中断了v1.7.8+hotfix.3。现在,即使手势几乎完全是垂直的,水平滚动似乎总是可以取胜的。如果页面完全垂直滚动,则仅在手势停止之后(即,当我在手势之后停止触摸屏幕时)-在手势进行时不会进行垂直滚动。

添加和删除VerticalDragGestureRecognizergestureRecognizers没有任何效果现在-
无论哪种方式,程序,工作起来就像识别器不在名单上(尽管这并不是说gestureRecognizers完全忽略,因为加入EagerGestureRecognizer确实有效果)。

这是手势竞技场的调试输出(请记住,我试图使手势尽可能保持垂直,但是即使HorizontalDragGestureRecognizer我也垂直移动了手势,即使向两侧轻微的手指移动也足以赢得胜利。全部时间):

I/flutter (30125): Gesture arena 14   ❙ ★ Opening new gesture arena.
I/flutter (30125): Gesture arena 14   ❙ Adding: Instance of '_CombiningGestureArenaMember'
I/flutter (30125): Gesture arena 14   ❙ Adding: LongPressGestureRecognizer#9cad1(debugOwner: GestureDetector, state: ready)
I/flutter (30125): Gesture arena 14   ❙ Adding: HorizontalDragGestureRecognizer#69b8f(start behavior: start)
I/flutter (30125): Gesture arena 14   ❙ Closing with 3 members.
I/flutter (30125): Gesture arena 14   ❙ Rejecting: LongPressGestureRecognizer#9cad1(debugOwner: GestureDetector, state: possible)
I/flutter (30125): Gesture arena 14   ❙ Accepting: HorizontalDragGestureRecognizer#69b8f(start behavior: start)
I/flutter (30125): Gesture arena 14   ❙ Self-declared winner: HorizontalDragGestureRecognizer#69b8f(start behavior: start)

这就是当您在拖动手势进行时设法保持手势完全垂直(似乎在使用鼠标的模拟器上更容易)时发生的情况:

flutter: Gesture arena 30   ❙ ★ Opening new gesture arena.
flutter: Gesture arena 30   ❙ Adding: Instance of '_CombiningGestureArenaMember'
flutter: Gesture arena 30   ❙ Adding: HorizontalDragGestureRecognizer#11e7f(start behavior: down)
flutter: Gesture arena 30   ❙ Closing with 2 members.

即使是轻微的垂直移动也可以使HorizontalDragGestureRecognizer宣布获胜,但是VerticalDragGestureRecognizer(似乎包裹在中_CombiningGestureArenaMember)永远不会取得胜利。这似乎在事实上被完全忽略-
手势舞台输出,VerticalDragGestureRecognizergestureRecognizers没有它是完全一致的。

这可能是Flutter中的错误,因此我还在Flutter的GitHub上创建了一个问题。但是,无论哪种方式-
我如何在当前版本的Flutter中达到这种效果?任何解决方法或规范解决方案将不胜感激。


阅读 395

收藏
2020-08-13

共1个答案

一尘不染

竞技场的规则似乎已经改变。现在,竞技场宣布具有主动接收器的手势将获胜。确实,这甚至可以进一步增加手势的响应速度。但是,由于本机视图不声明手势,仅当没有其他活动的检测器/接收器声明手势时才使用手势,因此我怀疑垂直拖动甚至不会从WebView作为手势进入竞技场。这就是为什么任何轻微的水平拖动都会导致水平拖动手势获胜的原因-
因为根本没有其他小部件可以要求任何手势。

您可以扩展VerticalDragGestureRecognizer,使其接受手势:

class PlatformViewVerticalGestureRecognizer
    extends VerticalDragGestureRecognizer {
  PlatformViewVerticalGestureRecognizer({PointerDeviceKind kind})
      : super(kind: kind);

  Offset _dragDistance = Offset.zero;

  @override
  void addPointer(PointerEvent event) {
    startTrackingPointer(event.pointer);
  }

  @override
  void handleEvent(PointerEvent event) {
    _dragDistance = _dragDistance + event.delta;
    if (event is PointerMoveEvent) {
      final double dy = _dragDistance.dy.abs();
      final double dx = _dragDistance.dx.abs();

      if (dy > dx && dy > kTouchSlop) {
        // vertical drag - accept
        resolve(GestureDisposition.accepted);
        _dragDistance = Offset.zero;
      } else if (dx > kTouchSlop && dx > dy) {
        // horizontal drag - stop tracking
        stopTrackingPointer(event.pointer);
        _dragDistance = Offset.zero;
      }
    }
  }

  @override
  String get debugDescription => 'horizontal drag (platform view)';

  @override
  void didStopTrackingLastPointer(int pointer) {}
}

之后,您可以在中使用新类gestureRecognizers

PageView.builder(
  itemCount: 5,
  itemBuilder: (context, index) {
    return WebView(
      initialUrl: 'https://flutter.dev/docs',
      gestureRecognizers: [
        Factory(() => PlatformViewVerticalGestureRecognizer()),
      ].toSet(),
    );
  },
);
2020-08-13