blob: 1a7c39b6823d0794cb4bd9b1dea53d7ba51447d2 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
void main() {
group('Horizontal', () {
testWidgets('gets local coordinates', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: GestureDetector(
onHorizontalDragCancel: () {
dragCancelCount++;
},
onHorizontalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onHorizontalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onHorizontalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
);
await tester.drag(find.byKey(redContainer), const Offset(100, 0));
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50, 75));
expect(startDetails.single.globalPosition, const Offset(400, 300));
expect(updateDetails.last.localPosition, const Offset(50 + 100.0, 75));
expect(updateDetails.last.globalPosition, const Offset(400 + 100.0, 300));
expect(
updateDetails.fold(Offset.zero, (Offset offset, DragUpdateDetails details) => offset + details.delta),
const Offset(100, 0),
);
expect(
updateDetails.fold(0.0, (double offset, DragUpdateDetails details) => offset + (details.primaryDelta ?? 0)),
100.0,
);
});
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: Transform.scale(
scale: 2.0,
child: GestureDetector(
onHorizontalDragCancel: () {
dragCancelCount++;
},
onHorizontalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onHorizontalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onHorizontalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
onTap: () {
// Competing gesture detector.
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(kTouchSlop + 1, 0));
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50 + (kTouchSlop + 1) / 2, 75));
expect(startDetails.single.globalPosition, const Offset(400 + (kTouchSlop + 1), 300));
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move just below kTouchSlop does not recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(kTouchSlop - 1, 0));
expect(dragCancelCount, 1);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, isEmpty);
expect(startDetails, isEmpty);
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move in two separate movements
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
await gesture.moveBy(const Offset(kTouchSlop + 1, 30));
await gesture.moveBy(const Offset(100, 10));
await gesture.up();
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50 + (kTouchSlop + 1) / 2, 75.0 + 30.0 / 2));
expect(startDetails.single.globalPosition, const Offset(400 + (kTouchSlop + 1), 300 + 30.0));
expect(updateDetails.single.localPosition, startDetails.single.localPosition + const Offset(100.0 / 2, 10 / 2));
expect(updateDetails.single.globalPosition, startDetails.single.globalPosition + const Offset(100.0, 10.0));
expect(updateDetails.single.delta, const Offset(100.0 / 2, 0.0));
expect(updateDetails.single.primaryDelta, 100.0 / 2);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
});
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: Transform.scale(
scale: 0.5,
child: GestureDetector(
onHorizontalDragCancel: () {
dragCancelCount++;
},
onHorizontalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onHorizontalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onHorizontalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
onTap: () {
// Competing gesture detector.
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(kTouchSlop + 1, 0));
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50 + (kTouchSlop + 1) * 2, 75));
expect(startDetails.single.globalPosition, const Offset(400 + (kTouchSlop + 1), 300));
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move just below kTouchSlop does not recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(kTouchSlop - 1, 0));
expect(dragCancelCount, 1);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, isEmpty);
expect(startDetails, isEmpty);
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move in two separate movements
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
await gesture.moveBy(const Offset(kTouchSlop + 1, 30));
await gesture.moveBy(const Offset(100, 10));
await gesture.up();
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50 + (kTouchSlop + 1) * 2, 75.0 + 30.0 * 2));
expect(startDetails.single.globalPosition, const Offset(400 + (kTouchSlop + 1), 300 + 30.0));
expect(updateDetails.single.localPosition, startDetails.single.localPosition + const Offset(100.0 * 2, 10.0 * 2.0));
expect(updateDetails.single.globalPosition, startDetails.single.globalPosition + const Offset(100.0, 10.0));
expect(updateDetails.single.delta, const Offset(100.0 * 2.0, 0.0));
expect(updateDetails.single.primaryDelta, 100.0 * 2);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
});
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when rotated 45 degrees', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: Transform.rotate(
angle: math.pi / 4,
child: GestureDetector(
onHorizontalDragCancel: () {
dragCancelCount++;
},
onHorizontalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onHorizontalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onHorizontalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
onTap: () {
// Competing gesture detector.
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
),
);
// Move just below kTouchSlop should not recognize drag.
const Offset moveBy1 = Offset(kTouchSlop/ 2, kTouchSlop / 2);
expect(moveBy1.distance, lessThan(kTouchSlop));
await tester.drag(find.byKey(redContainer), moveBy1);
expect(dragCancelCount, 1);
expect(downDetails.single.localPosition, within(distance: 0.0001, from: const Offset(50, 75)));
expect(downDetails.single.globalPosition, within(distance: 0.0001, from: const Offset(400, 300)));
expect(endDetails, isEmpty);
expect(startDetails, isEmpty);
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move above kTouchSlop recognizes drag.
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
await gesture.moveBy(const Offset(kTouchSlop, kTouchSlop));
await gesture.moveBy(const Offset(3, 4));
await gesture.up();
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, within(distance: 0.0001, from: const Offset(50, 75)));
expect(downDetails.single.globalPosition, within(distance: 0.0001, from: const Offset(400, 300)));
expect(endDetails, hasLength(1));
expect(startDetails, hasLength(1));
expect(updateDetails.single.globalPosition, within(distance: 0.0001, from: const Offset(400 + kTouchSlop + 3, 300 + kTouchSlop + 4)));
expect(updateDetails.single.delta, within(distance: 0.1, from: const Offset(5, 0.0))); // sqrt(3^2 + 4^2)
expect(updateDetails.single.primaryDelta, within<double>(distance: 0.1, from: 5.0)); // sqrt(3^2 + 4^2)
});
});
group('Vertical', () {
testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: GestureDetector(
onVerticalDragCancel: () {
dragCancelCount++;
},
onVerticalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onVerticalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onVerticalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
);
await tester.drag(find.byKey(redContainer), const Offset(0, 100));
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50, 75));
expect(startDetails.single.globalPosition, const Offset(400, 300));
expect(updateDetails.last.localPosition, const Offset(50, 75 + 100.0));
expect(updateDetails.last.globalPosition, const Offset(400, 300 + 100.0));
expect(
updateDetails.fold(Offset.zero, (Offset offset, DragUpdateDetails details) => offset + details.delta),
const Offset(0, 100),
);
expect(
updateDetails.fold(0.0, (double offset, DragUpdateDetails details) => offset + (details.primaryDelta ?? 0)),
100.0,
);
});
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: Transform.scale(
scale: 2.0,
child: GestureDetector(
onVerticalDragCancel: () {
dragCancelCount++;
},
onVerticalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onVerticalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onVerticalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
onTap: () {
// Competing gesture detector.
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(0, kTouchSlop + 1));
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50, 75 + (kTouchSlop + 1) / 2));
expect(startDetails.single.globalPosition, const Offset(400, 300 + (kTouchSlop + 1)));
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move just below kTouchSlop does not recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(0, kTouchSlop - 1));
expect(dragCancelCount, 1);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, isEmpty);
expect(startDetails, isEmpty);
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move in two separate movements
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
await gesture.moveBy(const Offset(30, kTouchSlop + 1));
await gesture.moveBy(const Offset(10, 100));
await gesture.up();
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50 + 30.0 / 2, 75.0 + (kTouchSlop + 1) / 2));
expect(startDetails.single.globalPosition, const Offset(400 + 30.0, 300 + (kTouchSlop + 1)));
expect(updateDetails.single.localPosition, startDetails.single.localPosition + const Offset(10.0 / 2, 100.0 / 2));
expect(updateDetails.single.globalPosition, startDetails.single.globalPosition + const Offset(10.0, 100.0));
expect(updateDetails.single.delta, const Offset(0.0, 100.0 / 2));
expect(updateDetails.single.primaryDelta, 100.0 / 2);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
});
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: Transform.scale(
scale: 0.5,
child: GestureDetector(
onVerticalDragCancel: () {
dragCancelCount++;
},
onVerticalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onVerticalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onVerticalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
onTap: () {
// Competing gesture detector.
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(0, kTouchSlop + 1));
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50, 75 + (kTouchSlop + 1) * 2));
expect(startDetails.single.globalPosition, const Offset(400, 300 + (kTouchSlop + 1)));
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move just below kTouchSlop does not recognize drag.
await tester.drag(find.byKey(redContainer), const Offset(0, kTouchSlop - 1));
expect(dragCancelCount, 1);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, isEmpty);
expect(startDetails, isEmpty);
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move in two separate movements
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
await gesture.moveBy(const Offset(30, kTouchSlop + 1));
await gesture.moveBy(const Offset(10, 100));
await gesture.up();
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, const Offset(50, 75));
expect(downDetails.single.globalPosition, const Offset(400, 300));
expect(endDetails, hasLength(1));
expect(startDetails.single.localPosition, const Offset(50 + 30.0 * 2, 75.0 + (kTouchSlop + 1) * 2));
expect(startDetails.single.globalPosition, const Offset(400 + 30.0, 300 + (kTouchSlop + 1)));
expect(updateDetails.single.localPosition, startDetails.single.localPosition + const Offset(10.0 * 2, 100.0 * 2.0));
expect(updateDetails.single.globalPosition, startDetails.single.globalPosition + const Offset(10.0, 100.0));
expect(updateDetails.single.delta, const Offset(0.0, 100.0 * 2.0));
expect(updateDetails.single.primaryDelta, 100.0 * 2);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
});
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when rotated 45 degrees', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
final List<DragStartDetails> startDetails = <DragStartDetails>[];
final List<DragUpdateDetails> updateDetails = <DragUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: Transform.rotate(
angle: math.pi / 4,
child: GestureDetector(
onVerticalDragCancel: () {
dragCancelCount++;
},
onVerticalDragDown: (DragDownDetails details) {
downDetails.add(details);
},
onVerticalDragEnd: (DragEndDetails details) {
endDetails.add(details);
},
onVerticalDragStart: (DragStartDetails details) {
startDetails.add(details);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
updateDetails.add(details);
},
onTap: () {
// Competing gesture detector.
},
child: Container(
key: redContainer,
width: 100,
height: 150,
color: Colors.red,
),
),
),
),
);
// Move just below kTouchSlop should not recognize drag.
const Offset moveBy1 = Offset(kTouchSlop/ 2, kTouchSlop / 2);
expect(moveBy1.distance, lessThan(kTouchSlop));
await tester.drag(find.byKey(redContainer), moveBy1);
expect(dragCancelCount, 1);
expect(downDetails.single.localPosition, within(distance: 0.0001, from: const Offset(50, 75)));
expect(downDetails.single.globalPosition, within(distance: 0.0001, from: const Offset(400, 300)));
expect(endDetails, isEmpty);
expect(startDetails, isEmpty);
expect(updateDetails, isEmpty);
dragCancelCount = 0;
downDetails.clear();
endDetails.clear();
startDetails.clear();
updateDetails.clear();
// Move above kTouchSlop recognizes drag.
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
await gesture.moveBy(const Offset(kTouchSlop, kTouchSlop));
await gesture.moveBy(const Offset(-4, 3));
await gesture.up();
expect(dragCancelCount, 0);
expect(downDetails.single.localPosition, within(distance: 0.0001, from: const Offset(50, 75)));
expect(downDetails.single.globalPosition, within(distance: 0.0001, from: const Offset(400, 300)));
expect(endDetails, hasLength(1));
expect(startDetails, hasLength(1));
expect(updateDetails.single.globalPosition, within(distance: 0.0001, from: const Offset(400 + kTouchSlop - 4, 300 + kTouchSlop + 3)));
expect(updateDetails.single.delta, within(distance: 0.1, from: const Offset(0.0, 5.0))); // sqrt(3^2 + 4^2)
expect(updateDetails.single.primaryDelta, within<double>(distance: 0.1, from: 5.0)); // sqrt(3^2 + 4^2)
});
});
}