How do I prevent onTapDown from being triggered on a parent widgets GestureDetector?











up vote
1
down vote

favorite












I have a Stack in which several widget can be dragged around. In addition, the container that the Stack is in has a GestureDetector to trigger on onTapDown and onTapUp. I want those onTap events only to be triggered when the user taps outside of the widget in the Stack. I've tried the following code:



class Gestures extends StatefulWidget {
@override
State<StatefulWidget> createState() => _GesturesState();
}

class _GesturesState extends State<Gestures> {
Color background;
Offset pos;

@override
void initState() {
super.initState();
pos = Offset(10.0, 10.0);
}

@override
Widget build(BuildContext context) => GestureDetector(
onTapDown: (_) => setState(() => background = Colors.green),
onTapUp: (_) => setState(() => background = Colors.grey),
onTapCancel: () => setState(() => background = Colors.grey),
child: Container(
color: background,
child: Stack(
children: <Widget>[
Positioned(
top: pos.dy,
left: pos.dx,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onPanUpdate: _onPanUpdate,
// onTapDown: (_) {}, Doesn't affect the problem
child: Container(
width: 30.0,
height: 30.0,
color: Colors.red,
),
),
)
],
),
),
);

void _onPanUpdate(DragUpdateDetails details) {
RenderBox renderBox = context.findRenderObject();
setState(() {
pos = renderBox.globalToLocal(details.globalPosition);
});
}
}


However, when starting to drag the widget, the onTap of the outermost container is triggered as well, making the background momentarily go green in this case. Settings HitTestBehavior.opaque doesn't seem to work like I'd expect. Neither does adding a handler for onTapDown to the widget in the Stack.



So, how do I prevent onTapDown from being triggered on the outermost GestureDetector when the user interacts with the widget inside of the Stack?



Update:



An even simpler example of the problem I'm encountering:



GestureDetector(
onTapDown: (_) {
print("Green");
},
child: Container(
color: Colors.green,
width: 300.0,
height: 300.0,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) {
print("Red");
},
child: Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
),
),
),
);


When I tap and hold the red container, both "Red" and "Green" are printed even though the inner GestureDetector has HitTestBehavior.opaque.










share|improve this question
























  • I don't know if it can help, but cuold you try chanhe outer GestureDetector to Listener?
    – Andrey Turkovsky
    Nov 9 at 20:01










  • @AndreyTurkovsky I'm not sure if I understand what you mean. Just replacing the outer detector with a Listener and using onPointerDown etc. like I used onTapDown does fix the problem. (This is a reduced example, in the real code the outer GestureDetector is further up the stack in a different Widget. So I'd prefer no to change that GestureDetector if possible)
    – spkersten
    Nov 9 at 20:21

















up vote
1
down vote

favorite












I have a Stack in which several widget can be dragged around. In addition, the container that the Stack is in has a GestureDetector to trigger on onTapDown and onTapUp. I want those onTap events only to be triggered when the user taps outside of the widget in the Stack. I've tried the following code:



class Gestures extends StatefulWidget {
@override
State<StatefulWidget> createState() => _GesturesState();
}

class _GesturesState extends State<Gestures> {
Color background;
Offset pos;

@override
void initState() {
super.initState();
pos = Offset(10.0, 10.0);
}

@override
Widget build(BuildContext context) => GestureDetector(
onTapDown: (_) => setState(() => background = Colors.green),
onTapUp: (_) => setState(() => background = Colors.grey),
onTapCancel: () => setState(() => background = Colors.grey),
child: Container(
color: background,
child: Stack(
children: <Widget>[
Positioned(
top: pos.dy,
left: pos.dx,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onPanUpdate: _onPanUpdate,
// onTapDown: (_) {}, Doesn't affect the problem
child: Container(
width: 30.0,
height: 30.0,
color: Colors.red,
),
),
)
],
),
),
);

void _onPanUpdate(DragUpdateDetails details) {
RenderBox renderBox = context.findRenderObject();
setState(() {
pos = renderBox.globalToLocal(details.globalPosition);
});
}
}


However, when starting to drag the widget, the onTap of the outermost container is triggered as well, making the background momentarily go green in this case. Settings HitTestBehavior.opaque doesn't seem to work like I'd expect. Neither does adding a handler for onTapDown to the widget in the Stack.



So, how do I prevent onTapDown from being triggered on the outermost GestureDetector when the user interacts with the widget inside of the Stack?



Update:



An even simpler example of the problem I'm encountering:



GestureDetector(
onTapDown: (_) {
print("Green");
},
child: Container(
color: Colors.green,
width: 300.0,
height: 300.0,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) {
print("Red");
},
child: Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
),
),
),
);


When I tap and hold the red container, both "Red" and "Green" are printed even though the inner GestureDetector has HitTestBehavior.opaque.










share|improve this question
























  • I don't know if it can help, but cuold you try chanhe outer GestureDetector to Listener?
    – Andrey Turkovsky
    Nov 9 at 20:01










  • @AndreyTurkovsky I'm not sure if I understand what you mean. Just replacing the outer detector with a Listener and using onPointerDown etc. like I used onTapDown does fix the problem. (This is a reduced example, in the real code the outer GestureDetector is further up the stack in a different Widget. So I'd prefer no to change that GestureDetector if possible)
    – spkersten
    Nov 9 at 20:21















up vote
1
down vote

favorite









up vote
1
down vote

favorite











I have a Stack in which several widget can be dragged around. In addition, the container that the Stack is in has a GestureDetector to trigger on onTapDown and onTapUp. I want those onTap events only to be triggered when the user taps outside of the widget in the Stack. I've tried the following code:



class Gestures extends StatefulWidget {
@override
State<StatefulWidget> createState() => _GesturesState();
}

class _GesturesState extends State<Gestures> {
Color background;
Offset pos;

@override
void initState() {
super.initState();
pos = Offset(10.0, 10.0);
}

@override
Widget build(BuildContext context) => GestureDetector(
onTapDown: (_) => setState(() => background = Colors.green),
onTapUp: (_) => setState(() => background = Colors.grey),
onTapCancel: () => setState(() => background = Colors.grey),
child: Container(
color: background,
child: Stack(
children: <Widget>[
Positioned(
top: pos.dy,
left: pos.dx,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onPanUpdate: _onPanUpdate,
// onTapDown: (_) {}, Doesn't affect the problem
child: Container(
width: 30.0,
height: 30.0,
color: Colors.red,
),
),
)
],
),
),
);

void _onPanUpdate(DragUpdateDetails details) {
RenderBox renderBox = context.findRenderObject();
setState(() {
pos = renderBox.globalToLocal(details.globalPosition);
});
}
}


However, when starting to drag the widget, the onTap of the outermost container is triggered as well, making the background momentarily go green in this case. Settings HitTestBehavior.opaque doesn't seem to work like I'd expect. Neither does adding a handler for onTapDown to the widget in the Stack.



So, how do I prevent onTapDown from being triggered on the outermost GestureDetector when the user interacts with the widget inside of the Stack?



Update:



An even simpler example of the problem I'm encountering:



GestureDetector(
onTapDown: (_) {
print("Green");
},
child: Container(
color: Colors.green,
width: 300.0,
height: 300.0,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) {
print("Red");
},
child: Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
),
),
),
);


When I tap and hold the red container, both "Red" and "Green" are printed even though the inner GestureDetector has HitTestBehavior.opaque.










share|improve this question















I have a Stack in which several widget can be dragged around. In addition, the container that the Stack is in has a GestureDetector to trigger on onTapDown and onTapUp. I want those onTap events only to be triggered when the user taps outside of the widget in the Stack. I've tried the following code:



class Gestures extends StatefulWidget {
@override
State<StatefulWidget> createState() => _GesturesState();
}

class _GesturesState extends State<Gestures> {
Color background;
Offset pos;

@override
void initState() {
super.initState();
pos = Offset(10.0, 10.0);
}

@override
Widget build(BuildContext context) => GestureDetector(
onTapDown: (_) => setState(() => background = Colors.green),
onTapUp: (_) => setState(() => background = Colors.grey),
onTapCancel: () => setState(() => background = Colors.grey),
child: Container(
color: background,
child: Stack(
children: <Widget>[
Positioned(
top: pos.dy,
left: pos.dx,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onPanUpdate: _onPanUpdate,
// onTapDown: (_) {}, Doesn't affect the problem
child: Container(
width: 30.0,
height: 30.0,
color: Colors.red,
),
),
)
],
),
),
);

void _onPanUpdate(DragUpdateDetails details) {
RenderBox renderBox = context.findRenderObject();
setState(() {
pos = renderBox.globalToLocal(details.globalPosition);
});
}
}


However, when starting to drag the widget, the onTap of the outermost container is triggered as well, making the background momentarily go green in this case. Settings HitTestBehavior.opaque doesn't seem to work like I'd expect. Neither does adding a handler for onTapDown to the widget in the Stack.



So, how do I prevent onTapDown from being triggered on the outermost GestureDetector when the user interacts with the widget inside of the Stack?



Update:



An even simpler example of the problem I'm encountering:



GestureDetector(
onTapDown: (_) {
print("Green");
},
child: Container(
color: Colors.green,
width: 300.0,
height: 300.0,
child: Center(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) {
print("Red");
},
child: Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
),
),
),
);


When I tap and hold the red container, both "Red" and "Green" are printed even though the inner GestureDetector has HitTestBehavior.opaque.







flutter gesture-recognition






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 10 at 10:39

























asked Nov 9 at 18:57









spkersten

1408




1408












  • I don't know if it can help, but cuold you try chanhe outer GestureDetector to Listener?
    – Andrey Turkovsky
    Nov 9 at 20:01










  • @AndreyTurkovsky I'm not sure if I understand what you mean. Just replacing the outer detector with a Listener and using onPointerDown etc. like I used onTapDown does fix the problem. (This is a reduced example, in the real code the outer GestureDetector is further up the stack in a different Widget. So I'd prefer no to change that GestureDetector if possible)
    – spkersten
    Nov 9 at 20:21




















  • I don't know if it can help, but cuold you try chanhe outer GestureDetector to Listener?
    – Andrey Turkovsky
    Nov 9 at 20:01










  • @AndreyTurkovsky I'm not sure if I understand what you mean. Just replacing the outer detector with a Listener and using onPointerDown etc. like I used onTapDown does fix the problem. (This is a reduced example, in the real code the outer GestureDetector is further up the stack in a different Widget. So I'd prefer no to change that GestureDetector if possible)
    – spkersten
    Nov 9 at 20:21


















I don't know if it can help, but cuold you try chanhe outer GestureDetector to Listener?
– Andrey Turkovsky
Nov 9 at 20:01




I don't know if it can help, but cuold you try chanhe outer GestureDetector to Listener?
– Andrey Turkovsky
Nov 9 at 20:01












@AndreyTurkovsky I'm not sure if I understand what you mean. Just replacing the outer detector with a Listener and using onPointerDown etc. like I used onTapDown does fix the problem. (This is a reduced example, in the real code the outer GestureDetector is further up the stack in a different Widget. So I'd prefer no to change that GestureDetector if possible)
– spkersten
Nov 9 at 20:21






@AndreyTurkovsky I'm not sure if I understand what you mean. Just replacing the outer detector with a Listener and using onPointerDown etc. like I used onTapDown does fix the problem. (This is a reduced example, in the real code the outer GestureDetector is further up the stack in a different Widget. So I'd prefer no to change that GestureDetector if possible)
– spkersten
Nov 9 at 20:21














1 Answer
1






active

oldest

votes

















up vote
0
down vote













In this answer, I'll solve the simpler example you have given. You are creating the following Widget hierarchy:



 - GestureDetector       // green
- Container
- Center
- GestureDetector // red
- Container


Therefore the red GestureDetector is a child Widget of the green GestureDetector. The green GestureDetector has the default HitTestBehavior: HitTestBehavior.deferToChild. That is why onTapDown is fired for both containers.




Targets that defer to their children receive events within their
bounds only if one of their children is hit by the hit test.




Instead, you can use a Stack to build your UI:



 - Stack
- GestureDetector // green
- Container
- GestureDetector // red
- Container


This structure would result in the follwing sourcecode. It looks the same, but the behavior is the one you desired:



Stack(
alignment: Alignment.center,
children: <Widget>[
GestureDetector(
onTapDown: (_) {
print("Green");
},
child: Container(
color: Colors.green,
width: 300.0,
height: 300.0,
),
),
GestureDetector(
onTapDown: (_) {
print("Red");
},
child: Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
)
],
)





share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53231796%2fhow-do-i-prevent-ontapdown-from-being-triggered-on-a-parent-widgets-gesturedetec%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote













    In this answer, I'll solve the simpler example you have given. You are creating the following Widget hierarchy:



     - GestureDetector       // green
    - Container
    - Center
    - GestureDetector // red
    - Container


    Therefore the red GestureDetector is a child Widget of the green GestureDetector. The green GestureDetector has the default HitTestBehavior: HitTestBehavior.deferToChild. That is why onTapDown is fired for both containers.




    Targets that defer to their children receive events within their
    bounds only if one of their children is hit by the hit test.




    Instead, you can use a Stack to build your UI:



     - Stack
    - GestureDetector // green
    - Container
    - GestureDetector // red
    - Container


    This structure would result in the follwing sourcecode. It looks the same, but the behavior is the one you desired:



    Stack(
    alignment: Alignment.center,
    children: <Widget>[
    GestureDetector(
    onTapDown: (_) {
    print("Green");
    },
    child: Container(
    color: Colors.green,
    width: 300.0,
    height: 300.0,
    ),
    ),
    GestureDetector(
    onTapDown: (_) {
    print("Red");
    },
    child: Container(
    color: Colors.red,
    width: 50.0,
    height: 50.0,
    ),
    )
    ],
    )





    share|improve this answer

























      up vote
      0
      down vote













      In this answer, I'll solve the simpler example you have given. You are creating the following Widget hierarchy:



       - GestureDetector       // green
      - Container
      - Center
      - GestureDetector // red
      - Container


      Therefore the red GestureDetector is a child Widget of the green GestureDetector. The green GestureDetector has the default HitTestBehavior: HitTestBehavior.deferToChild. That is why onTapDown is fired for both containers.




      Targets that defer to their children receive events within their
      bounds only if one of their children is hit by the hit test.




      Instead, you can use a Stack to build your UI:



       - Stack
      - GestureDetector // green
      - Container
      - GestureDetector // red
      - Container


      This structure would result in the follwing sourcecode. It looks the same, but the behavior is the one you desired:



      Stack(
      alignment: Alignment.center,
      children: <Widget>[
      GestureDetector(
      onTapDown: (_) {
      print("Green");
      },
      child: Container(
      color: Colors.green,
      width: 300.0,
      height: 300.0,
      ),
      ),
      GestureDetector(
      onTapDown: (_) {
      print("Red");
      },
      child: Container(
      color: Colors.red,
      width: 50.0,
      height: 50.0,
      ),
      )
      ],
      )





      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        In this answer, I'll solve the simpler example you have given. You are creating the following Widget hierarchy:



         - GestureDetector       // green
        - Container
        - Center
        - GestureDetector // red
        - Container


        Therefore the red GestureDetector is a child Widget of the green GestureDetector. The green GestureDetector has the default HitTestBehavior: HitTestBehavior.deferToChild. That is why onTapDown is fired for both containers.




        Targets that defer to their children receive events within their
        bounds only if one of their children is hit by the hit test.




        Instead, you can use a Stack to build your UI:



         - Stack
        - GestureDetector // green
        - Container
        - GestureDetector // red
        - Container


        This structure would result in the follwing sourcecode. It looks the same, but the behavior is the one you desired:



        Stack(
        alignment: Alignment.center,
        children: <Widget>[
        GestureDetector(
        onTapDown: (_) {
        print("Green");
        },
        child: Container(
        color: Colors.green,
        width: 300.0,
        height: 300.0,
        ),
        ),
        GestureDetector(
        onTapDown: (_) {
        print("Red");
        },
        child: Container(
        color: Colors.red,
        width: 50.0,
        height: 50.0,
        ),
        )
        ],
        )





        share|improve this answer












        In this answer, I'll solve the simpler example you have given. You are creating the following Widget hierarchy:



         - GestureDetector       // green
        - Container
        - Center
        - GestureDetector // red
        - Container


        Therefore the red GestureDetector is a child Widget of the green GestureDetector. The green GestureDetector has the default HitTestBehavior: HitTestBehavior.deferToChild. That is why onTapDown is fired for both containers.




        Targets that defer to their children receive events within their
        bounds only if one of their children is hit by the hit test.




        Instead, you can use a Stack to build your UI:



         - Stack
        - GestureDetector // green
        - Container
        - GestureDetector // red
        - Container


        This structure would result in the follwing sourcecode. It looks the same, but the behavior is the one you desired:



        Stack(
        alignment: Alignment.center,
        children: <Widget>[
        GestureDetector(
        onTapDown: (_) {
        print("Green");
        },
        child: Container(
        color: Colors.green,
        width: 300.0,
        height: 300.0,
        ),
        ),
        GestureDetector(
        onTapDown: (_) {
        print("Red");
        },
        child: Container(
        color: Colors.red,
        width: 50.0,
        height: 50.0,
        ),
        )
        ],
        )






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 12 at 9:22









        Niklas

        3447




        3447






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53231796%2fhow-do-i-prevent-ontapdown-from-being-triggered-on-a-parent-widgets-gesturedetec%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Schultheiß

            Verwaltungsgliederung Dänemarks

            Liste der Kulturdenkmale in Wilsdruff