12

我在水平滚动中进行了垂直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。现在水平滚动似乎总是赢,即使手势非常明显几乎完全垂直。如果页面完全垂直滚动,那么只有在手势停止之后(即,当我在一个手势后停止触摸屏幕时) - 手势发生时没有垂直滚动。

添加和删​​除VerticalDragGestureRecognizer现在gestureRecognizers没有效果 - 无论哪种方式,程序都像识别器不在列表中一样工作(尽管它不是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)从来没有宣布胜利。事实上,它似乎完全被忽略了——带有VerticalDragGestureRecognizeringestureRecognizers和没有它的手势 Arena 输出是完全一样的。

这可能是 Flutter 中的一个错误,所以我还在Flutter 的 GitHub 上创建了一个问题。但无论哪种方式 - 如何使用当前版本的 Flutter 实现这种效果?任何解决方法或规范的解决方案都将受到高度赞赏。

4

2 回答 2

30

似乎竞技场的规则发生了变化。现在,竞技场宣布具有活动接收器的手势获胜。这确实进一步增加了手势的响应能力。但是,由于本机视图不声明手势并且仅在没有其他活动检测器/接收器声明它们时才使用它们,我怀疑垂直拖动甚至没有作为 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(),
    );
  },
);
于 2019-07-22T17:23:03.833 回答
2

我升级了我的 sdk 只是为了遇到你描述的这个问题。这个问题太烦人了,我想出了这个相当丑陋的黑客。

CustomGestureRecognizer当事件发生在中间(通常是我们滚动的地方)时,这将忽略不需要的行为。这确实带有一些我相信可以处理的过度滚动阴影,可能需要更多时间就是这样。

class CustomGestureRecognizer extends OneSequenceGestureRecognizer {
  double maxScreenOffsetX;
  final double edgeMargin = 20.0;

  CustomGestureRecognizer({@required this.maxScreenOffsetX});

  @override
  void addAllowedPointer(PointerDownEvent event) {

    print("CustomGestureRecognizer: Screen Width: "+ maxScreenOffsetX.toString());
    print("CustomGestureRecognizer: dx: "+event.position.dx.toString());

    if (event.position.dx < edgeMargin || event.position.dx > (maxScreenOffsetX - edgeMargin)) {
      print("CustomGestureRecognizer: At the Edge.");
      return;
    }
    print("CustomGestureRecognizer: Inside Safe Zone");
    startTrackingPointer(event.pointer, event.transform);
    resolve(GestureDisposition.accepted);
    stopTrackingPointer(event.pointer);
  }

页面视图小部件

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

屏幕宽度

  @override
  Widget build(BuildContext context) {
    screenWidth = MediaQuery.of(context).size.width;
    return Scaffold(//...
于 2019-07-22T11:13:53.980 回答