129

I have two containers in a stack and both containers have GestureDetector.The OnTap for the first container is working fine but it's not working with another container. The first container is the image and the second one is the green background aligned partially over the first container.

new Stack(
            alignment: Alignment(0.0, 1.44),
            children: <Widget>[
              GestureDetector(
                onTap: () => _openImage(context),
                child: Container(
                  width: 340.0,
                  foregroundDecoration: new BoxDecoration(
                      color: Color.fromRGBO(155, 85, 250, 0.55)),
                  height: 240.0,
                  child: FadeInImage.assetNetwork(
                    placeholder: 'assets/dimlight.png',
                    image: post.imageUrl,
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              new GestureDetector(
                child: new Container(
                  color: Colors.green,
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    children: <Widget>[
                      SizedBox(width: 7.0),
                      CircleAvatar(
                        backgroundImage: 
                           new AssetImage("assets/boy.png")
                        radius: 30.0,
                      ),
                      SizedBox(
                        width: 7.0,
                      ),
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          new SizedBox(
                            height: 20.0,
                          ),
                          Text(
                            post.user.name,
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          Text(
                            getTimeString(post.timestamp.toString()),
                            style: TextStyle(
                                color: Colors.grey, fontSize: 10.0),
                          ),
                        ],
                      ),
                      SizedBox(
                        width: 20.0,
                      ),
                    ],
                  ),
                ),
                onTap: () => _navigateToDetails(context),
              )
            ],
          )

Layout Screenshot

enter image description here

Viren V Varasadiya
  • 25,492
  • 9
  • 45
  • 61
Himanshu Yadav
  • 1,311
  • 2
  • 9
  • 9

19 Answers19

329

Try setting the behavior property of GestureDetector to HitTestBehavior.translucent.

Athul Sai
  • 3,447
  • 1
  • 10
  • 6
  • 30
    Yeap, it works. Why this behavior isn't by default? – Dmitriy Blokhin Jul 18 '19 at 02:37
  • 4
    Note this doesn't work with pageviews. https://stackoverflow.com/q/49510694/8608146. – Phani Rithvij Dec 16 '19 at 07:34
  • 1
    In the case of 2nd container is overnighting the first container then is there any way to get the ```onTap``` event of 1st container? – Dhaval Kansara Jul 14 '20 at 13:18
  • 1
    This defaults to HitTestBehavior.deferToChild if child is not null and HitTestBehavior.translucent if child is null. – nitinku5021a Aug 25 '20 at 16:41
  • 10
    This didn't work for me, any other suggestions? – Daniel Hernandez Jan 04 '21 at 05:12
  • There are 8 million code samples that explain how to use the GestureDetector to accomplish closing the keyboard in Flutter and it took me the better part of a week to stumble across this comment which fixed it. I know I'm not supposed to leave comments saying "thanks" but in addition to my upvote, felt it necessary to call out my sincere appreciation for this answer. – Deeko Jul 02 '22 at 18:36
  • doesnt work in ```listItem``` in my ```ListView.builder``` ```GestureDetector ``` ---> stack --> [ column, position ] . – Yogi Arif Widodo Jan 28 '23 at 08:39
44

Using InkWell instead of GestureDetector solved my problem.

Sobhan Moradi
  • 529
  • 5
  • 17
  • 5
    Yes, but it adds other (generally) unneeded parts - like, you know, ink well. Setting the `behavior` is the correct answer in current context. – Alex Semeniuk Aug 01 '19 at 11:27
  • FYI, `InkWell` is a material component. It adds visual/sound feedback which may not be something that a developer wants. Also, it is possible for app to be using iOS Cupertino theme. If you are only interested in tap/click events, `GestureDetector` is a good option. (with `HitTestBehavior.translucent`) – user482594 Jan 05 '20 at 17:21
  • 2
    OP made clear that they wanted to use a `GestureDetector`. – HBG Mar 20 '20 at 06:52
  • you have any solution ..if we use clipBehaviou:Clip.none in Stack ..? – benten Jul 28 '21 at 08:06
42

You should try wrap your Container with an AbsorbPointer.

GestureDetector(
   child: AbsorbPointer(
      child: Container(...)
   )
)
Michel Fortes
  • 819
  • 8
  • 12
14

For me it was an issue with items overflowing outside a Stack, with clipBehavior: Clip.none.

In that case the item is visible but not touchable. So I updated my layout to remove the clipBehavior: Clip.none on my Stack and the GestureDetector started working.

Hugo H
  • 6,029
  • 5
  • 37
  • 57
8

Also consider Listener widget. It helped me.
https://api.flutter.dev/flutter/widgets/Listener-class.html
Example:

Listener(
    onPointerDown: (_) {}, // onTapDown
    onPointerUp: (_) => {}, // onTapUp
    child: Container(),
)
Rodion Mostovoi
  • 1,205
  • 12
  • 16
3

Because of stack..There are some widget on your GestureDetector.

Solution:

Stack(
        children: <Widget>[
              ...//Other widgets,
              GestureDetector()
        ]
BIS Tech
  • 17,000
  • 12
  • 99
  • 148
  • Have a look at this file https://gist.github.com/himanshu096/b9f8d4506a024c3322c9291027491815... I'm using it in the same way. – Himanshu Yadav Nov 05 '19 at 09:17
  • add height to second contaner.. example: ```GestureDetector( onTap: () => print("this is second container"), child: Container( height: 100,``` – BIS Tech Nov 06 '19 at 03:48
3

In my case I had a CustomPainter inside the stack. Nothing of the above was working, until I gave the CustomerPainter a size. It was showing without explicitly giving a size, but no tap events were triggered:

Container(
      child: CustomPaint(
    size: Size(30, 30),
    painter: RecordingButton(30),
  ))
stan
  • 229
  • 3
  • 10
2

Another GestureDetector in the BuildContext tree

Ok on my side it was quite stupid but since it may happens to many of us I will explain:

My build context (simplified for the example) tree was this way:

Widget build(BuildContext context) {

return (...) (
    child: GestureDetector(
        onTap: () => _myFunction(),
        child: Container(
            height: 64.0,
            width: 128.0,
            child: (...)

Seems legit right? But then I inspected into more details the bottom of my tree:

(...)
        onTap: () => _myFunction(),
        child: Container(
            height: 64.0,
            width: 128.0,
            child: TheSourceOfEvil() // <-- whoopsie!

Quickly inspected TheSourceOfEvil to find out that the culprit was in fact ANOTHER GestureDetector has a child, that I had not yet implemented onTap: () => {},:

Widget TheSourceOfEvil( {

return (...) (
    child: GestureDetector(
        onTap: () => {}, // override previous GestureDetector
        child: Container(
            child: (...);

Got rid of it and it instantly fixed my puzzling issue. I hope it will help!

Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
1

i think that your widgets are overlapping each other and that the causing a problem. you can check it by wrapping your GestureDetector with container and provide color to get better understanding.

your code is not enough that's why i added following example may help you to understand more clearly.

swap the position of GestureDetector in example and you can found that in first case it prints only second and in other case if you click in above part then it prints first to.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Card(
        margin: EdgeInsets.all(40.0),
        child: new Column(
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            GestureDetector(
              onTap: () => print("first container"),
              child: Container(
                width: 340.0,
                foregroundDecoration: new BoxDecoration(
                    color: Color.fromRGBO(155, 85, 250, 0.0)),
                height: 240.0,
                child: FadeInImage.assetNetwork(
                  placeholder: 'images/p1.png',
                  image:
                  "https://www.straitstimes.com/sites/default/files/styles/article_pictrure_780x520_/public/articles/2016/06/15/ST_20160615_LLIMH_2368135.jpg?itok=8Dggu2PM&timestamp=1465926004",
                  fit: BoxFit.cover,
                ),
              ),
            ),
            new GestureDetector(
              child: new Container(
                foregroundDecoration: BoxDecoration(
                    color: Color.fromRGBO(155, 85, 250, 0.4)),
                child: Row(
                  mainAxisSize: MainAxisSize.max,
                  children: <Widget>[
                    SizedBox(width: 7.0),
                    CircleAvatar(
                      backgroundImage: new AssetImage("images/p2.jpg"),
                      radius: 30.0,
                    ),
                    SizedBox(
                      width: 7.0,
                    ),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          new SizedBox(
                            height: 20.0,
                          ),
                          Text(
                            "sfvgefbv",
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          Text(
                            "sfvmsfkv",
                            style: TextStyle(
                                color: Colors.grey, fontSize: 10.0),
                          ),
                        ],
                      ),
                    ),
                    new Container(
                      alignment: AlignmentDirectional.centerEnd,
//            todo add here check if not logged in then ask to
                      child: Row(
                        mainAxisSize: MainAxisSize.min,
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          IconButton(
                              icon: Icon(
                                Icons.comment,
                                color: Colors.green,
                              ),
                              onPressed: () => print("message click")),
                          Text(
                            "2",
                            style: TextStyle(
                              color: Colors.green,
                            ),
                          ),
                          SizedBox(
                            width: 10.0,
                          )
                        ],
                      ),
                    ),
                  ],
                ),
              ),
              onTap: () => print("this is second container"),
            ),
            new Expanded(
              child: Container(
                padding: EdgeInsets.all(10.0),
                child: Column(
                  children: <Widget>[
                    Text(
                      "fsvkmfskbnmkffvberk",
                      style: TextStyle(
                          color: Colors.green, fontWeight: FontWeight.bold),
                    ),
                    new Text(
                      "svklmfslkbnernkjrnvkrwjnvrw",
                      maxLines: 6,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Viren V Varasadiya
  • 25,492
  • 9
  • 45
  • 61
  • This is what I already mentioned in the question that the container with the green background is having GestureDetector and its partially over the other container having a background as Image(have a look at an attached image in question). Problem is GestureDector only working in the part which is over the first container not on another half part of a container with green colour and which is not overlapping with the first container. – Himanshu Yadav Oct 25 '18 at 11:34
  • anyhow you have to remove that overlapping in your designing portion. you can only achieve your desire output by doing that. your code is not enough. please add some more code related to this. – Viren V Varasadiya Oct 25 '18 at 11:53
  • Ok so here's the link to the file to get the issue. https://gist.github.com/himanshu096/b9f8d4506a024c3322c9291027491815 – Himanshu Yadav Oct 25 '18 at 13:47
  • i just updated my answer. i think that you are using to much stack widget which is really not necessary. i hope that this code solved your problem. if not then describe exactly what you need. – Viren V Varasadiya Oct 25 '18 at 18:30
  • I want my second container(with that user image) partially over the first container (container having a large image). To give you a more clear view, I just updated the gist file. Just run that code and this is the exact UI I want. – Himanshu Yadav Oct 26 '18 at 05:58
0

For others coming to this post, another thing that can cause this issue is an AbsorbPointer higher is the hierarchy.

johngray1965
  • 163
  • 1
  • 9
0

If your are using GoogleMap widget and want to add a searchfield/textfield you should be aware that the map widget is consuming your gestures. I do something like this:

final FocusNode _focusNode = new FocusNode();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.translucent,
      onTap: () {            
        print("tap");
        if (_focusNode.hasPrimaryFocus) {
          _focusNode.unfocus();
          rebuildAfterFocusChange();
        }
      },
      child: Stack(children: [
        AbsorbPointer(absorbing: _focusNode.hasPrimaryFocus, child: StoreMapView()),
        showSearchField(model),
      ]),
    );
  }

Widget showSearchField(StoreFinderViewModel model) {
    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 12.0),
        child: TextField(
          onTap: () {
            rebuildAfterFocusChange();
          },
          onSubmitted: (String input) {
            rebuildAfterFocusChange();
            // do other stuff
            });
          },
          focusNode: _focusNode,
        ),
      ),
    );
  }

  void rebuildAfterFocusChange() {
    setState(() {});
  }

I am using the AbsorbPointer widget to absorb the clicks/taps, but ONLY if my textfield has the focus. I call my rebuildAfterFocusChange(); if I tap on my "Searchfield", use the onsubmit method of the textfield to trigger a search in my backend and after I unfocus my textfield.

basti12354
  • 2,490
  • 4
  • 24
  • 43
0

Okay here's the deal, I just changed the position of the widgets. In Stack Everything is like a Tree index.

Stack Parent
  └ Child One    [Far Back]
  └ Child Two
  └ Child Three  [Top Front]

My Code:

     return Stack(
              children: [
                accountInfo(),   // this is not clickable
                chatMessages(),
                typingArea(),
              ],
            ),

I just changes the sequence

    return Stack(
              children: [
                chatMessages(),
                typingArea(),
                accountInfo(), // Now this is clickable by any of onTap Event ad
              ],
            ),
Sayed Muhammad Idrees
  • 1,245
  • 15
  • 25
0

For my case, I placed the GestureDetector inside the Stack which created the same problem. I solved it by making the parent Stack into a child of the GestureDetector .

Before -

Stack(
    children: [
    GestureDetector()
    ])

After -

GestureDetector(
    child: 
    Stack()
   )
Gk Mohammad Emon
  • 6,084
  • 3
  • 42
  • 42
0

Keep your gesture detector as the last child in the stack that means it will be on the top front like below;

Stack Parent └ Child One [Far Back] └ Child Two └ Child Three [Top Front]

if its at the back of another child then the overlapping child prevents it from being clicked somehow

0

OnTap does not work outside the first childrens area in stack. So put everything inside a container and give it a height and width and make it a first child.

shams7
  • 71
  • 1
  • 3
0
  1. The click is invalid because your widget is beyond the scope of Stack

  2. Use ConstrainedBox to fill the stack with screen

class MyHomePage extends StatefulWidget {
   final String title;
   const MyHomePage({Key? key, required this.title}) : super(key: key);
    @override
    State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
    @override
    Widget build(BuildContext context) {
        return Scaffold(
             appBar: AppBar(
                title: Text(widget.title),
             ),
             body: ConstrainedBox(
                    constraints: BoxConstraints.expand(),
                    child: Stack(
                      children: [
                        Positioned(
                          right: 100,
                          bottom: 100,
                          child: GestureDetector(
                            behavior: HitTestBehavior.translucent,
                            onTap: () {
                              debugPrint("hello");
                              showDialog(context: context, builder: ((context) => Text("dialog")));
                            },
                            child: Container(
                                child: Text("123"),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              }
}
ifredom
  • 979
  • 9
  • 6
0

Use Inkwell instead of GestureDetector

-1

@Michel Fortes' AbsorbPointer() solution worked for me, even with Stack() as one of the descendants.

Prior to this I had tried modifying the GestureDetector.behavior to HitTestBehavior.transluscent, using Inkwell() instead, and wrapping different widgets in the tree. I also tried using RawGestureDetector().

Nothing worked until AbsorbPointer():

GestureDetector(
   child: AbsorbPointer(
      child: Container(...) // or any Material widget, even if descendants include a Stack() 
      )
)
dutch
  • 7
  • 3
-2

I see that you are calling the callback method instead of assigning it to onTap property of GestureDetector widget. Pass the name of the method don't call it there.

paulos
  • 32
  • 1
  • 3
  • if we want to execute a function in onTap then I think that's how we should use this. If we pass the reference of func. then it's not going to execute at all. – Himanshu Yadav Jan 10 '19 at 06:09