Skip to content

Add more explanations of HitTestBehavior #18450

Closed

Description

@fralways

Internal: b/155674981

When I use GestureDetector and put behavior = translucent, I cannot tap on anything bellow it. Also when I use GestureDetector and put behavior lets say opaque and have RawMaterialButton as a child, taps on button are registered on Button but not on GestureDetector and expected behavior in this case would be that taps do not pass to the child.
Example:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Stack(
        children: <Widget>[
          new Positioned(
            left: 0.0,
            right: 0.0,
            top: 200.0,
            bottom: 200.0,
            child: new TextField(
              textAlign: TextAlign.center,
              style: const TextStyle(
                color: Colors.blue,
              ),
            ),
          ),
          new Positioned.fill(
            child: new GestureDetector(
              behavior: HitTestBehavior.translucent,
              onTap: (){
                print("Clicked gesture");
              },
            ),
          ),

//          new Positioned.fill(
//            child: new GestureDetector(
//              behavior: HitTestBehavior.opaque,
//              onTap: (){
//                print("Clicked gesture");
//              },
//              child: new RawMaterialButton(
//                child: new Text(
//                  'BUTTON',
//                  textAlign: TextAlign.center,
//                ),
//                onPressed: (){
//                  print("Clicked button");
//                },
//              ),
//            ),
//          ),
        ],
      ),
    );
  }
}

In this example, I would expect that GestureDetector receives tap input, but also that TextField gets focus, but what happens is that taps are registered in GestureDetector, but nothing passes to the TextField. If I comment out GestureDetector part, TextField works.
Further, you can comment out first two widgets in stack and uncomment last one to check other issue- when GestureDetector has child. I would expect that no tap is passed to child but child receives input and GestureDetector does not.

flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, v0.5.5-pre.10, on Mac OS X 10.13.4 17E202, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 9.4)
[✓] Android Studio (version 3.1)
[!] IntelliJ IDEA Ultimate Edition (version 2018.1.2)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] Connected devices (2 available)

Activity

  1. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Jun 14, 2018
  2. zoechi commented on Jun 14, 2018

    @zoechi
    Contributor
  3. changed the title [-]GestureRecognizer doesn't handle HitTestBehavior correctly[/-] [+]GestureDetector doesn't handle HitTestBehavior correctly[/+] on Jun 14, 2018
  4. Hixie commented on Jun 14, 2018

    @Hixie
    Contributor

    opaque just means the entire size of the gesture detector is the hit region, it doesn't stop the child from being hit. To block hits on children use an IgnorePointer or AbsorbPointer.

    translucent means that the hit testing will traverse below. However, if both widgets are listening for the same gesture, in this case Tap, only one will "win". You can tell that both are getting the pointers though by listening to onTapDown instead of onTap.

  5. added this to the milestone on Jun 14, 2018
  6. fralways commented on Jun 14, 2018

    @fralways
    Author

    Oh my, I completely misunderstood this. I guess adding those comments to the HitTestBehavior enum description would prevent other people from misunderstanding as well. I'm wondering now how can we register same event on multiple widgets?

  7. Hixie commented on Jun 14, 2018

    @Hixie
    Contributor

    Use a PointerListener instead of a GestureDetector.

  8. changed the title [-]GestureDetector doesn't handle HitTestBehavior correctly[/-] [+]Add more explanations of HitTestBehavior[/+] on Jun 14, 2018
  9. zoechi commented on Jun 15, 2018

    @zoechi
    Contributor

    @Hixie do you mean https://docs.flutter.io/flutter/rendering/RenderPointerListener-class.html?
    Flutter.io doesn't find anything with PointerListener

  10. Hixie commented on Jun 18, 2018

    @Hixie
    Contributor

    Sorry it's just called Listener at the widget level. (It's the one that creates a RenderPointerListener.)

  11. kaustavjaiswal commented on Aug 4, 2018

    @kaustavjaiswal

    @Hixie I am facing the same issue, I have a Stack widget on the main app screen and the last widget on the stack intercepts all the onTap calls, any example on how to make the onTap reach the lower widgets, positioned lower in the stack would be extremely helpful.

  12. grigy commented on Aug 30, 2018

    @grigy

    I have a similar issue. I want to wrap the child of a Dismissible widget with a GuestureDetector so that I can catch the horizontal swipe events and add additional functionality. But when I use GestureDetector the Dismissible stops to respond to swipe actions.

  13. wwwdata commented on Oct 9, 2018

    @wwwdata

    @kaustavjaiswal did you find a solution for this problem? Also have this issue.. The only solution for me is to put things that need gestures on top, so at the end of the stack list.

  14. kaustavjaiswal commented on Oct 9, 2018

    @kaustavjaiswal

    @wwwdata I am doing the same thing today.

    https://flutter.io/gestures/

    The gesture disambiguation section here helps to understand how flutter is treating these gestures.

  15. 26 remaining items

  16. removed this from the milestone on Aug 17, 2020
  17. alexeyselina commented on Nov 12, 2020

    @alexeyselina

    Does somebody know how to fix StackWithAllChildrenReceiveEvents class?
    I'm totally confused with overriding of
    'bool hitTestChildren(BoxHitTestResult result, { required Offset position }) '

    When I'm trying to overload it offers me this method:
    @override bool hitTestChildren(BoxHitTestResult result, {Offset}) { // TODO: implement hitTestChildren return super.hitTestChildren(result, Offset); }

    I have 2.10.3 Dart version but the syntax causes compilation issues.

  18. xJon commented on Nov 12, 2020

    @xJon

    @alexeyselina This is how I have it:

    @override
      bool hitTestChildren(HitTestResult result, {Offset position}) {
        return allCdefaultHitTestChildren(result, position: position);
      }
  19. h65wang commented on Dec 30, 2020

    @h65wang

    FWIW, this example correctly showcases all 3 HitTestBehaviors, remember to long press when testing:

    Stack(
      children: <Widget>[
        GestureDetector(
          onTapDown: (_) => print('Bottom Container'),
          child: Container(width: 200, height: 200, color: Colors.blue),
        ),
        GestureDetector(
          /* change the behavior property results in 3 different behaviors:
             (try long pressing on the blue square when running the app)
    
           * translucent: gestures pass through, so both events will trigger.
    
           * opaque: top (this container) blocks the bottom container,
             so only long press event on this container is triggered.
    
           * deferToChild: this child (transparent container) won't accept clicks,
             so only the bottom container will be triggered. */
          behavior: HitTestBehavior.translucent,
          onLongPressStart: (_) => print('Long press of top container!'),
          child: Container(width: 200, height: 200),
        ),
      ],
    )
    
  20. allComputableThings commented on Dec 30, 2020

    @allComputableThings

    FWIW, this example correctly showcases all 3 HitTestBehaviors, remember to long press when testing:

    Yes... works for Stacks, but not for nested widgets, which I would think is the very common cases.

  21. allComputableThings commented on Jan 6, 2021

    @allComputableThings

    A simple workaround to the bug of duplicate events when mixing GeatureRecognizer and Listener in parent child relationships is to wrap the Listener with an additional

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Welcome to Flutter',
          home: Scaffold(
            appBar: AppBar(
              title: Text('Welcome to Flutter'),
            ),
            body: Center(
                child: Column(children: [
              // Parent-child -- two events when red is tapped
              GestureDetector(
                onTap: () {
                  print("Green");
                },
                child: Container(
                  color: Colors.green,
                  width: 100.0,
                  height: 100.0,
                  child: Center(
                    child: GestureDetector(    //  <<<<< Add this junk
                      onTap: () {},                    //  <<<<< Add this junk
                      child: Listener(
                        onPointerDown: (_) {
                          print("Red");
                        },
                        child: Container(
                          color: Colors.red,
                          width: 50.0,
                          height: 50.0,
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ])),
          ),
        );
      }
    }
    

    Thanks to: https://stackoverflow.com/questions/65572920/how-to-avoid-duplicate-events-when-combining-gesturedetector-and-listener-in-flu

  22. giorgio79 commented on Feb 1, 2021

    @giorgio79

    Testing StackWithAllChildrenReceiveEvents
    I notice overflow is getting an error, saying the setter is not defined using flutter stable channel

    flutter doctor -v
    [√] Flutter (Channel stable, 1.22.6, on Microsoft Windows [Version 10.0.16299.2166], locale hu-HU)
        • Flutter version 1.22.6 at C:\flutter
        • Framework revision 9b2d32b605 (10 days ago), 2021-01-22 14:36:39 -0800
        • Engine revision 2f0af37152
        • Dart version 2.10.5
    
    
    [√] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
        • Android SDK at C:\Users\gchityil\AppData\Local\Android\Sdk
        • Platform android-30, build-tools 30.0.2
        • Java binary at: C:\Program Files\Android\Android Studio1\jre\bin\java
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
        • All Android licenses accepted.
    
    
    
  23. giorgio79 commented on Feb 1, 2021

    @giorgio79

    I completed testing StackWithAllChildrenReceiveEvents for the issue with sample at #75086 (comment)

    The bottom child in the Stack is now receiving the events, so all looks good! Thanks a lot!! :)
    I had to remove the EagerGestureRecognizer from the bottom child, GoogleMap, since with that none of the children above the GoogleMap were getting gestures. Without EagerGestureRecognizer it works like magic.

  24. Hixie commented on Feb 3, 2021

    @Hixie
    Contributor

    Closing in preference to #74733 which has a start to text and diagrams we could add.

  25. github-actions commented on Aug 6, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  26. locked as resolved and limited conversation to collaborators on Aug 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: proposalA detailed proposal for a change to Flutterd: api docsIssues with https://api.flutter.dev/frameworkflutter/packages/flutter repository. See also f: labels.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions