1. GestureDetector
GestureDetector 定义
GestureDetector({
Key? key,
this.child,
this.onTapDown,
this.onTapUp,
this.onTap,
this.onTapCancel,
this.onSecondaryTap,
this.onSecondaryTapDown,
this.onSecondaryTapUp,
this.onSecondaryTapCancel,
this.onTertiaryTapDown,
this.onTertiaryTapUp,
this.onTertiaryTapCancel,
this.onDoubleTapDown,
this.onDoubleTap,
this.onDoubleTapCancel,
this.onLongPressDown,
this.onLongPressCancel,
this.onLongPress,
this.onLongPressStart,
this.onLongPressMoveUpdate,
this.onLongPressUp,
this.onLongPressEnd,
this.onSecondaryLongPressDown,
this.onSecondaryLongPressCancel,
this.onSecondaryLongPress,
this.onSecondaryLongPressStart,
this.onSecondaryLongPressMoveUpdate,
this.onSecondaryLongPressUp,
this.onSecondaryLongPressEnd,
this.onTertiaryLongPressDown,
this.onTertiaryLongPressCancel,
this.onTertiaryLongPress,
this.onTertiaryLongPressStart,
this.onTertiaryLongPressMoveUpdate,
this.onTertiaryLongPressUp,
this.onTertiaryLongPressEnd,
this.onVerticalDragDown,
this.onVerticalDragStart,
this.onVerticalDragUpdate,
this.onVerticalDragEnd,
this.onVerticalDragCancel,
this.onHorizontalDragDown,
this.onHorizontalDragStart,
this.onHorizontalDragUpdate,
this.onHorizontalDragEnd,
this.onHorizontalDragCancel,
this.onForcePressStart,
this.onForcePressPeak,
this.onForcePressUpdate,
this.onForcePressEnd,
this.onPanDown,
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
this.onPanCancel,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd,
this.behavior,
this.excludeFromSemantics = false,
this.dragStartBehavior = DragStartBehavior.start,
})
-
onTap
单击事件回调 -
onDoubleTap
双击事件回调 -
onLongPress
长按事件回调
2. GestureDetector 手势事件监听
2.1 单击 双击 长按
示例 1
GestureDetector对Container进行手势识别,触发相应事件后,在Container上显示事件名
class MSGestureDetectorDemo extends StatefulWidget {
const MSGestureDetectorDemo({Key? key}) : super(key: key);
@override
State<MSGestureDetectorDemo> createState() => _MSGestureDetectorDemoState();
}
class _MSGestureDetectorDemoState extends State<MSGestureDetectorDemo> {
String _operation = "";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("GestureDetectorDemo")),
body: Center(
child: GestureDetector(
onTap: () => _updateText("Tap"),
onDoubleTap: () => _updateText("DoubleTap"),
onLongPress: () => _updateText("LongPress"),
child: Container(
width: 200,
height: 200,
color: Colors.amber,
alignment: Alignment.center,
child: Text(_operation),
),
),
),
);
}
_updateText(String text) {
setState(() {
_operation = text;
});
}
}

注意: 当同时监听
onTap
和onDoubleTap
事件时,当用户触发tap事件时,会有200毫秒左右的延时,这是因为当用户点击完之后很可能会再次点击以触发双击事件,所以GestureDetector
会等一段时间来确定是否为双击事件。如果用户只监听了onTap
(没有监听onDoubleTap
)事件时,则没有延时。
2.2 拖动、滑动
一次完整的手势过程是指用户手指按下到抬起的整个过程,期间,用户按下手指后可能会移动,也可能不会移动。GestureDetector对于拖动和滑动事件是没有区分的,他们本质上是一样的。GestureDetector会将要监听的组件的原点(左上角)作为本次手势的原点,当用户在监听的组件上按下手指时,手势识别就会开始。
示例 2
class MSGestureDetectorDemo2 extends StatefulWidget {
const MSGestureDetectorDemo2({Key? key}) : super(key: key);
@override
State<MSGestureDetectorDemo2> createState() => _MSGestureDetectorDemo2State();
}
class _MSGestureDetectorDemo2State extends State<MSGestureDetectorDemo2> {
double _left = 0.0;
double _top = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("GestureDetectorDemo2")),
body: Stack(
children: [
Positioned(
top: _top,
left: _left,
child: GestureDetector(
child: CircleAvatar(
child: Text("A"),
),
onPanDown: (DragDownDetails details) {
print("onPanDown ${details.globalPosition}");
},
onPanUpdate: (DragUpdateDetails details) {
print("onPanUpdate ${details.delta}");
setState(() {
_top += details.delta.dy;
_left += details.delta.dx;
});
},
onPanEnd: (DragEndDetails details) {
print("onPanEnd ${details.velocity}");
},
),
),
],
),
);
}
}
代码解释:
-
DragDownDetails.globalPosition
:当用户按下时,此属性为用户按下的位置相对于屏幕(而非父组件)原点(左上角)的偏移。 -
DragUpdateDetails.delta
:当用户在屏幕上滑动时,会触发多次Update事件,delta
指一次Update事件的滑动的偏移量。 -
DragEndDetails.velocity
:该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的),示例中并没有处理手指抬起时的速度,常见的效果是根据用户抬起手指时的速度做一个减速动画。

示例3 单一方向拖动
class MSGestureDetectorDemo3 extends StatefulWidget {
const MSGestureDetectorDemo3({Key? key}) : super(key: key);
@override
State<MSGestureDetectorDemo3> createState() => _MSGestureDetectorDemo3State();
}
class _MSGestureDetectorDemo3State extends State<MSGestureDetectorDemo3> {
var _top = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("GestureDetectorDemo3")),
body: LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: [
Positioned(
top: _top,
child: GestureDetector(
child: CircleAvatar(
child: Text("A"),
),
// 垂直方向拖动事件
onVerticalDragUpdate: (DragUpdateDetails details) {
setState(() {
_top += details.delta.dy;
if (_top < 0) {
_top = 0;
}
if (_top > constraints.maxHeight) {
_top = constraints.maxHeight - 20;
}
});
},
),
),
],
);
},
),
);
}
}
这样就只能在垂直方向拖动了,如果只想在水平方向滑动同理

2.3 缩放
GestureDetector可以监听缩放事件,下面示例演示了一个简单的图片缩放效果
class MSGestureDetectorDemo4 extends StatefulWidget {
const MSGestureDetectorDemo4({Key? key}) : super(key: key);
@override
State<MSGestureDetectorDemo4> createState() => _MSGestureDetectorDemo4State();
}
class _MSGestureDetectorDemo4State extends State<MSGestureDetectorDemo4> {
double _width = 200;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("GestureDetectorDemo4")),
body: Center(
child: GestureDetector(
child: Image.asset(
"assets/images/4.jpeg",
fit: BoxFit.cover,
width: _width,
),
onScaleUpdate: (ScaleUpdateDetails details) {
_width = 200 * details.scale.clamp(0.8, 5.0);
setState(() {});
},
),
),
);
}
}

3. GestureRecognizer
GestureDetector
内部是使用一个或多个GestureRecognizer
来识别各种手势的,而GestureRecognizer
的作用就是通过Listener
来将原始指针事件转换为语义手势,GestureDetector
直接可以接收一个子widget。GestureRecognizer
是一个抽象类,一种手势的识别器对应一个GestureRecognizer
的子类,Flutter实现了丰富的手势识别器,我们可以直接使用。
示例
假设我们要给一段富文本(RichText)的不同部分分别添加点击事件处理器,但是TextSpan并不是一个widget,这时我们不能用GestureDetector,但TextSpan有一个recognizer属性,它可以接收一个GestureRecognizer。
class MSGestureDetectorDemo5 extends StatefulWidget {
const MSGestureDetectorDemo5({Key? key}) : super(key: key);
@override
State<MSGestureDetectorDemo5> createState() => _MSGestureDetectorDemo5State();
}
class _MSGestureDetectorDemo5State extends State<MSGestureDetectorDemo5> {
TapGestureRecognizer _tapGestureRecognizer = TapGestureRecognizer();
bool _toggle = false; // 变色开关
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("GestureDetectorDemo5")),
body: Center(
child: Text.rich(
TextSpan(
children: [
TextSpan(text: "你好世界"),
TextSpan(
text: "点击变色",
style: TextStyle(
fontSize: 30, color: _toggle ? Colors.red : Colors.blue),
recognizer: _tapGestureRecognizer
..onTap = () {
setState(() {
_toggle = !_toggle;
});
},
),
],
),
),
),
);
}
@override
void dispose() {
_tapGestureRecognizer.dispose();
super.dispose();
}
}

注意:使用GestureRecognizer后一定要调用其dispose()方法来释放资源(主要是取消内部的计时器)。
网友评论