Flutter : How to control conflicting gestures?

Viewed 433

Currently I use a photoview(similar to flutter's interactive viewer) widget inside a column with Customscrollview. So basically I have an app link instagram feed. However the pinch zoom functionality is a little buggy because the pinch gestures are sometimes taken by the customscrollview for scrolling.

So what I need is , Whenever there is a "multitouch" I need PhotoViewGestureRecognizer to win the arena against VerticalDragGestureRecognizer.

Here is my gesture debug logging :

I/flutter (12956): Gesture arena 12   ❙ Adding: PhotoViewGestureRecognizer#6bdab(debugOwner: PhotoViewGestureDetector)
I/flutter (12956): Gesture arena 12   ❙ Adding: DoubleTapGestureRecognizer#6e985(debugOwner: PhotoViewGestureDetector)
I/flutter (12956): Gesture arena 12   ❙ Adding: VerticalDragGestureRecognizer#b7e88(start behavior: down)
I/flutter (12956): Gesture arena 12   ❙ Adding: HorizontalDragGestureRecognizer#d6b73(start behavior: down)
I/flutter (12956): Gesture arena 12   ❙ Closing with 4 members.
I/flutter (12956): Gesture arena 12   ❙ Rejecting: DoubleTapGestureRecognizer#6e985(debugOwner: PhotoViewGestureDetector)
I/flutter (12956): Gesture arena 11   ❙ Accepting: VerticalDragGestureRecognizer#b7e88(start behavior: down)
I/flutter (12956): Gesture arena 11   ❙ Self-declared winner: VerticalDragGestureRecognizer#b7e88(start behavior: down)
I/flutter (12956): Gesture arena 12   ❙ Accepting: VerticalDragGestureRecognizer#b7e88(start behavior: down)
I/flutter (12956): Gesture arena 12   ❙ Self-declared winner: VerticalDragGestureRecognizer#b7e88(start behavior: down)


1 Answers

I solved the problem by using this class

import 'package:flutter/gestures.dart';

class TouchCountRecognizer extends OneSequenceGestureRecognizer {
  TouchCountRecognizer(this.onMultiTouchUpdated);
  Function(bool) onMultiTouchUpdated;
  int touchcount = 0;

  @override
  void addPointer(PointerDownEvent event) {
    startTrackingPointer(event.pointer);
    if (touchcount < 1) {
      //resolve(GestureDisposition.rejected);
      //_p = event.pointer;

      onMultiTouchUpdated(false);
    } else {
      onMultiTouchUpdated(true);
      //resolve(GestureDisposition.accepted);
    }
    touchcount++;
  }

  @override
  String get debugDescription => 'touch count recognizer';

  @override
  void didStopTrackingLastPointer(int pointer) {}

  @override
  void handleEvent(PointerEvent event) {
    if (!event.down) {
      touchcount--;
      if (touchcount < 1) {
        onMultiTouchUpdated(false);
      }
    }
  }
}

then wrap your custom scroll view like this:

RawGestureDetector(
            gestures: <Type, GestureRecognizerFactory>{
              TouchCountRecognizer:
                  GestureRecognizerFactoryWithHandlers<TouchCountRecognizer>(
                () => TouchCountRecognizer(this.onMultiTouchUpdated),
                (TouchCountRecognizer instance) {},
              ),
            },
            child: NotificationListener(
              // ignore: missing_return
              onNotification: (notification) {
                if (notification is ScrollStartNotification) {
                  _scrolling = true;
                }
                if (notification is ScrollUpdateNotification) {}
                if (notification is ScrollEndNotification) {
                  _scrolling = false;
                }
              },
              child: CustomScrollView(physics: _scrollPhysics,......)

then have the multitouch callback something like this :

 onMultiTouchUpdated(bool status) {
    _multiTouch = status;
    //setState(() {});
  }

define scroll physics conditionally :

 if (!_scrolling && _multiTouch) {
      _scrollPhysics = NeverScrollableScrollPhysics();
    } else {
      _scrollPhysics = AlwaysScrollableScrollPhysics();
    }

Basically what we are trying to do is detect whether the user is trying to do a multitouch or a single touch and accordingly give preference to pinch or scroll action.

Related