From c7c6bd01ca694372ea89d953564407a7b0d31b7d Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Mon, 2 Oct 2023 14:07:11 -0700 Subject: [PATCH] Fixed PositionedTapDetector2's transformation of global to local coords (#1676) --- .../private/positioned_tap_detector_2.dart | 15 +--- .../positioned_tap_detector_2_test.dart | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 test/misc/private/positioned_tap_detector_2_test.dart diff --git a/lib/src/misc/private/positioned_tap_detector_2.dart b/lib/src/misc/private/positioned_tap_detector_2.dart index bab7c379e..c65e454c7 100644 --- a/lib/src/misc/private/positioned_tap_detector_2.dart +++ b/lib/src/misc/private/positioned_tap_detector_2.dart @@ -164,23 +164,10 @@ class _TapPositionDetectorState extends State { ) async { _firstTap = null; if (callback != null) { - callback(_getTapPositions(details)); + callback(TapPosition(details.globalPosition, details.localPosition)); } } - TapPosition _getTapPositions(TapDownDetails details) { - final topLeft = _getWidgetTopLeft(); - final global = details.globalPosition; - final relative = topLeft != null ? global - topLeft : null; - return TapPosition(global, relative); - } - - Offset? _getWidgetTopLeft() { - final translation = - context.findRenderObject()?.getTransformTo(null).getTranslation(); - return translation != null ? Offset(translation.x, translation.y) : null; - } - @override void dispose() { _controller.close(); diff --git a/test/misc/private/positioned_tap_detector_2_test.dart b/test/misc/private/positioned_tap_detector_2_test.dart new file mode 100644 index 000000000..e30fd8ff5 --- /dev/null +++ b/test/misc/private/positioned_tap_detector_2_test.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Test tap is detected where expected.', (tester) async { + const screenSize = Size(400, 400); + await tester.binding.setSurfaceSize(screenSize); + + // PositionedTapDetector2 fills the full screen. + Offset? lastTap; + final widget = PositionedTapDetector2( + onTap: (position) => lastTap = position.relative, + child: SizedBox( + width: screenSize.width, + height: screenSize.height, + child: Container( + color: Colors.red, + ), + ), + ); + + await tester.pumpWidget(widget); + + Future tap(Offset pos) async { + lastTap = null; + await tester.tapAt(pos); + expect(lastTap, pos); + } + + // Tap top left + await tap(Offset.zero); + // Tap middle + await tap(Offset(screenSize.width / 2, screenSize.height / 2)); + // Tap bottom right + await tap(Offset(screenSize.width - 1, screenSize.height - 1)); + }); + + testWidgets('Test tap is detected where expected with scale and offset.', + (tester) async { + const screenSize = Size(400, 400); + await tester.binding.setSurfaceSize(screenSize); + + Offset? lastTap; + // The Transform.scale fills the screen, but the PositionedTapDetector2 + // occupies the center, with height of 200 (0.5 * 400) and width 400. + final widget = Transform.scale( + scaleY: 0.5, + child: PositionedTapDetector2( + onTap: (position) => lastTap = position.relative, + child: SizedBox( + width: screenSize.width, + height: screenSize.height, + child: Container( + color: Colors.red, + ), + ), + ), + ); + + await tester.pumpWidget(widget); + + // On the screen the PositionedTapDetector2 is actually 400x200, but the + // widget thinks its 400x400. + expect(screenSize, tester.getSize(find.byType(SizedBox))); + + Future tap(Offset pos, Offset expected) async { + lastTap = null; + await tester.tapAt(pos); + expect(lastTap, expected); + } + + // Tap top left of PositionedTapDetector2 which should be 0,0. + await tap(const Offset(0, 100), Offset.zero); + + // Tap bottom right of PositionedTapDetector2 + await tap(const Offset(400 - 1, 300 - 0.5), + Offset(screenSize.width - 1, screenSize.height - 1)); + }); +}