diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 67beb0ad38d..6964117577b 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart'; import 'basic.dart'; import 'framework.dart'; +import 'media_query.dart'; export 'package:flutter/gestures.dart' show DragDownDetails, @@ -955,6 +956,7 @@ class GestureDetector extends StatelessWidget { @override Widget build(BuildContext context) { final Map gestures = {}; + final DeviceGestureSettings? gestureSettings = MediaQuery.maybeOf(context)?.gestureSettings; if (onTapDown != null || onTapUp != null || @@ -982,7 +984,8 @@ class GestureDetector extends StatelessWidget { ..onSecondaryTapCancel = onSecondaryTapCancel ..onTertiaryTapDown = onTertiaryTapDown ..onTertiaryTapUp = onTertiaryTapUp - ..onTertiaryTapCancel = onTertiaryTapCancel; + ..onTertiaryTapCancel = onTertiaryTapCancel + ..gestureSettings = gestureSettings; }, ); } @@ -994,7 +997,8 @@ class GestureDetector extends StatelessWidget { instance ..onDoubleTapDown = onDoubleTapDown ..onDoubleTap = onDoubleTap - ..onDoubleTapCancel = onDoubleTapCancel; + ..onDoubleTapCancel = onDoubleTapCancel + ..gestureSettings = gestureSettings; }, ); } @@ -1044,7 +1048,8 @@ class GestureDetector extends StatelessWidget { ..onTertiaryLongPressStart = onTertiaryLongPressStart ..onTertiaryLongPressMoveUpdate = onTertiaryLongPressMoveUpdate ..onTertiaryLongPressUp = onTertiaryLongPressUp - ..onTertiaryLongPressEnd = onTertiaryLongPressEnd; + ..onTertiaryLongPressEnd = onTertiaryLongPressEnd + ..gestureSettings = gestureSettings; }, ); } @@ -1063,7 +1068,8 @@ class GestureDetector extends StatelessWidget { ..onUpdate = onVerticalDragUpdate ..onEnd = onVerticalDragEnd ..onCancel = onVerticalDragCancel - ..dragStartBehavior = dragStartBehavior; + ..dragStartBehavior = dragStartBehavior + ..gestureSettings = gestureSettings; }, ); } @@ -1082,7 +1088,8 @@ class GestureDetector extends StatelessWidget { ..onUpdate = onHorizontalDragUpdate ..onEnd = onHorizontalDragEnd ..onCancel = onHorizontalDragCancel - ..dragStartBehavior = dragStartBehavior; + ..dragStartBehavior = dragStartBehavior + ..gestureSettings = gestureSettings; }, ); } @@ -1101,7 +1108,8 @@ class GestureDetector extends StatelessWidget { ..onUpdate = onPanUpdate ..onEnd = onPanEnd ..onCancel = onPanCancel - ..dragStartBehavior = dragStartBehavior; + ..dragStartBehavior = dragStartBehavior + ..gestureSettings = gestureSettings; }, ); } @@ -1114,7 +1122,8 @@ class GestureDetector extends StatelessWidget { ..onStart = onScaleStart ..onUpdate = onScaleUpdate ..onEnd = onScaleEnd - ..dragStartBehavior = dragStartBehavior; + ..dragStartBehavior = dragStartBehavior + ..gestureSettings = gestureSettings; }, ); } @@ -1130,7 +1139,8 @@ class GestureDetector extends StatelessWidget { ..onStart = onForcePressStart ..onPeak = onForcePressPeak ..onUpdate = onForcePressUpdate - ..onEnd = onForcePressEnd; + ..onEnd = onForcePressEnd + ..gestureSettings = gestureSettings; }, ); } diff --git a/packages/flutter/test/gestures/gesture_config_regression_test.dart b/packages/flutter/test/gestures/gesture_config_regression_test.dart new file mode 100644 index 00000000000..c21ade7824e --- /dev/null +++ b/packages/flutter/test/gestures/gesture_config_regression_test.dart @@ -0,0 +1,83 @@ +// 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' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class TestResult { + bool dragStarted = false; + bool dragUpdate = false; +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.testResult}) : super(key: key); + + final TestResult testResult; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + SliverFixedExtentList( + itemExtent: 50.0, + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return Container( + alignment: Alignment.center, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onVerticalDragDown: (DragDownDetails details) { + widget.testResult.dragStarted = true; + }, + onVerticalDragUpdate: (DragUpdateDetails details){ + widget.testResult.dragUpdate = true; + }, + onVerticalDragEnd: (_) {}, + child: Text('List Item $index', key: ValueKey(index), + ), + ), + ); + }, + ), + ), + ], + ), + ); + } +} + +void main() { + testWidgets('Scroll Views get the same ScrollConfiguration as GestureDetectors', (WidgetTester tester) async { + tester.binding.window.viewConfigurationTestValue = const ui.ViewConfiguration( + gestureSettings: ui.GestureSettings(physicalTouchSlop: 4), + ); + final TestResult result = TestResult(); + + await tester.pumpWidget(MaterialApp( + title: 'Scroll Bug', + home: MyHomePage(testResult: result), + )); + + // By dragging the scroll view more than the configured touch slop above but less than + // the framework default value, we demonstrate that this causes gesture detectors + // that do not receive the same gesture settings to fire at different times than would + // be expected. + final Offset start = tester.getCenter(find.byKey(const ValueKey(1))); + await tester.timedDragFrom(start, const Offset(0, 5), const Duration(milliseconds: 50)); + await tester.pumpAndSettle(); + + expect(result.dragStarted, true); + expect(result.dragUpdate, true); + }); +}