Commit 9c15407b authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

When a list is scrolling, children can't be tapped (#5222) (#5348)

parent cab7c8d7
...@@ -9,6 +9,8 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -9,6 +9,8 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gallery/gallery/item.dart' show GalleryItem, kAllGalleryItems; import 'package:flutter_gallery/gallery/item.dart' show GalleryItem, kAllGalleryItems;
import 'package:flutter_gallery/main.dart' as flutter_gallery_main; import 'package:flutter_gallery/main.dart' as flutter_gallery_main;
const String kCaption = 'Flutter Gallery';
final List<String> demoCategories = new LinkedHashSet<String>.from( final List<String> demoCategories = new LinkedHashSet<String>.from(
kAllGalleryItems.map((GalleryItem item) => item.category) kAllGalleryItems.map((GalleryItem item) => item.category)
).toList(); ).toList();
...@@ -43,8 +45,9 @@ Future<Null> smokeDemo(WidgetTester tester, String routeName) async { ...@@ -43,8 +45,9 @@ Future<Null> smokeDemo(WidgetTester tester, String routeName) async {
await tester.tap(menuItem); await tester.tap(menuItem);
await tester.pump(); // Launch the demo. await tester.pump(); // Launch the demo.
await tester await tester.pump(const Duration(seconds: 1)); // Wait until the demo has opened.
.pump(const Duration(seconds: 1)); // Wait until the demo has opened.
expect(find.text(kCaption), findsNothing);
// Go back // Go back
if (routeName == '/pesto') { if (routeName == '/pesto') {
...@@ -64,16 +67,14 @@ Future<Null> smokeDemo(WidgetTester tester, String routeName) async { ...@@ -64,16 +67,14 @@ Future<Null> smokeDemo(WidgetTester tester, String routeName) async {
} }
void main() { void main() {
TestWidgetsFlutterBinding binding =
TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) binding.allowAllFrames = true;
testWidgets('Flutter Gallery app smoke test', (WidgetTester tester) async { testWidgets('Flutter Gallery app smoke test', (WidgetTester tester) async {
flutter_gallery_main flutter_gallery_main
.main(); // builds the app and schedules a frame but doesn't trigger one .main(); // builds the app and schedules a frame but doesn't trigger one
await tester.pump(); // see https://github.com/flutter/flutter/issues/1865 await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
await tester.pump(); // triggers a frame await tester.pump(); // triggers a frame
expect(find.text(kCaption), findsOneWidget);
final List<double> scrollDeltas = new List<double>(); final List<double> scrollDeltas = new List<double>();
double previousY = tester.getTopRight(find.text(demoCategories[0])).y; double previousY = tester.getTopRight(find.text(demoCategories[0])).y;
for (String routeName in routeNames) { for (String routeName in routeNames) {
...@@ -87,15 +88,17 @@ void main() { ...@@ -87,15 +88,17 @@ void main() {
final String routeName = routeNames[i]; final String routeName = routeNames[i];
await smokeDemo(tester, routeName); await smokeDemo(tester, routeName);
await tester.scroll(findGalleryItemByRouteName(tester, routeName), new Offset(0.0, scrollDeltas[i])); await tester.scroll(findGalleryItemByRouteName(tester, routeName), new Offset(0.0, scrollDeltas[i]));
await tester.pump(); await tester.pump(); // start the scroll
await tester.pump(const Duration(milliseconds: 500)); // wait for overscroll to timeout, if necessary
await tester.pump(const Duration(milliseconds: 2000)); // wait for overscroll to fade away, if necessary
tester.binding.debugAssertNoTransientCallbacks('A transient callback was still active after leaving route $routeName');
} }
Finder navigationMenuButton = findNavigationMenuButton(tester); Finder navigationMenuButton = findNavigationMenuButton(tester);
expect(navigationMenuButton, findsOneWidget); expect(navigationMenuButton, findsOneWidget);
await tester.tap(navigationMenuButton); await tester.tap(navigationMenuButton);
await tester.pump(); // Start opening drawer. await tester.pump(); // Start opening drawer.
await tester await tester.pump(const Duration(seconds: 1)); // Wait until it's really opened.
.pump(const Duration(seconds: 1)); // Wait until it's really opened.
// switch theme // switch theme
await tester.tap(find.text('Dark')); await tester.tap(find.text('Dark'));
......
...@@ -259,7 +259,8 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -259,7 +259,8 @@ class ScrollableState<T extends Scrollable> extends State<T> {
@override @override
void dispose() { void dispose() {
_stop(); _controller.dispose();
_simulation = null;
super.dispose(); super.dispose();
} }
...@@ -367,8 +368,10 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -367,8 +368,10 @@ class ScrollableState<T extends Scrollable> extends State<T> {
} }
void _handleAnimationStatusChanged(AnimationStatus status) { void _handleAnimationStatusChanged(AnimationStatus status) {
if (!_controller.isAnimating) setState(() {
_simulation = null; if (!_controller.isAnimating)
_simulation = null;
});
} }
void _setScrollOffset(double newScrollOffset, { DragUpdateDetails details }) { void _setScrollOffset(double newScrollOffset, { DragUpdateDetails details }) {
...@@ -579,9 +582,12 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -579,9 +582,12 @@ class ScrollableState<T extends Scrollable> extends State<T> {
} }
void _stop() { void _stop() {
assert(mounted);
assert(_controller.isAnimating || _simulation == null); assert(_controller.isAnimating || _simulation == null);
_controller.stop(); setState(() {
_simulation = null; _controller.stop();
_simulation = null;
});
} }
void _handleDragStart(DragStartDetails details) { void _handleDragStart(DragStartDetails details) {
...@@ -652,7 +658,10 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -652,7 +658,10 @@ class ScrollableState<T extends Scrollable> extends State<T> {
key: _gestureDetectorKey, key: _gestureDetectorKey,
gestures: buildGestureDetectors(), gestures: buildGestureDetectors(),
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: buildContent(context) child: new IgnorePointer(
ignoring: _controller.isAnimating,
child: buildContent(context)
)
); );
} }
......
...@@ -58,4 +58,49 @@ void main() { ...@@ -58,4 +58,49 @@ void main() {
expect(result1, lessThan(result2)); // iOS (result2) is slipperier than Android (result1) expect(result1, lessThan(result2)); // iOS (result2) is slipperier than Android (result1)
}); });
testWidgets('fling and tap to stop', (WidgetTester tester) async {
List<String> log = <String>[];
List<Widget> textWidgets = <Widget>[];
for (int i = 0; i < 250; i++)
textWidgets.add(new GestureDetector(onTap: () { log.add('tap $i'); }, child: new Text('$i')));
await tester.pumpWidget(new Block(children: textWidgets));
expect(log, equals(<String>[]));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.fling(find.byType(Scrollable), new Offset(0.0, -200.0), 1000.0);
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18', 'tap 31']));
});
testWidgets('fling and wait and tap', (WidgetTester tester) async {
List<String> log = <String>[];
List<Widget> textWidgets = <Widget>[];
for (int i = 0; i < 250; i++)
textWidgets.add(new GestureDetector(onTap: () { log.add('tap $i'); }, child: new Text('$i')));
await tester.pumpWidget(new Block(children: textWidgets));
expect(log, equals(<String>[]));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.fling(find.byType(Scrollable), new Offset(0.0, -200.0), 1000.0);
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.pump(const Duration(seconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18', 'tap 48']));
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment