Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using GestureDetector to catch gestures of MaterialButton child

I want to have a MaterialButton to detect onTapDown, onTapUp and onTapCancel.

Unfortunately MaterialButtons only detect onPressed. So I wrapped it with a GestureDetector but for MaterialButtons, onPressed is @required so I set it to () {}. The problem is that, even if I set the GestureDetector behavior to be translucent, if I tap on the button, only the onPressed callback of the Button will be triggered and I loose the onTapDown and onTapUp callbacks of GestureDetector. I only get the onTapDown to trigger if I long press the button, and when I release it, onTapCancel is triggered. I can never get the onTapUp to be triggered this way.

I still want the MaterialButton for its haptic/sound/visual feedback capabilities.

I tried not to set the onPressed on the MaterialButton at all, and (oddly) it still worked but the button was disabled and didn't give any feedback at all.

GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTapDown: (_) {
            th.handleTap(i, 0);
          },
          onTapUp: (_) {
            th.handleTap(i, 1);
          },
          onTapCancel: () {
            th.handleTap(i, 2);
          },
          child: MaterialButton(
            onPressed: () {},
            child: Container(
              margin: EdgeInsets.all(5),
              child: Icon(icon),
            ),
          )
        )

Edit: Thank you Hugo Passos, for some reason I assumed MaterialButtons would never pass gesture to their children. I'm getting near but I'm still not there: the only way I found to match the size of the Gesture is to "sandwitch" it between two Containers, which doesn't look like a very elegant solution to me. By the way, even in this case I run into two problems:

1: If I (quite easily) tap on the border of the button I still have the onPressed of the MaterialButton triggered instead of the GestureDetector's callbacks. I thought it had zero size and so it couldn't never tapped. This problem doesn't show if I set the GestureDetector behavior to opaque (still strange to me still).

2: Most important: I still can't get the native MaterialButton feedback capabilities unless I accidentally trigger its onPressed.

Container(
margin: EdgeInsets.all(2),
        width: buttonSize,
        height: buttonSize,
        child: RaisedButton(
          padding: EdgeInsets.zero,
          onPressed: () {print("onTap");},
          child: Container(
            margin: EdgeInsets.zero,
            width: buttonSize,
            height: buttonSize,
            child: GestureDetector(
              onTapDown: (_) {
                th.handleTap(i, 0);
              },
              onTapUp: (_) {
                th.handleTap(i, 1);
              },
              onTapCancel: () {
                th.handleTap(i, 2);
              },
              child: Icon(icon)
            ),
          ),
        ),
      );
like image 895
Matteo Bertino Avatar asked May 15 '19 17:05

Matteo Bertino


1 Answers

You must do the opposite. Instead of wrapping RaisedButton in a GestureDetector, wrap a GestureDetector in a RaisedButton. You also have to remove the RaisedButton padding, so you can expand the GestureDetector acting area.

RaisedButton(
  padding: EdgeInsets.zero,
  onPressed: () {},
  child: Container(
    height: 40,
    width: 80,
    child: GestureDetector(
      onTapDown: (_) => print('onTapDown'),
      onTapUp: (_) => print('onTapUp'),
      onTapCancel: () => print('onTapCancel'),
      child: Icon(icon),
    ),
  ),
),

Edit

Center(
  child: RaisedButton(
    padding: EdgeInsets.zero,
    onPressed: () => print('onPressed'),
    child: GestureDetector(
      onTapDown: (_) => print('onTapDown'),
      onTapUp: (_) => print('onTapUp'),
      onTapCancel: () => print('onTapCancel'),
      child: Container(
        color: Colors.blue,
        height: 40,
        width: 80,
        child: Icon(icon),
      ),
    ),
  ),
);

You'll get a little border that calls onPressed, though. If this bothers you, wrap this code in another Container, just like you did in your code.

You can easily get the native RaisedButton feedback capabilities by calling the onPressButton method at onTapUp. It'll have the same behavior.

RaisedButton(
  ...
  onPressed: onPressButton,
  child: GestureDetector(
    ...
    onTapUp: (_) {
      onPressButton();
      print('onTapUp')
    },
    ...
  ),
),
like image 68
Hugo Passos Avatar answered Oct 29 '22 05:10

Hugo Passos