UIGestureRecognizer Tutorial in Flutter: Taps, Drags, and More!
Introduction
In this Flutter tutorial, you will learn how to add different types of gestures easily, also you will learn about drags to your app.
flutter1.22.3, Android studio 3.6.3
In Flutter, Gestures are primarily a way for the reader to interact with a mobile, using gestures like taps, pinches, pans, or rotations are used for the reader’s input. In your app, you can react to gestures, like a tap on text, without ever thinking about how to detect them. But in case you want to use gestures on views that don’t support them, you can easily do it with the built-in SDK
import package: flutter/gestures.dart libraries.
In this tutorial, you’ll learn how to add gesture recognizers to your app, programmatically using Dart language.
You’ll do this by creating an app where you can read online E-books using dragging, pinching, and rotating with the help of gesture recognizers.
You’ll also try out some cool extra features like:
This tutorial assumes you’re familiar with the basic concepts of Flutter and Dart. If you’re new to them, you may wish to check out our Flutter tutorials first.
It’s time to be a book geeks reader, so it’s time to get started!
Getting Started
Download and explore the starter project
Download the starter project using the?Materials?and open it up with your favorite IDE. This tutorial will be using Android Studio, but you can also use Visual Studio Code if that’s your preference. Make sure to run flutter packages get, either at the command line or when prompted by your IDE, to pull down the latest version of the HTTP package.
The starter project contains some basic models and networking files. When you open up the project it should look like this.
Gesture Detectors Overview
Before you get started, here’s a brief overview of why UIGestureDetectorss are so handy and how to use them.
Gestures are an interesting characteristic in Flutter that allows us to interact with the screen physically with different behaviors it can be single or multiple taps over the screen or maybe swiping in a different direction to perform a specific action, if you are trying to unlock your mobile phone with the pattern you will use gestures to draw your secret pattern also it’s heavily used in gaming to give you the ability to perform moves or using any gadgets in your game.
Gestures cycle
Gestures represent semantic actions like taps, drags, and scales that are recognized from pointer-events over the screen, so if you unlock your phone with the pattern, the cycle will be onPanStart() then when you start to move onPanUpdate() will be called, when you remove your hand the onPanEnd() will be called.
Create a gesture recognizer
When you create a gesture detector, you specify a target widget to be tapped and an action to be performed so the gesture detector widget can send you updates when the gesture starts, changes or ends.
Adding gestures to the views
You wrapped your target widget with a gesture detector widget. When a touch occurs within the bounds of that view, the gesture detector will check if it matches the type of touch it’s looking for. If it finds a match, it notifies the target.
You will perform these two steps later in this tutorial step by step, so Keep Going!
Implementing Tap Gestures
When you tap over the screen surface using your fingerprints The TapgesterRecognizer class is called to identify the Tap action type.
Using onTap() Gesture
The first implementation for Gestures will be onTap() To capture Tap actions over text widget so you will wrap the Padding widget with GestuerDetector() on bookTopic() widget then add the onTap() implementation inside.
onTap: () {
AlertViewDialogue().createAlertDialogue(context, false);
}
Now Build and run then tap over the text widget the alert dialogue should be viewed.
Great, you have implemented your first Tap Gesture, Now Let’s create more types of them.
Using onDoubleTap() Gesture
In GesterDetector() widget After onTap() you will add onDoubleTap() to allow the user to change the app to the dark mode, onDoubleTap() is similar to onTap() gesture, but it requires to tap twice over the widget to be triggered and execute its implementation.
onDoubleTap: () {
setState(() {
_changeColor = !_changeColor;
});
},
Now Build and run then tap twice over the text widget the app will change to the dark mode.
Using onLongPress() Gesture
So what if the reader has a LongPress() tap over text, so the alert dialogue should appear and view the source of our article and allow him to copy the link of our website, you should add.
onLongPress: () {
AlertViewDialogue().createAlertDialogue(context, true);
},
Now Build and run then tap and hold once over the text widget the alert dialogue should appear and contains the buttons to copy the meduim link.
Hooray, Now we have implemented several Tap detectors over the widget, Keep Going (Y)
Connecting Drag Gestures
You may notice that users need to change the topics in the E-book so in our design, we need users to change the page using dragging.
Dragging over widgets using pointers to describe the location and movement of the event in the screen surface
There are two types of dragging Vertical drag and Horizontal drag, each one of them has there lifecycle when the user starts, moves then removes its finger, you will implement both of them to let the user move forward and backward
When the pointer starts to contact the screen the lifecycle of drag will start then update then end providing us with Drag details that contain the velocity was moving along with the primary axis (X, Y)
Horizontal Dragging
When the reader will release his finger after dragging we need to check the values if it’s positive so that means we need to go to the next page if negative we need to back previous so add these lines in changePageview() after wrapping its container with GesterDetector widget.
GestureDetector(
onDoubleTap: () {
setState(() {
_changeColor = !_changeColor;
});
},
onHorizontalDragEnd: (DragEndDetails details) {
setState(() {
if (details.primaryVelocity > 1) {
_pageNumber <= 4 && _pageNumber != 0
? _pageNumber —
: _pageNumber = 4;
} else {
_pageNumber >= 0 && _pageNumber != 4
? _pageNumber++
: _pageNumber = 0;
}
});
},
Now Build and run you have to swipe right or left in the arrow’s area to change the page.
Vertical Dragging
If you need to do it with a vertical direction, You should add the same implementation but inside VerticalDragEnd()
onVerticalDragEnd: (DragEndDetails details) {
setState(() {
if (details.primaryVelocity > 1) {
_pageNumber <= 4 && _pageNumber != 0
? _pageNumber —
: _pageNumber = 4;
} else {
_pageNumber >= 0 && _pageNumber != 4
? _pageNumber++
: _pageNumber = 0;
}
});
},
Now Build and run you have to swipe up or down in the arrow’s area to change the page.
Good job till now let’s deep dive more!
Implementing Pan Gesture
What about if the reader wants to select or Mark a certain part of the text in the book, you should add this feature to your E-book as below.
To implement this we need to add PanGesture() means touching the surface of the screen with a fingertip, which can move in any direction without releasing the fingertip. This gesture contains events:
Adding Pan Gesture To The widget
In The bookTopic() widget you will add the below code right after onLongPress()
onPanStart: (DragStartDetails details) {
_points.clear();
},
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox object = context.findRenderObject();
Offset _localPosition = object.globalToLocal(details.localPosition);
_points = new List.from(_points)..add(_localPosition);
});
},
onPanEnd: (DragEndDetails details) => _points.add(null),
Screen Painting using onPanUpdate()
Inside setState() you will add RenderBox they size themselves based on those constraints and whatever other inputs they may have
The local position in the coordinate system of the event receiver at which the pointer contacted the screen
- Points mean the starting and the ending points you draw.
- Canvas current transformation matrix which applied all operations.
To paint over the screen your widget is wrapped with customPaint() to allow the user to draw over the screen and the ScreenDrawing class constructs an empty paint object while drawing and provides its attributes.
Now Build and run and try to mark any part of the text.
Using Scaling and Rotation Gestures
The Banner image maybe looks smaller, you need to add a zooming ability to the image also changing its orientation will be nice!
If you need to add this feature it requires a scaling widget that allows you to scale up or down easily.
Allow zooming Into the Widget
In the coverImageView() widget you need to wrap your container with GesterDetector() then add the following:
onScaleStart: (ScaleStartDetails details) {
_previousScale = _scale;
setState(() {});
},
onScaleUpdate: (ScaleUpdateDetails details) {
_scale = _previousScale * details.scale;
setState(() {});
领英推荐
},
onScaleEnd: (ScaleEndDetails details) {
_scale = 1.0;
setState(() {});
},
Using Rotation To Widget
For the Rotating image, you should wrap it with RotatedBox() widget and Transform() widget as well
Rotated Box: A widget that provides an easy way to rotate its child widget by the number of quarter turns
Transform: A widget that applies a transformation before painting its child using Matrices
Now Build and run and try to zoom using your fingers, also change image orientation while clicking over the app bar button.
Great, Let’s keep moving
Adding Reset Button
After readers draw over the screen they may need to remove the marked part, so you need to add a Reset button to remove points add over the screen.
Removing screen painting
You need to clear the screen using the points below.
setState(() {
_points.clear();
});
Now Build and run you should notice point removal
Handling multiple gestures
Sometimes you will need to add one or more gestures to the widget of the same types, but can you add onTap() twice in GesterDetector() widget?
The answer is no, the solution is to develop RawGestureDetector and a custom ‘GestureRecognizer’.
The custom GestureRecognizer provides Allow “MultipleGestureRecognizer” property to the gesture list and creates a
GestureRecognizerFactoryWithHandlers
Framework disambiguation
Using Multiple gestures listen to a stream of pointer events The GestureDetector widget decides which gestures to attempt to recognize based on which of its callbacks are non-null.
When there is more than one tap over the widget the framework disambiguates which gesture the user intends by having each recognizer join the gesture arena and determine which one will be executed.
Gesture arena
The gesture arena determines which gesture will be executed using the following rules:
In your app, when disambiguating the onTap() detector, recognizers enter the arena when they receive the pointer down event. The recognizers observe the pointer move events.
Creating Custom Gesture
*You have three widgets to implement onTap() over them*
When the reader clicks over Copy Button it will copy the article URL if he clicked the visit site Button medium website link will be copied and the last part needs to be handled when he clicks in the empty space so the alert will disappear.
implement your own GestureDetector, using RawGestureDetector
CustomGestureRecognizer to the GestureRecognizerFactory in my RawGestureDetector to deal with the taps
Use instance to call the gesture type and add your logic inside
You will replace your container in the alert dialogue with the following implementation.
RawGestureDetector(
gestures: {
AllowMultipleGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
AllowMultipleGestureRecognizer>(
() => AllowMultipleGestureRecognizer(), //constructor
(AllowMultipleGestureRecognizer instance) {
//initializer
instance.onTap = () {
print(“Row Tapped”);
Navigator.of(dialogContext).pop();
};
},
)
},
child: Container(
height: 50,
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RawGestureDetector(
gestures: {
AllowMultipleGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
AllowMultipleGestureRecognizer>(
() =>
AllowMultipleGestureRecognizer(), //constructor
(AllowMultipleGestureRecognizer instance) {
//initializer
instance.onTap = () {
Clipboard.setData(new ClipboardData(
text: Constants.rayUrl));
showToastMessage(context);
print(‘Copy Button Tapped’);
};
},
)
},
child: Container(
child: Center(
child: Text(
Constants.copyButton,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 12),
),
)),
),
RawGestureDetector(
behavior: HitTestBehavior.deferToChild,
gestures: {
AllowMultipleGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
AllowMultipleGestureRecognizer>(
() =>
AllowMultipleGestureRecognizer(), //constructor
(AllowMultipleGestureRecognizer instance) {
//initializer
instance.onTap = () {
Clipboard.setData(new ClipboardData(
text: Constants.mainUrl));
showToastMessage(context);
print(‘visit Site Tapped’);
};
},
)
},
child: Container(
height: 50,
child: Center(
child: Text(
Constants.visit,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 12),
),
)),
),
],
),
),
),
Now Build and run the tap over text to view the alert dialogue and test your behavior!
Where to Go From Here?
Download the completed version of the project from the?Materials.
Congrats, you’re now a master of gesture detectors — both built-in and custom ones! Touch interaction is such an important part of flutter development and UIGestureDetector is the key to adding easy-to-use gestures beyond simple button taps.
There are many ways to add custom gestures and there is a challenge for you if you would link to make a more complex Custom Gesture you can mix two pan gestures together!
There is much more to learn and very useful resources to dig deep in you could check the official documentation.
I hope you enjoyed this tutorial! If you have any questions or comments, please join the discussion below.