Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested GestureDetector OnTap Functions

I have nested GestureDetetors but problem is that only the child GestureDetector's onTap is executed. I do not wish to override the child's onTap, rather, I want both the parent's and the child's onTap to execute. Here's my code:

    GestureDetector(
        onTap: () {
            todo1(); 
        },
        child: GestureDetector(
            onTap: () {
                todo2();
            },
            child: Text("Nested Gesture")))

How do I change this to call both todo1() and todo2() onTap?

edit: the child is a re-usable custom widget that has its own implementation but is now being used by the parent which also has an implementation of its own in addition to its child

like image 434
aLL Avatar asked May 16 '19 10:05

aLL


2 Answers

I made a quick custom gesture recognizer - it cancels the gesture only if user has moved too far from the initial tap point.

Usage example:

UniversalTapHandler(
  onTap: () {
    print("Tap 1");
  },
  child: UniversalTapHandler(
    onTap: () {
      print("Tap 2");
    },
    child: Text("Nested Gesture"),
  )
)

Source code:

class UniversalTapHandler extends RawGestureDetector {
  UniversalTapHandler({
    @required GestureTapCallback onTap,
    @required Widget child,
  }):
    super(
      gestures: <Type, GestureRecognizerFactory>{
        _UniversalPointerHandler: GestureRecognizerFactoryWithHandlers<_UniversalPointerHandler>(
          () => _UniversalPointerHandler(onTap: onTap),
          (_) {},
        ),
      },
      child: child,
    );
}

class _UniversalPointerHandler extends OneSequenceGestureRecognizer {
  _UniversalPointerHandler({
    @required this.onTap,
  }): super();

  final GestureTapCallback onTap;

  final _maxDistance = 18; // as in official recognizer by default
  Offset _startPosition;

  void _reset() {
    _startPosition = null;
  }

  @override
  void addPointer(PointerDownEvent event) {
    _startPosition = event.position;
    startTrackingPointer(event.pointer);
    resolve(GestureDisposition.accepted);
  }

  @override
  void handleEvent(PointerEvent event) {
    if (event is PointerUpEvent) {
      stopTrackingPointer(event.pointer);
      if (_startPosition != null) {
        onTap();
      }
    }
    if (event is PointerMoveEvent && _startPosition != null) {
      if ((event.position - _startPosition).distance > _maxDistance) {
        rejectGesture(event.pointer);
        _reset();
      }
    }
    if (event is PointerCancelEvent || event is PointerExitEvent || event is PointerRemovedEvent) {
      _reset();
    }
  }

  @override
  void resolve(GestureDisposition disposition) {
    if (disposition == GestureDisposition.rejected) {
      _reset();
    }
    super.resolve(disposition);
  }

  @override
  void didStopTrackingLastPointer(int pointer) {}

  @override
  String get debugDescription => "_UniversalPointerHandler: Custom Gesture Recognizer";
}

UPDATE Don't forget to import this:

import 'package:flutter/gestures.dart';
like image 200
George Avatar answered Sep 17 '22 13:09

George


You can call todo1() in the nested onTap callback:

  GestureDetector(
        onTap: () {
            todo1(); 
        },
        child: GestureDetector(
            onTap: () {
                todo2();
                todo1();
            },
            child: Text("Nested Gesture")))
like image 25
chesszhang Avatar answered Sep 19 '22 13:09

chesszhang