Flutter - How tap / touch area works?

Viewed 4317

1) If I have this, when I tap the child Container it doesn't print 'tap':

Container(
  color: Colors.red,
  child: GestureDetector(
    onTap: () => print('tap'),
    child: Container(
      width: 100,
      height: 100,
    ),
  ),
),

2) If I have this, when I tap the child Container it prints 'tap':

Container(
  color: Colors.red,
  child: GestureDetector(
    onTap: () => print('tap'),
    child: Container(
      width: 100,
      height: 100,
      decoration: BoxDecoration(),
    ),
  ),
),

3) If I have this, when I tap the child Container, outside the text, it prints 'tap':

Container(
  color: Colors.red,
  child: GestureDetector(
    onTap: () => print('tap'),
    child: Container(
      width: 100,
      height: 100,
      child: Text("A"),
    ),
  ),
),

Can someone explain me these 3 behaviors?

2 Answers
  1. A Container with bounds (height and width) is nothing but a placeholder for other widgets to go inside it. Since you haven't provided any child to it or any other property such as color or decoration to it, the container is considered to be invisible for GestureDetector.

According to the official GestureDetector document, by default a GestureDetector with an invisible child ignores touches. this behavior can be controlled with behavior.

If you still want the placeholder container to be recognized as tap event, use behavior property of GestureDetector, that will tell GestureDetector to tap on the container and then you will see tap printed, as below:

Container(
          color: Colors.red,
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () => print('tap'),
            child: Container(
              width: 100,
              height: 100,
            ),
          ),
        ),
  1. In this case, since you provided decoration property and used BoxDecoration(), it renders a box based on the bounds provided by its parent widget. So, the container has a box shape. In order to see how the box shape is rendered inside the container, just provide a border property, as below:
Container(
        color: Colors.red,
        child: GestureDetector(
          onTap: () => print('tap'),
          child: Container(
            width: 100,
            height: 100,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.yellow, width: 4)
            ),
          ),

enter image description here

You can see the yellow borders spanning entire container size, which acts as a layer on top of the container and now considered to be tappable. Hence, GestureDetector's tap is recognized and you see tap printed.

  1. In this case, since you have provided child widget as text, GestureDetector identifies it since the widget is visible and hence tap is printed, irrespective of whether you tap on the text or outside of it, because since GestureDetector doesn't have a size by itself, it relies on child's size, which in this case is the bounds (height and width). Hence, it considers the entire bounded area to be tappable and you get the tap event printed anywhere inside it.

Hope this answers your question.

First understand that GestureDetector only detects tap on the area which is drawn by its child, ignoring behaviour set by it. Now let's take a look at your cases.


Case 1: (Doesn't detect touch)

Container(
  color: Colors.red,
  child: GestureDetector(
    onTap: () => print('tap'),
    child: Container( // child Container (has nothing to draw on screen)
      width: 100,
      height: 100,
    ),
  ),
),

The child Container isn't drawing anything on screen, so GestureDetector won't allow click. But you will say it has both width and height, yes you're right but it doesn't have anything to draw on screen.

If you give it any color or decoration, or any child it will have something to draw and thus it would be able to detect tap.


Case 2 (Detects touch):

Container( 
  color: Colors.red,
  child: GestureDetector(
    onTap: () => print('tap'),
    child: Container( // child Container
      width: 100,
      height: 100,
      decoration: BoxDecoration(), // has something to draw on screen
    ),
  ),
),

Here you are giving child Container a decoration so it has something to draw on screen, and GestureDetector would allow click.


Case 3 (Detects touch):

Container(
  color: Colors.red,
  child: GestureDetector(
    onTap: () => print('tap'),
    child: Container( // child Container 
      width: 100,
      height: 100,
      child: Text("A"), // has something to draw on screen 
    ),
  ),
),

Here you are giving child Container a Text, and thus something to draw on screen and thus clicking is enabled.

Note: Not only Text but full area of child Container receives click because GestureDetector direct child is Container with width and height set to 100 and not just the Text.

Related