在Flutter 1.7.8中将PageView与WebView结合使用时的滚动优先级

时间:2019-07-17 06:27:20

标签: flutter flutter-plugin

我在水平滚动PageView中有一个垂直滚动WebView。像这样:

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内)从不要求胜利。实际上,似乎似乎完全忽略了它-在VerticalDragGestureRecognizer中使用gestureRecognizers的手势竞技场输出,如果没有,则完全相同。

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

3 个答案:

答案 0 :(得分:1)

我升级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窗口小部件

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(//...

答案 1 :(得分:1)

竞技场的规则似乎已更改,现在竞技场将宣布具有主动接收器的手势的胜利,这确实增加了手势的响应能力,但< / strong>,因为本机视图不声明手势,仅在没有其他活动检测器/接收器声明手势时才使用手势,所以我什至怀疑垂直拖动不会作为从WebView手势进入竞技场的原因,这就是为什么水平拖动将宣布水平拖动手势为胜利,因为根本没有其他小部件要求任何手势。

您可以扩展VerticalDragGestureRecognizer,这是相同的想法,但对我来说效果很好。

代码:

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

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

  @override
  void handleEvent(PointerEvent event) {
    if (event is PointerMoveEvent){
      if (event.delta.dy.abs()>event.delta.dx.abs()){
        resolve(GestureDisposition.accepted);
      } else {
        stopTrackingPointer(event.pointer);
      }
    }
  }

  @override
  String get debugDescription => 'customPan';

  @override
  void didStopTrackingLastPointer(int pointer) {}


}

答案 2 :(得分:-1)

Horizontal ListView inside a Vertical ScrollView in Flutter

他们在这里所说的解决方案是整个页面都是可滚动的,然后他们内部只有水平的列表视图,可能对此有用。 Havent对其进行了尝试,但是它在外观和听起来与您的用例高度相关。

希望它能有所帮助。

enter image description here