1. はじめに
タッチ/ボタン入力検出用途で利用する、GestureDetectorの使い方についてのまとめ記事です。
サポート済みの入力デバイス
GestureDetector
は、PrimaryとSecondaryの2つのボタン入力をサポートしています。基本的にはPrimaryのタッチ入力のみの利用になると思います。デスクトップ環境であれば、マウスボタンでも反応します (現状、Secondaryはどこにも接続されていない?)。
Flutter EngineのAPI経由で各種プラットフォームの入力デバイスI/Fと接続することで、Flutter FrameworkのDart側で入力を検出する仕組みです。
kPrimaryStylusButton: The stylus contacts the screen.
kSecondaryMouseButton: The primary mouse button.
2. 基本的な使い方
タッチを検出したいWidgetの親WidgetにGestureDetectorを利用します。タッチ検出にはonTap
プロパティを利用します。
GestureDetector(
onTap: () {
// シングルタップ時に呼ばれる
},
// タッチ検出対象のWidget
child: Text(
'How to use GestureDetector',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
),
)
behaviorプロパティについて
GestureDetectorのbehaviorプロパティは意外と重要です。
デフォルトはHitTestBehavior.deferToChildですが、このプロパティに設定するenum値に連動してGesture検出時の動きが変わります。子WidgetのGesture検出範囲に応じて適切に設定しましょう。
GestureDetector(
behavior: HitTestBehavior.opaque, // こんな感じで指定
HitTestBehavior enum選択肢 | 内容 |
---|---|
deferToChild | 子Widgetの有効範囲のみ反応するようにする |
opaque | Padding領域も含めてタッチに反応するようにする |
translucent | 背景色が透明の場合、そのWidgetの背景部分をタッチしても反応するようにする |
3. 検出可能なイベント一覧とコールバック
検出可能なイベントとプロパティ名を以下にまとめています。
コールされる順番については、後述のユースケースの部分を参照して下さい。
シングルタップ
スクリーン/ボタンの一回押し操作のイベントを検出する時に利用します。
タップ操作時の途中経過を知る必要がなければ、通常はonTap
のみの利用になると思います。
GestureDetector(
...
// シングルタップ開始時にコール
// void Function(TapDownDetails) onTapDown
onTapDown: (details) => print('onTapDown()'),
// シングルタップ開始後、そのままタッチを動かすなどしてシングルタップがキャンセルされる時にコール
// void Function() onTapCancel
onTapCancel: () => print('onTapCancel()'),
// タップが離れる時にコール
// void Function(TapUpDetails) onTapUp
onTapUp: (details) => print('onTapUp()'),
// シングルタップ確定時にコール
// void Function() onTap
onTap: () => print('onTap()'),
)
TapUpDetailsのメンバ変数
実際に利用しそうなメンバ変数のみ紹介しておきます。globalPosition
は公式サイトの説明ではスクリーン座標と書いてあり、スマホ本体のスクリーンの絶対座標かと思ってしまいますが、そうではなくてアプリ内画面の絶対座標です。Androidスマホであれば、アプリを画面分割して起動させて動作確認してみれば分かるかと思います。
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
TapDownDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
kind | 入力デバイス種類 |
PointerDeviceKindクラスのkind
は、入力デバイスの種類を示しており、mouse
やtouch
のようなenum値が格納されています。入力デバイスを見分けたい場合には便利かもしれません。
タブルタップ
素早くタッチ2回操作ですね。
これが反応する時は、onTap系のコールバックは当然コールされません。
GestureDetector(
...
// ダブルタップ確定時にコール
// void Function() onDoubleTap
onDoubleTap: () => print('onDoubleTap()'),
)
ロングタップ
タッチ後、そのまましばらく静止する操作です。その状態でタッチ位置を移動するとそれも検出可能です。
onLongPressEndとonLongPressUpの厳密な違いは何でしょう? (ソースコード読むか。。)
GestureDetector(
...
// ロングプレス検出処理開始時にコール
// void Function(LongPressStartDetails) onLongPressStart
onLongPressStart: (details) => print('onLongPressStart()'),
// ロングプレス確定時にコール
// void Function() onLongPress
onLongPress: () => print('onLongPress()'),
// ロングプレス状態での移動で位置が変化した時にコール
// void Function(LongPressMoveUpdateDetails) onLongPressMoveUpdate
onLongPressMoveUpdate: (details) => print('onLongPressMoveUpdate()'),
// ロングタップ状態が解除された (実質、タッチが離れた) 時にコール。onLongPressUpより先にコール
// void Function(LongPressEndDetails) onLongPressEnd
onLongPressEnd: (details) => print('onLongPressEnd()'),
// ロングタップ状態が解除されて、タッチが離れた時にコール
// void Function() onLongPressUp
onLongPressUp: () => print('onLongPressUp()'),
)
LongPressStartDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
LongPressEndDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
velocity | 画面からタッチが離れる速度 |
LongPressMoveUpdateDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
offsetFromOrigin | 最初のロングタップ検出位置からの相対座標 |
localOffsetFromOrigin |
3Dタップ
iOS専用の機能です。
それ以外のプラットフォームでは利用しません (何も反応しません)。
GestureDetector(
...
// 3Dタップ開始時にコール
// void Function(ForcePressDetails) onForcePressStart
onForcePressStart: (details) => print('onForcePressStart()'),
// 押し込みの強さが変化した時にコール
// void Function(ForcePressDetails) onForcePressUpdate
onForcePressUpdate: (details) => print('onForcePressUpdate()'),
// 押し込みの強さが最大時にコール
// void Function(ForcePressDetails) onForcePressPeak
onForcePressPeak: (details) => print('onForcePressPeak()'),
// 3Dタップ終了時にコール
// void Function(ForcePressDetails) onForcePressEnd
onForcePressEnd: (details) => print('onForcePressEnd()'),
)
ForcePressDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
pressure | 押し込みの強さ |
スケール
2本指でのピンチ (イン/アウト/回転) 操作用です。ただし、1タッチ操作でも反応します。
また、後述のジェスチャーの所に記載しているonHorizontalDrag*/onVerticalDrag*/onPan*
系のプロパティと同時に利用するとエラーになるため、注意して下さい。
GestureDetector(
...
// スケール操作開始時にコール
// void Function(ScaleStartDetails) onScaleStart
onScaleStart: (details) => print('onScaleStart'),
// スケール操作によるズーム倍率や回転率の変更時にコール
// void Function(ScaleUpdateDetails) onScaleUpdate
onScaleUpdate: (details) => print('onScaleUpdate'),
// スケール操作終了時にコール
// void Function(ScaleEndDetails) onScaleEnd
onScaleEnd: (details) => print('onScaleEnd'),
)
ScaleStartDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
focalPoint | アプリ画面内の絶対座標 |
localFocalPoint | Widget内の相対座標 |
ScaleUpdateDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
focalPoint | アプリ画面内の絶対座標 |
localFocalPoint | Widget内の相対座標 |
rotation | 開始時点からの回転角度 |
scale | 開始時点からの倍率 |
verticalScale | 開始時点からの垂直方向の倍率 |
ScaleEndDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
velocity | 画面からタッチが離れる速度(スワイプ操作の強さ検出に使えます) |
ジェスチャー (水平方向)
水平方向のジェスチャー検出やドラッグ操作検出用です。
GestureDetector(
...
// 初回タップ時にコール
// void Function(DragDownDetails) onHorizontalDragDown
onHorizontalDragDown: (details) => print('onHorizontalDragDown()'),
// 初回タップ後にタップが離れたり、水平方向に移動した場合にコール
// void Function() onHorizontalDragCancel
onHorizontalDragCancel: () => print('onHorizontalDragCancel()'),
// onHorizontalDragDownコール後の初回水平方向に移動時にコール
// void Function() onHorizontalDragCancel
onHorizontalDragStart: (details) => print('onHorizontalDragStart()'),
// 水平方向に移動した時にコール
// void Function() onHorizontalDragCancel
onHorizontalDragUpdate: (details) => print('onHorizontalDragUpdate()'),
// タッチを離すなど、水平方向の移動終了時にコール
// void Function(DragEndDetails) onHorizontalDragEnd
onHorizontalDragEnd: (details) => print('onHorizontalDragEnd()'),
)
DragDownDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
DragStartDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
sourceTimeStamp | タイムスタンプ |
DragUpdateDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
primaryDelta | |
delta |
ジェスチャー (垂直方向)
onHorizontalDrag*同様、onVerticalDragDown/onVerticalDragCancel/onVerticalDragStart/onVerticalDragUpdate/onVerticalDragEnd
が用意されています。
ドラッグ
前述の水平垂直お構いなしにドラッグ操作で位置を検出したい場合に利用します。
GestureDetector(
...
// 初回タップ時にコール
// void Function(DragDownDetails) onPanDown
onPanDown: (details) => print('onPanDown()'),
// ドラッグ操作がキャンセルされた時にコール
// void Function() onPanCancel
onPanCancel: () => print('onPanCancel()'),
// ドラッグ操作が開始された時にコール
// void Function(DragStartDetails) onPanStart
onPanStart: (details) => print('onPanStart()'),
// ドラッグ操作で位置が変化した時にコール
// void Function(DragUpdateDetails) onPanUpdate
onPanUpdate: (details) => print('onPanUpdate()'),
// ドラッグ操作が終了した時にコール
// void Function(DragEndDetails) onPanEnd
onPanEnd: (details) => print('onPanEnd()'),
DragDownDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
DragStartDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
sourceTimeStamp | タイムスタンプ |
DragUpdateDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
globalPosition | アプリ画面内の絶対座標 |
localPosition | Widget内の相対座標 |
sourceTimeStamp | タイムスタンプ |
delta | 前回からの変化量 |
primaryDelta | 画面からタッチが離れる速度(スワイプ操作の強さ検出に使えます) |
DragEndDetailsのメンバ変数
メンバ変数 | 内容 |
---|---|
primaryVelocity | |
velocity |
セカンダリーボタン
Android/iOSの通常操作ではまず使う (反応する) ことがないと思います。
無視しましょう。
GestureDetector(
...
// void Function(TapDownDetails) onSecondaryTapDown
onSecondaryTapDown: (details) => print('onSecondaryTapDown()'),
// void Function(TapUpDetails) onSecondaryTapUp
onSecondaryTapUp: (details) => print('onSecondaryTapUp()'),
// void Function() onSecondaryTapCancel
onSecondaryTapCancel: () => print('onSecondaryTapCancel'),
)
4. ユースケース毎のコール順序
以下のユースケース毎に各コールバックイベントがどのように発生するのか、その確認結果を掲載しておきます。
シングルタップ操作時
onTap系
onTapDown()
onTapUp()
onTap()
# タッチ後、指をそのまま異動
onTapDown()
onTapCancel()
ドラッグ(onPan)系
onPanDown()
onPanCancel()
スケール操作時
onScaleStart()
onScaleUpdate()
onScaleEnd()
ピンチ操作時
onPanDown()
onPanStart()
onPanUpdate()
onPanEnd()
ダブルタップ操作時
onDoubleTap()
ロングプレス操作時
# 最初はシングルタップから反応
onTapDown()
onTapCancel()
# そのあとでロングタップ判定に移行
onLongPressStart()
onLongPress()
# そのままタッチを動かす
onLongPressMoveUpdate()
# ここでタッチを離す
onLongPressEnd()
onLongPressUp()