How to React to Touch Events in Flutter

Almost every app will need some sort of user input. Usually, your app will need to respond to touch events. This is a small guide to touch events, especially for starting Flutter developers.

When it comes to gesture recognition and reaction to touch events, Flutter offers incredible widgets for new developers. You can make any widget in your app respond to touch events by just wrapping it in one of these touch recognition widgets.

Whether you are a developer just learning more about the management of touch events on Flutter or a professional looking for a quick recap, this post will get you up to speed.

How Does the Gesture System Operate?

Essentially, the gesture system in Flutter operates on two distinct layers. The first layer stores the raw pointer events that describe the physical details like the location and movement of the pointers in use, such as styli, mice, and touches in general.

Above this, we have the layer of “gestures”. This interprets the pointer movements as meaningful semantics and adds a context to the pointer movements.

A Quick Touch Event Glossary for Beginners

Touch events cannot be handled unless you understand semantic gestures and pointers. The pointer is the hero in this arena! You’ll see how this is true below.

Before we explore the reaction of individual elements, let’s have a look at the features involved in the case:

Pointers

Pointers represent raw data related to user interaction with the screen in question. There are four different types of pointer events:

  • PointerUpEvent: The pointer has stopped interacting with the screen.
  • PointerDownEvent: The pointer is in contact with the screen at a specific location.
  • PointerMoveEvent: The pointer has moved to a new position on the screen.
  • PointerCancelEvent: The input of this pointer is no longer directed towards the app in progress.

When you engage the PointerDownEvent, the framework will examine the app to determine the widget located at that specific point on the app’s wireframe. The PointerDownEvent will be dispatched to that widget and the widgets above it to the root of the component tree, and so too will any events that follow the down event on that particular path.

Gestures

As mentioned earlier, gestures represent the semantic actions, such as tap, scale, and drag, that are easily recognized from multiple individual events emerging from a pointer activity. A single gesture could possibly incorporate multiple touch events, each handling a unique milestone of the gesture like the drag start or drag end!

Here are the most common types of gestures involved in Touch Event Management on Flutter:

Tap Gestures

  • onTapUp: A pointer triggers a tap on a particular location on screen.
  • onTapDown: A pointer might cause a tap that has been contacted on screen.
  • onTap: When the pointer that previously triggered onTapDown also triggers onTapUp, it causes a tap.
  • onTapCancel: When the pointer triggering onTapDown will not cause a tap.

Double Tap Gestures

  • onDoubleTap: The user taps the screen in the same position twice in quick succession.

Long Press Gestures

  • onLongPress: A pointer remains in contact with the screen in the same location for a longer chunk of time.

Vertical Drag Gestures

  • onVerticalDragStart: The pointer contacts the screen and will probably move vertically.
  • onVerticalDragEndA pointer that had previously been in contact with the screen during a vertical movement ends contact with the screen at a similar velocity as the movement itself.
  • onVerticalDragUpdate: A pointer that is in contact with the screen moves vertically in the same direction.

Horizontal Drag Gestures

  • onHorizontalDragStart: A pointer contacts the screen to potentially move in the horizontal direction.
  • onHorizontalDragEnd: A pointer that was previously moving horizontally in contact with the screen, with a specific velocity, loses contact altogether.
  • onHorizontalDragUpdate: A pointer in contact with the screen moves horizontally from the onHorizontalDragStart.

Pan Gestures

  • onPanStart: A pointer has contacted the screen and can potentially move on the X or Y axis. This callback leads to a crash when onHorizontalDragStart or onVerticalDragStart have been set—there is no way for the event system to distinguish between a pan and a drag.
  • onPanEnd: A pointer that had been in contact with the screen and was previously moving at a specific velocity loses contact with the screen. A crash is caused when the callback is set at onHorizontalDragEnd or onVerticalDragEnd.
  • onPanUpdate: A pointer that is currently in contact with the screen and moving in either a horizontal or vertical direction. Crashes are only experienced when the callback for onHorizontalDragUpdate or onVerticalDragUpdate is set.

Understanding Gesture Disambiguation

Multiple gesture detectors can be present at any given location on the screen. All of them follow the cues from the stream of pointer events that flow past. In an attempt to recognize specific gestures, the GestureDetector widget determines specified semantic gestures according to which callbacks have been set. So if you set a certain callback on the GestureDetector, the widget will try to interpret the pointer events in terms of that type of gesture.

The framework disambiguates gestures in the gesture arena. Each type of gesture has a recognizer which competes in the arena, examining the pointer position and movements events that come in. The recognizer which corresponds to the input gesture wins by following a few rules:

  • A recognizer is free to declare defeat at any time and leave the arena. When only one recognizer is left, then it wins!
  • A recognizer can declare victory at any time and force others to claim defeat.

For example, suppose you want to recognize vertical and horizontal drags. Recognizers for each direction will enter the gesture arena as soon as they receive the first event. They will then observe the movement in terms of logical pixels in any given direction.

If the move event proceeds mostly vertically, the vertical recognizer will declare victory due to the logical pixels crossed, and the gesture will be interpreted as a vertical drag on the screen. The same series of events can be repeated for horizontal drags too.

If you only want to recognize horizontal drags, then it doesn’t matter whether there is a vertical component to the movement; only the horizontal movement will be recognized.

Coding Touch Event Processing in Flutter

When working in Android, you can respond to button taps by binding OnClick through the setOnClickListener. But when it comes to Flutter, you have the choice of two different methods of adding a touch listener.

If the widget supports the use of listeners, then you can pass a callback function to process the event. Here is a sample of code for the onPressed and RaisedButton parameters:

And this is how it would look in your app.

onPressed action on a widgetonPressed action on a widgetonPressed action on a widget

GestureDetector Example

However, you’ll often want to use a listener that is not already supported by the widget. When a listener is not directly supported by the widget, you can wrap that widget in GestureDetector and pass the handler to the onTap parameter instead. Here’s how:

And this what the app will look like:

onTap action on a widget with GestureDetectoronTap action on a widget with GestureDetectoronTap action on a widget with GestureDetector

Animate on Double Tap Example

Here is another prime example of a touch event reaction for a double tap on the Flutter logo that causes a visual rotation.

onDoubleTap action on a widget with GestureDetectoronDoubleTap action on a widget with GestureDetectoronDoubleTap action on a widget with GestureDetector

Wondering how to listen for the double tap event? This is how:

Final Thoughts

Reacting to touch events in Flutter is not as complicated as one would assume. Understanding the nature of widgets and their ability to listen is the first lesson you need. All else follows smoothly from there.