結論
FlutterのGestureDetectorは1回のタップで1つのイベントしか発火されないようになっているため、RawGestureDetectorを使う必要があります。
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class TitleScreen extends HookWidget {
const TitleScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: _GetAllScreenTapEventWidget(
child: Center(
child: SizedBox(
width: 100,
height: 100,
child: GestureDetector(
onTap: () => print("button tap event"),
child: const Card(
color: Colors.red,
child: Text('Tap me'),
),
),
),
),
),
);
}
}
class _GetAllScreenTapEventWidget extends HookWidget {
final Widget child;
const _GetAllScreenTapEventWidget({
required this.child,
});
@override
Widget build(BuildContext context) {
void onTapDown(details) {
print("all screen tap event");
}
return Stack(
fit: StackFit.expand,
children: [
child,
RawGestureDetector(
behavior: HitTestBehavior.translucent,
gestures: <Type, GestureRecognizerFactory>{
_TapAnimationGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
_TapAnimationGestureRecognizer>(
() {
return _TapAnimationGestureRecognizer(onTapDown: onTapDown);
},
(_) {},
),
},
),
],
);
}
}
class _TapAnimationGestureRecognizer extends OneSequenceGestureRecognizer {
_TapAnimationGestureRecognizer({required this.onTapDown});
final Function(PointerEvent) onTapDown;
bool _isDown = false;
@override
void addAllowedPointer(PointerDownEvent event) {
startTrackingPointer(event.pointer);
// イベントを他のオブジェクトに伝搬させるためにrejectする
resolve(GestureDisposition.rejected);
}
@override
String get debugDescription => 'recognizer for duplicate tap down event';
@override
void didStopTrackingLastPointer(int pointer) {}
@override
void handleEvent(PointerEvent event) {
if (event.down) {
if (!_isDown) {
onTapDown(event);
_isDown = true;
}
} else {
_isDown = false;
}
}
}
動作結果
Tap meの領域を押すと、2つのタップイベントが発生して以下のようなログとなります。
Tap meの領域外を押すと、片方のタップイベントのみが発生して以下のようなログになります。
用途
ゲームアプリ等で良くある、タップした場所が光るアニメーションを実装するために作成しました。
他のボタンがある場所を押した場合でも、タップアニメーションを表示するためにイベントを拾う必要があります。
サンプルコードのようなコードを活用して、以下のようなタップアニメーションを作りました。
ホシヅクリというボードゲームのアプリ版で、以下にて配信中です。
iOS版
https://apps.apple.com/jp/app/%E3%83%9B%E3%82%B7%E3%83%85%E3%82%AF%E3%83%AA/id6449194423
Android版
https://play.google.com/store/apps/details?id=org.silverroom.tellus&pli=1
※執筆時点ではまだタップのアニメーションはリリースされていませんが、次回リリースに含まれる予定です。
参考記事
Flutterのタップイベントについてわかりやすく解説されているので、何故タップイベントが1つしか発火しないか気になる方はこちらの記事を見ることをおすすめします。
https://qiita.com/mjhd/items/8e300238e494d8755b44