Flutter 双指缩放和双指移动共存手势检测系列之–1方案
Flutter 双指缩放和双指移动共存手势检测
- 前言
- 关于Flutter缩放和移动
- 最终效果
- GestureDetector 移动和缩放动作
- 双指缩放和移动Gesture方案
- 源码解读
- 方案小结
-
- 小结
- 局限
- 代码
- 示例项目体验
- 本文源码
- 系列
前言
本文所采用Flutter 版本为 3.10.6, 也许后续版本官方会对手势做进一步丰富完善,以解决本文涉及到的解决方案。
本文涉及项目手势需求:双指向外或内触屏伸缩,对图片进行缩放;双指在屏幕上同向触屏移动,移动图片。
关于Flutter缩放和移动
Flutter 使用中采用GestureDetector进行移动或是手势判定,该手势检测器内置了移动,缩放,长按等动作检测。如果应用在缩放和移动上是单手势,该控件能够满足应用需求。但是如果是个复合手势,如手势可能是缩放也可能是移动,然后就可能会有冲突。
本文就是基于实际项目中遇到的需要双指缩放和双指移动并存手势,在GestureDetector基础上构建一个双手指并存手势控件
最终效果
这是制作好后最终效果图:

应用商店搜索七彩涂色体验
GestureDetector 移动和缩放动作
- GestureDetector 移动和手势列表
| 动作 | 回调 | 冲突动作 |
|---|---|---|
| 垂直移动 | onVerticalDrag* | 水平移动 |
| 水平移动 | onVerticalDrag* | 垂直移动 |
| 移动 | onPan* | 缩放 |
| 缩放 | onScale* | 移动 |
如上表所示,缩放和移动动作是相互冲突。如果同时指定了缩放回调和移动回调,就会遇到如下图的异常。

- GestureDetector 双指缩放和移动共存测试结果
| 组合 | 效果 | 结论 |
|---|---|---|
| 只移动 | 单双指均可行 | 单指头拖拽图片和手势跟随很好,但是双指拖拽移动增量超过双指移动 |
| 缩放 + 移动 | 直接异常 | 查看源码,无法共存,抛出异常 |
| 缩放 + 水平移动或垂直移动 | 不冲突,可移动 | 缩放或移动内部自行断定,效果很好;但是只能水平移动或垂直移动 |
| 缩放 + 水平移动+垂直移动 | 直接异常 | 查看源码,水平移动和垂直移动无法共存,抛出异常 |
从上述测试表中可以看到要实现本文的双指缩放和移动共存,采用GestureDetector 不可实现。
双指缩放和移动Gesture方案
- 方案思路
通过触摸点来进行来进行判断,如果触摸点之间距离在增加并大于阀值,则是缩放。如果触摸点之间距离相同,但是与起始点距离超过阀值,则是移动。
缩放示意图
缩放开始时,触点的中心位置基本保持不变。中心点相对不变则,但是触点距离变大,则是缩放动作。

移动示意图
移动开始时,触点之间相对距离不变,但是与起始点有较大位移。中心点位移,触摸点相对位置不变则是移动。
手势流程
2. 与**GestureDetector** 结合
本文中采用在GestureDetector 基础上实现双指缩放或移动,经过GestureDetector 源码查阅,决定在缩放回调基础上进行手指判断。因为缩放手势不仅对Scale 比例 进行了计算和回传,而且对触摸点位置也进行了回传。可以利用Scale 和缩放点判断,触摸点相对增加进行移动判断,减少工作量。
- 方案流程
- 效果
按上述方案,对GestureDetector 更新后,测试效果缩放和移动均和手势跟随效果很好,如下图:

源码解读
GestureDetector( onScaleStart: (details) { if (details.pointerCount >= 2) { /// 清空手势判断类型参数 offset = Offset.zero; scaleAccumulatedX = 0; scaleAccumulatedY = 0; twoTouchMode = TwoTuchMode.twoTouchNoneMode; } }, onScaleUpdate: (details) { if (details.pointerCount >= 2) { if (twoTouchMode == TwoTuchMode.twoTouchNoneMode) { twoTouchMode = checkTwoTouchesMoveMode(details); } if (twoTouchMode == TwoTuchMode.twoTouchZoomMode) { debugPrint("details.scale:${details.scale}"); scale = details.scale; setState(() {}); } else if (twoTouchMode == TwoTuchMode.twoTouchMoveMode) { offset += details.focalPointDelta; setState(() {}); } } },上述代码在接收scaleUpdate 时,如果还没有识别到手势,则先调用 checkTwoTouchesMoveMode 进行判断。已经识别到手势,就按已识别的手势进行缩放处理。
/// 判断手势类型是缩放还是移动
/// 缩放类型判断阀值
double constScaleThreshold = 0.1;
/// 移动类型判断阀值
double constPanningThreshold = 20;
/// 类型判断,移动累计值
double scaleAccumulatedX = 0;
double scaleAccumulatedY = 0;
TwoTuchMode checkTwoTouchesMoveMode(ScaleUpdateDetails touches) {
if ((1 - touches.scale).abs() >= constScaleThreshold) {
debugPrint("two touch scale mode:${(1 - touches.scale).abs()}");
return TwoTuchMode.twoTouchZoomMode;
}
scaleAccumulatedX += touches.focalPointDelta.dx;
scaleAccumulatedY += touches.focalPointDelta.dy;
if (scaleAccumulatedX.abs() >= constPanningThreshold ||
scaleAccumulatedY.abs() >= constPanningThreshold) {
debugPrint(
"two touch move mode dx:${scaleAccumulatedX.abs()} , dy:${scaleAccumulatedY.abs()}");
return TwoTuchMode.twoTouchMoveMode;
}
return TwoTuchMode.twoTouchNoneMode;
}
checkTwoTouchesMoveMode 进行缩放或是移动动作判断,缩放判断优先,如果超过阀值constScaleThreshold识别为缩放手势;横行或是纵向移动距离超过阀值 constPanningThreshold 识别为移动手势。
方案小结
小结
测试下来操作性还可以,代码在GestureDetector 基础上构建,也不复杂。
局限
代码没有进行过封装,与使用该缩放功能的代码混合编码,如果要跨项目使用,不便于功能更迭。
代码
本例代码在本文中全部打包提供。
环境:Flutter 3.10.6
示例项目体验
示例第一个截图,可以在应用商店搜索七彩涂色体验,由Flutter 构建的涂色应用。
本文源码
源码下载连接
系列
下一篇《Flutter 双指缩放和双指移动共存手势检测系列之–2封装》
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/ea5e0b5cae.html
