Skip to content

Instantly share code, notes, and snippets.

@Nash0x7E2
Last active November 3, 2023 08:56
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save Nash0x7E2/08acca529096d93f3df0f60f9c034056 to your computer and use it in GitHub Desktop.
Save Nash0x7E2/08acca529096d93f3df0f60f9c034056 to your computer and use it in GitHub Desktop.
[DEPRECATED] Sample code on how to enable gesture pass through so that both the parent and the child widget receive the gesture.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
//Main function. The entry point for your Flutter app.
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: DemoApp(),
),
),
);
}
// Simple demo app which consists of two containers. The goal is to allow multiple gestures into the arena.
// Everything is handled manually with the use of `RawGestureDetector` and a custom `GestureRecognizer`(It extends `TapGestureRecognizer`).
// The custom GestureRecognizer, `AllowMultipleGestureRecognizer` is added to the gesture list and creates a `GestureRecognizerFactoryWithHandlers` of type `AllowMultipleGestureRecognizer`.
// It creates a gesture recognizer factory with the given callbacks, in this case, an `onTap`.
// It listens for an instance of `onTap` then prints text to the console when it is called. Note that the `RawGestureDetector` code is the same for both
// containers. The only difference being the text that is printed(Used as a way to identify the widget)
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RawGestureDetector(
gestures: {
AllowMultipleGestureRecognizer: GestureRecognizerFactoryWithHandlers<
AllowMultipleGestureRecognizer>(
() => AllowMultipleGestureRecognizer(),
(AllowMultipleGestureRecognizer instance) {
instance.onTap = () => print('Episode 4 is best! (parent container) ');
},
)
},
behavior: HitTestBehavior.opaque,
//Parent Container
child: Container(
color: Colors.blueAccent,
child: Center(
//Wraps the second container in RawGestureDetector
child: RawGestureDetector(
gestures: {
AllowMultipleGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
AllowMultipleGestureRecognizer>(
() => AllowMultipleGestureRecognizer(), //constructor
(AllowMultipleGestureRecognizer instance) { //initializer
instance.onTap = () => print('Episode 8 is best! (nested container)');
},
)
},
//Creates the nested container within the first.
child: Container(
color: Colors.yellowAccent,
width: 300.0,
height: 400.0,
),
),
),
),
);
}
}
// Custom Gesture Recognizer.
// rejectGesture() is overridden. When a gesture is rejected, this is the function that is called. By default, it disposes of the
// Recognizer and runs clean up. However we modified it so that instead the Recognizer is disposed of, it is actually manually added.
// The result is instead you have one Recognizer winning the Arena, you have two. It is a win-win.
class AllowMultipleGestureRecognizer extends TapGestureRecognizer {
@override
void rejectGesture(int pointer) {
acceptGesture(pointer);
}
}
@mathrocco
Copy link

Awesome, thanks for sharing it! 🚀

@devrajraut
Copy link

Thanks, what if there is a longPress and onTap ?

Yeah I want to know too. I have a pageview which has children with pinch controls and sometimes the pageview takes the pinch gesture and tries to change the page. So I need to resolve for drag and scale gestures

Just Do this:

Change:

class AllowMultipleGestureRecognizer extends TapGestureRecognizer

to

class AllowMultipleGestureRecognizer extends LongPressGestureRecognizer

@jhass
Copy link

jhass commented May 20, 2021

!!! DON'T USE THIS !!!

This causes internal crashes in Flutter and upstream has officially decided this usecase is unsupported and will not provide a fix to those crashes: flutter/flutter#82107 (comment)

@tneotia
Copy link

tneotia commented May 22, 2021

For anyone that may stumble on this, @jhass is correct, do not use this. I used a different approach in flutter_html at #695:

class MultipleTapGestureDetector extends InheritedWidget {
  final void Function()? onTap;

  const MultipleTapGestureDetector({
    Key? key,
    required Widget child,
    required this.onTap,
  }) : super(key: key, child: child);

  static MultipleTapGestureDetector? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MultipleTapGestureDetector>();
  }

  @override
  bool updateShouldNotify(MultipleTapGestureDetector oldWidget) => false;
}

and

MultipleTapGestureDetector(
   onTap: () {
      print("top level tapped"); 
   }
   child: Builder(
      builder: (context) {
         return GestureDetector(
            onTap: () {
               if (MultipleTapGestureDetector.of(context) != null) {
                  MultipleTapGestureDetector.of(context)!.onTap?.call();
               }
               print("bottom level tapped"); 
            },
            child: Container(height: 150, color: Colors.red),
         )
      }
   )
);

This way you can access the nearest parent GestureDetector in the child GestureDetector and call both onTaps.

This doesn't use any weird workarounds, DropdownButtonHideUnderline uses the same approach.

@guitoof
Copy link

guitoof commented Jan 7, 2022

Hi there!

Thank you everyone for sharing that useful information.
I am working on a project where I need to detect any gestures (tap, long-press, drag ...) the user is performing on the screen, without interfering with the UX.
Currently, I am using the approach that @jhass and @tneotia are saying to avoid: Overriding the rejectGesture method
Here is a gist with a simplified version of what I do: https://gist.github.com/guitoof/0cc362984e8d60b51064bca6672c0bb5
It's very similar to the solution proposed here by @Nash0x7E2 but the major difference is this: the code is split in 2:

  • the "parent" widget: in global_gestures_listener.dart
  • the "child" widget: in main.dart
    My project consists in developing the parent widget: global_gestures_listener.dart which I actually develop in a standalone flutter lib.
    The code of the "child" widget is the code written by my clients and any developer that uses my lib on their Flutter app.
    Because of this, I don't own the code for the "child" widget and therefore CANNOT modify its code at all.

This constraint makes it impossible to use the approach using an InheritedWidget as suggested by @tneotia because I won't be able to override the tap methods from EVERY gesture handling widgets of my clients' apps.

Given this constraint, I have 2 questions for you @jhass & @tneotia:

  1. Could you explain to me why overriding the rejectGesture method like I do it is considered "illegal"?
  2. How would you do this in my context: without being able to modify the child widget and without using any illegal code?

Thank you very much for your help.
Let me know if it is not clear enough

@TheManuz
Copy link

Hello everyone,
I'm currently facing the same problem as @guitoof .

I need to log UI events, and send them to server, but changing every callback inside the project is not a good way to handle it.

I initially thought that onTap would generate something like Notifications, that would bubble up to the root (something like HTML DOM click events). Sadly, this assumption wasn't true, and I'm still trying to find a nice solution to listen for every gesture on the app and capture it.

@guitoof, did you find any solution?

@guitoof
Copy link

guitoof commented Mar 18, 2022

Hi @TheManuz ,

I did find a solution to this problem !
The issue actually comes from the fact that in theGesture Arena, only 1 widget can win...

The solution is to use the lower API of the Listener widget.
It requires that you handle directly PointerEvent like DownPointerEvent or UpPointerEvent ... but provided you pass the hitTestBehavior: HitTestBehaviour.transluscent property, you can handle each pointer events without interfering with any underlying widget that needs it too !
The caveat is that, since Listener is a lower level API (using pointerEvents) you will need to re-implement the behavior of higher level api such as Tap, LongPress ...
I had to do just this for my client and got my inspiration from this widget:
https://github.com/taodo2291/xgesture_flutter/blob/master/lib/gesture_x_detector.dart
You can directly use the gesture_x_detector library but I quickly encountered limitations and preferred recoding my own widget inspired by this one.

I hope it helps you. Let me know if you have further questions

@TheManuz
Copy link

Wow, thanks!

I'll give gesture_x_detector a quick try to see if it's suitable for my need, and then I'll evaluate if I need to rewrite my own.

@guitoof
Copy link

guitoof commented Mar 18, 2022

Great, let me know how it goes 😉

@TheManuz
Copy link

TheManuz commented Mar 18, 2022

I've added hitTestBehavior: HitTestBehaviour.transluscent to the XGestureDetector Listener, and finally both events are firing.

Sadly, I've got a problem with the order of firing: apparently, the child fires always before the parent.

My current configuration is to have the parent widget as the one responsible for logging the UI events, and the child is the original button, unchanged.

Since the actual original button triggers a navigation event (also logged), the result is something like this:

  1. child: execute navigation
  2. navigation log event fires
  3. parent: onTap log event fires

so I'm logging that the navigation happened before the click, and this is obviously wrong.

Yay, it works! The order is correct, all these tries confused me!
Now I'm up for some cleaning and it'll be done!

Thank you very much for your help @guitoof

@2p31-1
Copy link

2p31-1 commented Nov 3, 2023

Are there any alternative codes for this?

@guitoof
Copy link

guitoof commented Nov 3, 2023

Hi @2p31-1 ,
To achieve what I wanted, I drilled down to use Flutter's lower level: Listener widget. As @TheManuz said, in this case it fires pretty well.
After struggling with this, I decided to make a talk about it to explain what I've understood from the subject. If you want to take a look, maybe that'll clarify some things for you:
https://www.droidcon.com/2023/08/06/understanding-flutters-handling-of-gestures/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment