blob: a910dc31c2f695fa5185cf81b2b8005a7d83c3cc [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:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('TapRegionSurface detects outside tap down events', (WidgetTester tester) async {
final Set<String> tappedOutside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
// We intentionally don't call up() here because we're testing the down event.
await gesture.cancel();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Outside Surface'));
expect(tappedOutside, isEmpty);
});
testWidgets('TapRegionSurface detects outside tap up events', (WidgetTester tester) async {
final Set<String> tappedOutside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapUpOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapUpOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapUpOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
expect(tappedOutside, isEmpty); // No callbacks should been called before up event.
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
});
testWidgets('TapRegionSurface consumes outside taps when asked', (WidgetTester tester) async {
final Set<String> tappedOutside = <String>{};
int propagatedTaps = 0;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
GestureDetector(
onTap: () {
propagatedTaps += 1;
},
child: const Text('Outside'),
),
TapRegion(
consumeOutsideTaps: true,
onTapOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
consumeOutsideTaps: true,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
expect(propagatedTaps, equals(0));
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Outside Surface'));
expect(tappedOutside, isEmpty);
});
testWidgets('TapRegionSurface detects inside tap down events', (WidgetTester tester) async {
final Set<String> tappedInside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapInside: (PointerEvent event) {
tappedInside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapInside: (PointerEvent event) {
tappedInside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapInside: (PointerEvent event) {
tappedInside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
// We intentionally don't call up() here because we're testing the down event.
await gesture.cancel();
await gesture.removePointer();
}
expect(tappedInside, isEmpty);
await click(find.text('No Group'));
expect(tappedInside, unorderedEquals(<String>{'No Group'}));
tappedInside.clear();
await click(find.text('Group 1 A'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Group 1 B'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Outside'));
expect(tappedInside, isEmpty);
tappedInside.clear();
await click(find.text('Outside Surface'));
expect(tappedInside, isEmpty);
});
testWidgets('TapRegionSurface detects inside tap up events', (WidgetTester tester) async {
final Set<String> tappedInside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapUpInside: (PointerEvent event) {
tappedInside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapUpInside: (PointerEvent event) {
tappedInside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapUpInside: (PointerEvent event) {
tappedInside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
expect(tappedInside, isEmpty); // No callbacks should been called before up event.
await gesture.up();
await gesture.removePointer();
}
expect(tappedInside, isEmpty);
await click(find.text('No Group'));
expect(tappedInside, unorderedEquals(<String>{'No Group'}));
tappedInside.clear();
await click(find.text('Group 1 A'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Group 1 B'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Outside'));
expect(tappedInside, isEmpty);
tappedInside.clear();
});
testWidgets('TapRegionSurface detects inside taps correctly with behavior', (
WidgetTester tester,
) async {
final Set<String> tappedInside = <String>{};
const ValueKey<String> noGroupKey = ValueKey<String>('No Group');
const ValueKey<String> group1AKey = ValueKey<String>('Group 1 A');
const ValueKey<String> group1BKey = ValueKey<String>('Group 1 B');
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: 100, height: 100),
child: TapRegion(
onTapInside: (PointerEvent event) {
tappedInside.add(noGroupKey.value);
},
child: const Stack(key: noGroupKey),
),
),
ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: 100, height: 100),
child: TapRegion(
groupId: 1,
behavior: HitTestBehavior.opaque,
onTapInside: (PointerEvent event) {
tappedInside.add(group1AKey.value);
},
child: const Stack(key: group1AKey),
),
),
ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: 100, height: 100),
child: TapRegion(
groupId: 1,
behavior: HitTestBehavior.translucent,
onTapInside: (PointerEvent event) {
tappedInside.add(group1BKey.value);
},
child: const Stack(key: group1BKey),
),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedInside, isEmpty);
await click(find.byKey(noGroupKey));
expect(tappedInside, isEmpty); // No hittable children, so no hit.
await click(find.byKey(group1AKey));
// No hittable children, but set to opaque, so it hits, triggering the
// group.
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.byKey(group1BKey));
expect(tappedInside, isEmpty); // No hittable children while translucent, so no hit.
tappedInside.clear();
});
testWidgets('Setting the group updates the registration', (WidgetTester tester) async {
final Set<String> tappedOutside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
await click(find.text('Group 1 A'));
expect(tappedOutside, isEmpty);
await click(find.text('Group 1 B'));
expect(tappedOutside, isEmpty);
await click(find.text('Outside'));
expect(tappedOutside, equals(<String>['Group 1 A', 'Group 1 B']));
tappedOutside.clear();
// Now change out the groups.
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 2,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 2 A');
},
child: const Text('Group 2 A'),
),
],
),
),
),
);
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>['Group 2 A']));
tappedOutside.clear();
await click(find.text('Group 2 A'));
expect(tappedOutside, equals(<String>['Group 1 A']));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, equals(<String>['Group 1 A', 'Group 2 A']));
tappedOutside.clear();
});
testWidgets('TapRegionSurface detects outside right click', (WidgetTester tester) async {
final Set<String> tappedOutside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryButton,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Outside Surface'));
expect(tappedOutside, isEmpty);
});
testWidgets('TapRegionSurface detects outside middle click', (WidgetTester tester) async {
final Set<String> tappedOutside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
buttons: kTertiaryButton,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
tappedOutside.clear();
await click(find.text('Outside Surface'));
expect(tappedOutside, isEmpty);
});
testWidgets('TapRegionSurface consumes outside right click when asked', (
WidgetTester tester,
) async {
final Set<String> tappedOutside = <String>{};
int propagatedTaps = 0;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
GestureDetector(
onTap: () {
propagatedTaps += 1;
},
child: const Text('Outside'),
),
TapRegion(
consumeOutsideTaps: true,
onTapOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
consumeOutsideTaps: true,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryButton,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
expect(propagatedTaps, equals(0));
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Outside Surface'));
expect(tappedOutside, isEmpty);
});
testWidgets('TapRegionSurface consumes outside middle click when asked', (
WidgetTester tester,
) async {
final Set<String> tappedOutside = <String>{};
int propagatedTaps = 0;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
GestureDetector(
onTap: () {
propagatedTaps += 1;
},
child: const Text('Outside'),
),
TapRegion(
consumeOutsideTaps: true,
onTapOutside: (PointerEvent event) {
tappedOutside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
consumeOutsideTaps: true,
onTapOutside: (PointerEvent event) {
tappedOutside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
buttons: kTertiaryButton,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedOutside, isEmpty);
expect(propagatedTaps, equals(0));
await click(find.text('No Group'));
expect(tappedOutside, unorderedEquals(<String>{'Group 1 A', 'Group 1 B'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Group 1 A'));
expect(tappedOutside, equals(<String>{'No Group'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Group 1 B'));
expect(tappedOutside, equals(<String>{'No Group'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Outside'));
expect(tappedOutside, unorderedEquals(<String>{'No Group', 'Group 1 A', 'Group 1 B'}));
expect(propagatedTaps, equals(0));
tappedOutside.clear();
await click(find.text('Outside Surface'));
expect(tappedOutside, isEmpty);
});
testWidgets('TapRegionSurface detects inside right click', (WidgetTester tester) async {
final Set<String> tappedInside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapInside: (PointerEvent event) {
tappedInside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapInside: (PointerEvent event) {
tappedInside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapInside: (PointerEvent event) {
tappedInside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryButton,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedInside, isEmpty);
await click(find.text('No Group'));
expect(tappedInside, unorderedEquals(<String>{'No Group'}));
tappedInside.clear();
await click(find.text('Group 1 A'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Group 1 B'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Outside'));
expect(tappedInside, isEmpty);
tappedInside.clear();
await click(find.text('Outside Surface'));
expect(tappedInside, isEmpty);
});
testWidgets('TapRegionSurface detects inside middle click', (WidgetTester tester) async {
final Set<String> tappedInside = <String>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
const Text('Outside Surface'),
TapRegionSurface(
child: Row(
children: <Widget>[
const Text('Outside'),
TapRegion(
onTapInside: (PointerEvent event) {
tappedInside.add('No Group');
},
child: const Text('No Group'),
),
TapRegion(
groupId: 1,
onTapInside: (PointerEvent event) {
tappedInside.add('Group 1 A');
},
child: const Text('Group 1 A'),
),
TapRegion(
groupId: 1,
onTapInside: (PointerEvent event) {
tappedInside.add('Group 1 B');
},
child: const Text('Group 1 B'),
),
],
),
),
],
),
),
);
await tester.pump();
Future<void> click(Finder finder) async {
final TestGesture gesture = await tester.startGesture(
tester.getCenter(finder),
kind: PointerDeviceKind.mouse,
buttons: kTertiaryButton,
);
await gesture.up();
await gesture.removePointer();
}
expect(tappedInside, isEmpty);
await click(find.text('No Group'));
expect(tappedInside, unorderedEquals(<String>{'No Group'}));
tappedInside.clear();
await click(find.text('Group 1 A'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Group 1 B'));
expect(tappedInside, equals(<String>{'Group 1 A', 'Group 1 B'}));
tappedInside.clear();
await click(find.text('Outside'));
expect(tappedInside, isEmpty);
tappedInside.clear();
await click(find.text('Outside Surface'));
expect(tappedInside, isEmpty);
});
// Regression test for https://github.com/flutter/flutter/issues/153093.
testWidgets('TapRegion onTapOutside should only trigger on the current route during navigation', (
WidgetTester tester,
) async {
const ValueKey<String> tapRegion1Key = ValueKey<String>('TapRegion');
const ValueKey<String> tapRegion2Key = ValueKey<String>('TapRegion2');
int count1 = 0;
int count2 = 0;
final TapRegion tapRegion1 = TapRegion(
key: tapRegion1Key,
onTapOutside: (PointerEvent event) {
count1 += 1;
},
behavior: HitTestBehavior.opaque,
child: const SizedBox.square(dimension: 100),
);
final TapRegion tapRegion2 = TapRegion(
key: tapRegion2Key,
onTapOutside: (PointerEvent event) {
count2 += 1;
},
behavior: HitTestBehavior.opaque,
child: const SizedBox.square(dimension: 100),
);
Future<void> tapOutside(WidgetTester tester, Finder regionFinder) async {
// Find the RenderBox of the region.
final RenderBox renderBox = tester.firstRenderObject(find.byType(Scaffold).last);
final Offset outsidePoint = renderBox.localToGlobal(Offset.zero) + const Offset(200, 200);
await tester.tapAt(outsidePoint);
await tester.pump();
}
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(child: tapRegion1),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
tester.element(find.byType(FloatingActionButton)),
MaterialPageRoute<void>(
builder: (BuildContext context) => Scaffold(body: Center(child: tapRegion2)),
),
);
},
child: const Icon(Icons.add),
),
),
),
);
await tester.pumpAndSettle();
// Tap outside the first TapRegion to trigger onTapOutside.
await tapOutside(tester, find.byKey(tapRegion1Key));
expect(count1, 1);
expect(count2, 0);
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
// Tap outside the second TapRegion to trigger onTapOutside
await tapOutside(tester, find.byKey(tapRegion2Key));
expect(count1, 2); // When the Fab is pressed, the first TapRegion is still active.
expect(count2, 1);
// Back to the first page.
Navigator.pop(tester.element(find.byType(Scaffold).last));
await tester.pumpAndSettle();
// Tap outside the first TapRegion to trigger onTapOutside
await tapOutside(tester, find.byKey(tapRegion1Key));
expect(count1, 3);
expect(count2, 1);
});
// Regression test for https://github.com/flutter/flutter/issues/153093.
testWidgets('TapRegion on non-current routes should not respond to onTapOutside events', (
WidgetTester tester,
) async {
const ValueKey<String> tapRegion1Key = ValueKey<String>('TapRegion1');
const ValueKey<String> tapRegion2Key = ValueKey<String>('TapRegion2');
int count1 = 0;
int count2 = 0;
final TapRegion tapRegion1 = TapRegion(
key: tapRegion1Key,
onTapOutside: (PointerEvent event) {
count1 += 1;
},
behavior: HitTestBehavior.opaque,
child: const SizedBox.square(dimension: 100),
);
final TapRegion tapRegion2 = TapRegion(
key: tapRegion2Key,
onTapOutside: (PointerEvent event) {
count2 += 1;
},
behavior: HitTestBehavior.opaque,
child: const SizedBox.square(dimension: 100),
);
Future<void> tapOutside(WidgetTester tester, Finder regionFinder) async {
// Find the RenderBox of the region.
final RenderBox renderBox = tester.firstRenderObject(find.byType(Scaffold).last);
final Offset outsidePoint = renderBox.localToGlobal(Offset.zero) + const Offset(200, 200);
await tester.tapAt(outsidePoint);
await tester.pump();
}
await tester.pumpWidget(
MaterialApp(
initialRoute: '/',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => Scaffold(body: Center(child: tapRegion1)),
'/second': (BuildContext context) => Scaffold(body: Center(child: tapRegion2)),
},
onGenerateInitialRoutes: (String initialRouteName) {
return <Route<void>>[
MaterialPageRoute<void>(
builder: (BuildContext context) => Scaffold(body: Center(child: tapRegion1)),
),
MaterialPageRoute<void>(
builder: (BuildContext context) => Scaffold(body: Center(child: tapRegion2)),
),
];
},
),
);
await tester.pumpAndSettle();
// At this point, tapRegion2 is on top of tapRegion1.
// Tap outside tapRegion2.
await tapOutside(tester, find.byKey(tapRegion2Key));
expect(count1, 0); // tapRegion1 should not respond.
expect(count2, 1); // tapRegion2 should respond.
// Now pop the top route to reveal tapRegion1.
Navigator.pop(tester.element(find.byType(Scaffold).last));
await tester.pumpAndSettle();
// Tap outside tapRegion1.
await tapOutside(tester, find.byKey(tapRegion1Key));
expect(count1, 1); // tapRegion1 should respond.
expect(count2, 1); // tapRegion2 should not respond anymore.
});
}