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)
),
),
),
);
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),
),
),
),
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')
},
...
),
),
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With