aLL
aLL

Reputation: 1726

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

Upvotes: 13

Views: 5955

Answers (4)

EvertvdBraak
EvertvdBraak

Reputation: 1083

The issue is mentioned in the official docs 🤓 - https://api.flutter.dev/flutter/widgets/GestureDetector-class.html#widgets.GestureDetector.3

If you have two competing gesture detectors the nested one wins even though set to translucent, implement the onTapDown on the parent detector to still receive the tap event.

 GestureDetector(
        onTapDown: (_) => todo1(), // this should do the trick
        child: GestureDetector(
            onTap: () {
                todo2();
            },
            child: Text("Nested Gesture")))

Upvotes: 0

Brendan
Brendan

Reputation: 1167

If you want both the parent’s and child’s onTap methods to execute, you can use the following code snippet:

GestureDetector(
  onTap: () {
    // parent onTap method
  },
  child: GestureDetector(
    onTap: () {
      // child onTap method
    },
    behavior: HitTestBehavior.translucent,
    child: Container(),
  ),
);

The behavior property is set to HitTestBehavior.translucent, which allows both the parent and child widgets to receive taps.

Upvotes: -1

chesszhang
chesszhang

Reputation: 201

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

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

Upvotes: -2

Egor
Egor

Reputation: 10354

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';

Upvotes: 4

Related Questions