Skip to content

avoid_unnecessary_gesture_detector

v0.4.0 Warning Fix Widget Best Practices

Flags a GestureDetector with no argument whose name starts with on.

A handler-less GestureDetector still takes part in hit testing, so it can quietly swallow touches meant for something underneath — especially with behavior: HitTestBehavior.opaque. Either the handler was deleted and the wrapper left behind, or the handler was never wired up and the tap has been dead since it shipped.

This rule is in the recommended preset, so it is on with preset: recommended and every preset above it. No configuration.

See also: GestureDetector

The usual origin: the callback moved somewhere else and the wrapper stayed:

class ProductTile extends StatelessWidget {
const ProductTile({super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
child: const Text('Product'),
);
}
}

Either wire the handler back up:

class ProductTile extends StatelessWidget {
const ProductTile({required this.onOpen, super.key});
final VoidCallback onOpen;
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onOpen,
child: const Text('Product'),
);
}
}

Or drop the wrapper — the quick fix does this, replacing the GestureDetector with its child:

@override
Widget build(BuildContext context) => const Text('Product');

behavior, excludeFromSemantics, dragStartBehavior and friends configure the detector; they do not give it anything to do. Only an on* argument counts:

// Still reported — no on* argument
GestureDetector(
behavior: HitTestBehavior.translucent,
excludeFromSemantics: true,
child: const Text('Nothing happens'),
);

Prefer InkWell when the tap is a Material one

Section titled “Prefer InkWell when the tap is a Material one”

If you are adding a handler back to get a tap, InkWell gives you the ripple as well:

InkWell(
onTap: onOpen,
child: const Text('Product'),
);

Any argument whose name starts with on satisfies the rule, so onSomethingElse: null — an explicitly null handler — keeps it quiet. The rule reads the argument name, not the value.

RawGestureDetector and Listener are not checked.

many_lints.yaml
rules:
avoid_unnecessary_gesture_detector: false

To keep the rule on but skip certain paths, use per-rule exclude.