Using GestureDetector to catch gestures of MaterialButton child

2,916

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')
    },
    ...
  ),
),
Share:
2,916
Matteo Bertino
Author by

Matteo Bertino

Updated on December 11, 2022

Comments

  • Matteo Bertino
    Matteo Bertino over 1 year

    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)
                ),
              ),
            ),
          );
    
  • Matteo Bertino
    Matteo Bertino about 5 years
    Thank you Hugo, I still have issues anyway. I edited my question.
  • Matteo Bertino
    Matteo Bertino about 5 years
    Assuming I defined onPressButton elswhere like this: onPressButton() {print("onPressButton");}; It still doesn't work: onPressButton is called, but I don't get the Button feedbacks.
  • Hugo Passos
    Hugo Passos about 5 years
    I don't know what you mean by button feedback. The only feedback a button can provide is onPressed, right? If onPressButton is called, how didn't you get the button feedback?
  • Matteo Bertino
    Matteo Bertino about 5 years
    Sorry for the delay. I meant the UI feedback, like sound, vibration and splash effect on the button. With the last solution everything works like it should from a callback point of view, but my buttons don't give any visual feedback, the are just grey, working squares.