Skip to content

Flutter - Introduction to Gestures

Gestures are the primary way users interact with touch-based applications. Flutter provides a powerful and flexible system for detecting and responding to a wide range of user gestures, from simple taps to complex drags and scales.

Mobile users expect apps to respond to various touch inputs, including:

  • Tap: A brief touch on the screen surface.
  • Double Tap: Two taps in quick succession at the same location.
  • Long Press: Touching the screen and holding for an extended period.
  • Drag / Pan: Touching the screen and moving the finger across it.
    • Vertical Drag: Primarily vertical movement.
    • Horizontal Drag: Primarily horizontal movement.
  • Scale / Pinch: Using two fingers to move closer together (pinch/zoom out) or farther apart (spread/zoom in).
  • Flick / Fling: A quick drag-and-release motion, often resulting in continued movement (like scrolling).

Flutter’s main tool for gesture detection is the GestureDetector widget. It’s a non-visual widget, meaning it doesn’t draw anything itself. Instead, you wrap it around the widget you want to make interactive.

GestureDetector listens for various touch events on its child and invokes corresponding callback functions when a gesture is recognized.

GestureDetector offers numerous callbacks for different gestures:

  • Tap Gestures:

    • onTap: Called when a tap gesture is completed.
    • onTapDown: Called when the user first touches the screen at the start of a potential tap.
    • onTapUp: Called when the user releases the screen at the end of a potential tap.
    • onTapCancel: Called if a potential tap is cancelled (e.g., the finger moves too far).
    • onDoubleTap: Called for a double tap.
    • onLongPress: Called for a long press.
  • Drag Gestures (Vertical, Horizontal, Pan):

    • onVerticalDragStart, onVerticalDragUpdate, onVerticalDragEnd
    • onHorizontalDragStart, onHorizontalDragUpdate, onHorizontalDragEnd
    • onPanStart, onPanUpdate, onPanEnd (for drags in any direction)
    • These callbacks provide DragUpdateDetails or similar objects containing information about the drag position and delta (change).
  • Scale Gestures:

    • onScaleStart, onScaleUpdate, onScaleEnd
    • Provide ScaleUpdateDetails containing scale, rotation, focalPoint etc.

GestureDetector attempts to recognize only one gesture at a time per pointer interaction. It uses a gesture arena to disambiguate when multiple detectors might recognize gestures from the same input events (e.g., deciding between a tap and a vertical drag).

Example: Tappable ‘Hello World’ with Dialog

Section titled “Example: Tappable ‘Hello World’ with Dialog”

Let’s modify the simple ‘Hello World’ app to show a dialog when the text is tapped.

import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gesture Demo',
home: const MyHomePage(title: 'Gesture Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key, required this.title});
final String title;
// Function to show the dialog
void _showDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: const Text("Message"),
content: const Text("Hello World Tapped!"),
actions: <Widget>[
TextButton( // Use TextButton instead of deprecated FlatButton
child: const Text("Close"),
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
// Wrap the Text widget with GestureDetector
child: GestureDetector(
onTap: () {
// Call the function to show the dialog when tapped
_showDialog(context);
},
child: const Text(
'Hello World',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
),
);
}
}

In this example, the Text widget is wrapped by GestureDetector. The onTap callback is implemented to call _showDialog, which uses the built-in showDialog function and AlertDialog widget to display a simple popup.

Flutter provides other specialized widgets for interaction:

  • Listener: A lower-level widget that reports raw pointer events (PointerDownEvent, PointerMoveEvent, PointerUpEvent, PointerCancelEvent). Useful for custom gesture recognition or tracking pointer movements.
  • InkWell & InkResponse: Material Design widgets that display a visual splash effect (the ‘ink splash’) when tapped. Often used for making non-button widgets tappable with visual feedback.
  • Dismissible: Allows a widget (typically a list item) to be dismissed by swiping (flicking) it horizontally.
  • Draggable & LongPressDraggable: Make a widget draggable.
  • DragTarget: A widget that can accept a Draggable being dropped onto it.
  • IgnorePointer & AbsorbPointer: Control whether a widget and its subtree participate in hit testing (gesture detection). IgnorePointer is invisible to hits, while AbsorbPointer consumes the hit but prevents underlying widgets from receiving it.
  • Scrollable & Scrollable View Widgets (ListView, GridView, SingleChildScrollView): Handle scroll gestures automatically.

Choosing the right widget depends on the specific interaction you want to achieve. GestureDetector is the general-purpose tool, while others like InkWell or Dismissible provide more specific behaviors and visuals.