GestureDetector is a Flutter widget that detects and responds to touch gestures – taps, double taps, long presses, drags, and scales – by wrapping a child widget and exposing gesture callbacks.
It adds no visual feedback of its own: no ripple, no highlight, nothing. For that, Material apps typically reach for InkWell instead, which wraps the same gesture-recognition machinery with a Material ink response.
GestureDetector in Flutter is part of the core widgets library and works with or without Material.behavior property controls hit testing, and defaults to HitTestBehavior.deferToChild when it has a child, or HitTestBehavior.translucent when it doesn't.excludeFromSemantics to true to exclude those gestures from the semantics tree.GestureDetectors compete for the same pointer event, Flutter resolves the conflict through the gesture arena, so only one recognizer typically wins.Wrap any widget in GestureDetector and provide an onTap callback.
import 'package:flutter/material.dart';
class TapCounter extends StatefulWidget {
const TapCounter({super.key});
@override
State<TapCounter> createState() => _TapCounterState();
}
class _TapCounterState extends State<TapCounter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _count++),
child: Container(
color: Colors.blue.shade100,
padding: const EdgeInsets.all(16),
child: Text('Tapped $_count times'),
),
);
}
}Nothing here changes color or shows feedback on tap – that's expected. If the design needs a visible response, wrap the child in InkWell instead, or add your own state-driven styling.
The most common cause is behavior defaulting to HitTestBehavior.deferToChild: the GestureDetector only receives pointer events where its child is hit, so tapping padding or empty space around a child can do nothing.
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Padding(
padding: const EdgeInsets.all(24),
child: const Text('Tap anywhere in this padded area'),
),
),Setting behavior: HitTestBehavior.opaque makes the entire GestureDetector bounds participate in hit testing, including the padding around the text.
Note: HitTestBehavior.opaque does not make the GestureDetector larger; it makes its existing bounds hit-testable.
| GestureDetector | InkWell |
|---|---|
Visual feedback | |
| None | Ripple and highlight |
Requires Material ancestor | |
| No | Yes |
Gesture range | |
| Tap, double tap, long press, secondary press, drag, pan, scale, force press, and more | Tap, double tap, long press, secondary press, and more |
Typical use | |
| Custom or Cupertino interactions | Material surfaces and tappable areas |
Underlying mechanism | |
| GestureRecognizers | Built on InkResponse, which uses a GestureDetector |
Verdict: use InkWell for tappable Material surfaces that should provide ink/ripple feedback; use GestureDetector for custom gestures, non-Material UI, or interactions that InkWell does not cover.
behavior explicitly when the hit-test area matters – deferToChild (the default) only considers the GestureDetector hit when its child is hit; opaque makes the entire bounds participate in hit testing; translucent also allows hit testing of widgets visually behind it.GestureDetector alone gives users no confirmation that a tap registered.InkWell inside Material apps when you want Material ink/ripple feedback; use GestureDetector when you need gestures such as pan or scale that InkWell does not provide.excludeFromSemantics: true only when another widget already provides the appropriate semantics – otherwise the gestures handled by the GestureDetector are removed from the semantics tree.GestureDetector widgets when you expect both to respond to the same gesture – competing gesture recognizers are resolved through Flutter's gesture arena, so only one typically wins.onDoubleTap only when double-tap interaction is actually needed. When both onTap and onDoubleTap are provided, Flutter delays onTap while it determines whether a second tap occurs, which can make single taps feel less responsive.deferToChild behavior, only the child's own painted area registers hits.GestureDetector for a standard button-like action in a Material app. This misses the built-in visual feedback, disabled state, and button semantics provided by a real button widget; use InkWell when you need Material ink feedback.GestureDetector widgets both fire on the same tap. Competing gesture recognizers are resolved through the gesture arena, so only one typically wins and invokes its callback. Hit-testing issues caused by behavior or IgnorePointer can prevent a detector from entering the competition in the first place.GestureDetector belongs to Flutter's core widgets library and does not require a Material ancestor; InkWell, by contrast, relies on a Material ancestor for its ink effects.
Flutter is known for its seamless UI/UX features and robust design elements. It allowed us to deliver a unique customer experience in the “CA24 Mobile” banking app. This article shares some of the learnings and pain points behind implementing it in this large project.

Flutter ships with plenty of high-quality widgets, layouts, and themes that developers can use to speed up the whole creation process. A great example of custom widgets made in Flutter is the Placement Wheel developed for one of our clients. See how to do it.