| // 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 'package:flutter_test/flutter_test.dart'; |
| import 'package:flutter/widgets.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/gestures.dart'; |
| |
| void main() { |
| const Offset forcePressOffset = Offset(400.0, 50.0); |
| |
| testWidgets('Uncontested scrolls start immediately', (WidgetTester tester) async { |
| bool didStartDrag = false; |
| double updatedDragDelta; |
| bool didEndDrag = false; |
| |
| final Widget widget = GestureDetector( |
| onVerticalDragStart: (DragStartDetails details) { |
| didStartDrag = true; |
| }, |
| onVerticalDragUpdate: (DragUpdateDetails details) { |
| updatedDragDelta = details.primaryDelta; |
| }, |
| onVerticalDragEnd: (DragEndDetails details) { |
| didEndDrag = true; |
| }, |
| child: Container( |
| color: const Color(0xFF00FF00), |
| ), |
| ); |
| |
| await tester.pumpWidget(widget); |
| expect(didStartDrag, isFalse); |
| expect(updatedDragDelta, isNull); |
| expect(didEndDrag, isFalse); |
| |
| const Offset firstLocation = Offset(10.0, 10.0); |
| final TestGesture gesture = await tester.startGesture(firstLocation, pointer: 7); |
| expect(didStartDrag, isTrue); |
| didStartDrag = false; |
| expect(updatedDragDelta, isNull); |
| expect(didEndDrag, isFalse); |
| |
| const Offset secondLocation = Offset(10.0, 9.0); |
| await gesture.moveTo(secondLocation); |
| expect(didStartDrag, isFalse); |
| expect(updatedDragDelta, -1.0); |
| updatedDragDelta = null; |
| expect(didEndDrag, isFalse); |
| |
| await gesture.up(); |
| expect(didStartDrag, isFalse); |
| expect(updatedDragDelta, isNull); |
| expect(didEndDrag, isTrue); |
| didEndDrag = false; |
| |
| await tester.pumpWidget(Container()); |
| }); |
| |
| testWidgets('Match two scroll gestures in succession', (WidgetTester tester) async { |
| int gestureCount = 0; |
| double dragDistance = 0.0; |
| |
| const Offset downLocation = Offset(10.0, 10.0); |
| const Offset upLocation = Offset(10.0, 50.0); // must be far enough to be more than kTouchSlop |
| |
| final Widget widget = GestureDetector( |
| dragStartBehavior: DragStartBehavior.down, |
| onVerticalDragUpdate: (DragUpdateDetails details) { dragDistance += details.primaryDelta; }, |
| onVerticalDragEnd: (DragEndDetails details) { gestureCount += 1; }, |
| onHorizontalDragUpdate: (DragUpdateDetails details) { fail('gesture should not match'); }, |
| onHorizontalDragEnd: (DragEndDetails details) { fail('gesture should not match'); }, |
| child: Container( |
| color: const Color(0xFF00FF00), |
| ), |
| ); |
| await tester.pumpWidget(widget); |
| |
| TestGesture gesture = await tester.startGesture(downLocation, pointer: 7); |
| await gesture.moveTo(upLocation); |
| await gesture.up(); |
| |
| gesture = await tester.startGesture(downLocation, pointer: 7); |
| await gesture.moveTo(upLocation); |
| await gesture.up(); |
| |
| expect(gestureCount, 2); |
| expect(dragDistance, 40.0 * 2.0); // delta between down and up, twice |
| |
| await tester.pumpWidget(Container()); |
| }); |
| |
| testWidgets("Pan doesn't crash", (WidgetTester tester) async { |
| bool didStartPan = false; |
| Offset panDelta; |
| bool didEndPan = false; |
| |
| await tester.pumpWidget( |
| GestureDetector( |
| onPanStart: (DragStartDetails details) { |
| didStartPan = true; |
| }, |
| onPanUpdate: (DragUpdateDetails details) { |
| panDelta = panDelta == null ? details.delta : panDelta + details.delta; |
| }, |
| onPanEnd: (DragEndDetails details) { |
| didEndPan = true; |
| }, |
| child: Container( |
| color: const Color(0xFF00FF00), |
| ), |
| ), |
| ); |
| |
| expect(didStartPan, isFalse); |
| expect(panDelta, isNull); |
| expect(didEndPan, isFalse); |
| |
| await tester.dragFrom(const Offset(10.0, 10.0), const Offset(20.0, 30.0)); |
| |
| expect(didStartPan, isTrue); |
| expect(panDelta.dx, 20.0); |
| expect(panDelta.dy, 30.0); |
| expect(didEndPan, isTrue); |
| }); |
| |
| testWidgets('Translucent', (WidgetTester tester) async { |
| bool didReceivePointerDown; |
| bool didTap; |
| |
| Future<void> pumpWidgetTree(HitTestBehavior behavior) { |
| return tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: Stack( |
| children: <Widget>[ |
| Listener( |
| onPointerDown: (_) { |
| didReceivePointerDown = true; |
| }, |
| child: Container( |
| width: 100.0, |
| height: 100.0, |
| color: const Color(0xFF00FF00), |
| ), |
| ), |
| Container( |
| width: 100.0, |
| height: 100.0, |
| child: GestureDetector( |
| onTap: () { |
| didTap = true; |
| }, |
| behavior: behavior, |
| ), |
| ), |
| ], |
| ), |
| ), |
| ); |
| } |
| |
| didReceivePointerDown = false; |
| didTap = false; |
| await pumpWidgetTree(null); |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| expect(didReceivePointerDown, isTrue); |
| expect(didTap, isTrue); |
| |
| didReceivePointerDown = false; |
| didTap = false; |
| await pumpWidgetTree(HitTestBehavior.deferToChild); |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| expect(didReceivePointerDown, isTrue); |
| expect(didTap, isFalse); |
| |
| didReceivePointerDown = false; |
| didTap = false; |
| await pumpWidgetTree(HitTestBehavior.opaque); |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| expect(didReceivePointerDown, isFalse); |
| expect(didTap, isTrue); |
| |
| didReceivePointerDown = false; |
| didTap = false; |
| await pumpWidgetTree(HitTestBehavior.translucent); |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| expect(didReceivePointerDown, isTrue); |
| expect(didTap, isTrue); |
| |
| }); |
| |
| testWidgets('Empty', (WidgetTester tester) async { |
| bool didTap = false; |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| onTap: () { |
| didTap = true; |
| }, |
| ), |
| ), |
| ); |
| expect(didTap, isFalse); |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| expect(didTap, isTrue); |
| }); |
| |
| testWidgets('Only container', (WidgetTester tester) async { |
| bool didTap = false; |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| onTap: () { |
| didTap = true; |
| }, |
| child: Container(), |
| ), |
| ), |
| ); |
| expect(didTap, isFalse); |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| expect(didTap, isFalse); |
| }); |
| |
| testWidgets('cache render object', (WidgetTester tester) async { |
| final GestureTapCallback inputCallback = () { }; |
| |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| onTap: inputCallback, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final RenderSemanticsGestureHandler renderObj1 = tester.renderObject(find.byType(GestureDetector)); |
| |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| onTap: inputCallback, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final RenderSemanticsGestureHandler renderObj2 = tester.renderObject(find.byType(GestureDetector)); |
| |
| expect(renderObj1, same(renderObj2)); |
| }); |
| |
| testWidgets('Tap down occurs after kPressTimeout', (WidgetTester tester) async { |
| int tapDown = 0; |
| int tap = 0; |
| int tapCancel = 0; |
| int longPress = 0; |
| |
| await tester.pumpWidget( |
| Container( |
| alignment: Alignment.topLeft, |
| child: Container( |
| alignment: Alignment.center, |
| height: 100.0, |
| color: const Color(0xFF00FF00), |
| child: GestureDetector( |
| onTapDown: (TapDownDetails details) { |
| tapDown += 1; |
| }, |
| onTap: () { |
| tap += 1; |
| }, |
| onTapCancel: () { |
| tapCancel += 1; |
| }, |
| onLongPress: () { |
| longPress += 1; |
| }, |
| ), |
| ), |
| ), |
| ); |
| |
| // Pointer is dragged from the center of the 800x100 gesture detector |
| // to a point (400,300) below it. This should never call onTap. |
| Future<void> dragOut(Duration timeout) async { |
| final TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0)); |
| // If the timeout is less than kPressTimeout the recognizer will not |
| // trigger any callbacks. If the timeout is greater than kLongPressTimeout |
| // then onTapDown, onLongPress, and onCancel will be called. |
| await tester.pump(timeout); |
| await gesture.moveTo(const Offset(400.0, 300.0)); |
| await gesture.up(); |
| } |
| |
| await dragOut(kPressTimeout * 0.5); // generates nothing |
| expect(tapDown, 0); |
| expect(tapCancel, 0); |
| expect(tap, 0); |
| expect(longPress, 0); |
| |
| await dragOut(kPressTimeout); // generates tapDown, tapCancel |
| expect(tapDown, 1); |
| expect(tapCancel, 1); |
| expect(tap, 0); |
| expect(longPress, 0); |
| |
| await dragOut(kLongPressTimeout); // generates tapDown, longPress, tapCancel |
| expect(tapDown, 2); |
| expect(tapCancel, 2); |
| expect(tap, 0); |
| expect(longPress, 1); |
| }); |
| |
| testWidgets('Long Press Up Callback called after long press', (WidgetTester tester) async { |
| int longPressUp = 0; |
| |
| await tester.pumpWidget( |
| Container( |
| alignment: Alignment.topLeft, |
| child: Container( |
| alignment: Alignment.center, |
| height: 100.0, |
| color: const Color(0xFF00FF00), |
| child: GestureDetector( |
| onLongPressUp: () { |
| longPressUp += 1; |
| }, |
| ), |
| ), |
| ), |
| ); |
| |
| Future<void> longPress(Duration timeout) async { |
| final TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0)); |
| await tester.pump(timeout); |
| await gesture.up(); |
| } |
| |
| await longPress(kLongPressTimeout + const Duration(seconds: 1)); // To make sure the time for long press has occurred |
| expect(longPressUp, 1); |
| }); |
| |
| testWidgets('Force Press Callback called after force press', (WidgetTester tester) async { |
| int forcePressStart = 0; |
| int forcePressPeaked = 0; |
| int forcePressUpdate = 0; |
| int forcePressEnded = 0; |
| |
| await tester.pumpWidget( |
| Container( |
| alignment: Alignment.topLeft, |
| child: Container( |
| alignment: Alignment.center, |
| height: 100.0, |
| color: const Color(0xFF00FF00), |
| child: GestureDetector( |
| onForcePressStart: (_) => forcePressStart += 1, |
| onForcePressEnd: (_) => forcePressEnded += 1, |
| onForcePressPeak: (_) => forcePressPeaked += 1, |
| onForcePressUpdate: (_) => forcePressUpdate += 1, |
| ), |
| ), |
| ), |
| ); |
| const int pointerValue = 1; |
| |
| final TestGesture gesture = await tester.createGesture(); |
| await gesture.downWithCustomEvent( |
| forcePressOffset, |
| const PointerDownEvent( |
| pointer: pointerValue, |
| position: forcePressOffset, |
| pressure: 0.0, |
| pressureMax: 6.0, |
| pressureMin: 0.0, |
| ), |
| ); |
| |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.3, pressureMin: 0, pressureMax: 1)); |
| |
| expect(forcePressStart, 0); |
| expect(forcePressPeaked, 0); |
| expect(forcePressUpdate, 0); |
| expect(forcePressEnded, 0); |
| |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); |
| |
| expect(forcePressStart, 1); |
| expect(forcePressPeaked, 0); |
| expect(forcePressUpdate, 1); |
| expect(forcePressEnded, 0); |
| |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.6, pressureMin: 0, pressureMax: 1)); |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.7, pressureMin: 0, pressureMax: 1)); |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.2, pressureMin: 0, pressureMax: 1)); |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.3, pressureMin: 0, pressureMax: 1)); |
| |
| expect(forcePressStart, 1); |
| expect(forcePressPeaked, 0); |
| expect(forcePressUpdate, 5); |
| expect(forcePressEnded, 0); |
| |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.9, pressureMin: 0, pressureMax: 1)); |
| |
| expect(forcePressStart, 1); |
| expect(forcePressPeaked, 1); |
| expect(forcePressUpdate, 6); |
| expect(forcePressEnded, 0); |
| |
| await gesture.up(); |
| |
| expect(forcePressStart, 1); |
| expect(forcePressPeaked, 1); |
| expect(forcePressUpdate, 6); |
| expect(forcePressEnded, 1); |
| }); |
| |
| testWidgets('Force Press Callback not called if long press triggered before force press', (WidgetTester tester) async { |
| int forcePressStart = 0; |
| int longPressTimes = 0; |
| |
| await tester.pumpWidget( |
| Container( |
| alignment: Alignment.topLeft, |
| child: Container( |
| alignment: Alignment.center, |
| height: 100.0, |
| color: const Color(0xFF00FF00), |
| child: GestureDetector( |
| onForcePressStart: (_) => forcePressStart += 1, |
| onLongPress: () => longPressTimes += 1, |
| ), |
| ), |
| ), |
| ); |
| |
| const int pointerValue = 1; |
| const double maxPressure = 6.0; |
| |
| final TestGesture gesture = await tester.createGesture(); |
| |
| await gesture.downWithCustomEvent( |
| forcePressOffset, |
| const PointerDownEvent( |
| pointer: pointerValue, |
| position: forcePressOffset, |
| pressure: 0.0, |
| pressureMax: maxPressure, |
| pressureMin: 0.0, |
| ), |
| ); |
| |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(400.0, 50.0), pressure: 0.3, pressureMin: 0, pressureMax: maxPressure)); |
| |
| expect(forcePressStart, 0); |
| expect(longPressTimes, 0); |
| |
| // Trigger the long press. |
| await tester.pump(kLongPressTimeout + const Duration(seconds: 1)); |
| |
| expect(longPressTimes, 1); |
| expect(forcePressStart, 0); |
| |
| // Failed attempt to trigger the force press. |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(400.0, 50.0), pressure: 0.5, pressureMin: 0, pressureMax: maxPressure)); |
| |
| expect(longPressTimes, 1); |
| expect(forcePressStart, 0); |
| }); |
| |
| testWidgets('Force Press Callback not called if drag triggered before force press', (WidgetTester tester) async { |
| int forcePressStart = 0; |
| int horizontalDragStart = 0; |
| |
| await tester.pumpWidget( |
| Container( |
| alignment: Alignment.topLeft, |
| child: Container( |
| alignment: Alignment.center, |
| height: 100.0, |
| color: const Color(0xFF00FF00), |
| child: GestureDetector( |
| onForcePressStart: (_) => forcePressStart += 1, |
| onHorizontalDragStart: (_) => horizontalDragStart += 1, |
| ), |
| ), |
| ), |
| ); |
| |
| const int pointerValue = 1; |
| |
| final TestGesture gesture = await tester.createGesture(); |
| |
| await gesture.downWithCustomEvent( |
| forcePressOffset, |
| const PointerDownEvent( |
| pointer: pointerValue, |
| position: forcePressOffset, |
| pressure: 0.0, |
| pressureMax: 6.0, |
| pressureMin: 0.0, |
| ), |
| ); |
| |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.3, pressureMin: 0, pressureMax: 1)); |
| |
| expect(forcePressStart, 0); |
| expect(horizontalDragStart, 0); |
| |
| // Trigger horizontal drag. |
| await gesture.moveBy(const Offset(100, 0)); |
| |
| expect(horizontalDragStart, 1); |
| expect(forcePressStart, 0); |
| |
| // Failed attempt to trigger the force press. |
| await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); |
| |
| expect(horizontalDragStart, 1); |
| expect(forcePressStart, 0); |
| }); |
| |
| group("RawGestureDetectorState's debugFillProperties", () { |
| testWidgets('when default', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| final GlobalKey key = GlobalKey(); |
| await tester.pumpWidget(RawGestureDetector( |
| key: key, |
| )); |
| key.currentState.debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, <String>[ |
| 'gestures: <none>', |
| ]); |
| }); |
| |
| testWidgets('should show gestures, custom semantics and behavior', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| final GlobalKey key = GlobalKey(); |
| await tester.pumpWidget(RawGestureDetector( |
| key: key, |
| behavior: HitTestBehavior.deferToChild, |
| gestures: <Type, GestureRecognizerFactory>{ |
| TapGestureRecognizer: GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>( |
| () => TapGestureRecognizer(), |
| (TapGestureRecognizer recognizer) { |
| recognizer.onTap = () {}; |
| }, |
| ), |
| LongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>( |
| () => LongPressGestureRecognizer(), |
| (LongPressGestureRecognizer recognizer) { |
| recognizer.onLongPress = () {}; |
| }, |
| ), |
| }, |
| child: Container(), |
| semantics: _EmptySemanticsGestureDelegate(), |
| )); |
| key.currentState.debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, <String>[ |
| 'gestures: tap, long press', |
| 'semantics: _EmptySemanticsGestureDelegate()', |
| 'behavior: deferToChild', |
| ]); |
| }); |
| |
| testWidgets('should not show semantics when excludeFromSemantics is true', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| final GlobalKey key = GlobalKey(); |
| await tester.pumpWidget(RawGestureDetector( |
| key: key, |
| gestures: const <Type, GestureRecognizerFactory>{}, |
| child: Container(), |
| semantics: _EmptySemanticsGestureDelegate(), |
| excludeFromSemantics: true, |
| )); |
| key.currentState.debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, <String>[ |
| 'gestures: <none>', |
| 'excludeFromSemantics: true', |
| ]); |
| }); |
| |
| group('error control test', () { |
| test('constructor redundant pan and scale', () { |
| FlutterError error; |
| try { |
| GestureDetector(onScaleStart: (_) {}, onPanStart: (_) {},); |
| } on FlutterError catch (e) { |
| error = e; |
| } finally { |
| expect(error, isNotNull); |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' Incorrect GestureDetector arguments.\n' |
| ' Having both a pan gesture recognizer and a scale gesture\n' |
| ' recognizer is redundant; scale is a superset of pan.\n' |
| ' Just use the scale gesture recognizer.\n', |
| ); |
| expect(error.diagnostics.last.level, DiagnosticLevel.hint); |
| expect( |
| error.diagnostics.last.toStringDeep(), |
| equalsIgnoringHashCodes( |
| 'Just use the scale gesture recognizer.\n', |
| ) |
| ); |
| } |
| }); |
| |
| test('constructur duplicate drag recognizer', () { |
| FlutterError error; |
| try { |
| GestureDetector( |
| onVerticalDragStart: (_) {}, |
| onHorizontalDragStart: (_) {}, |
| onPanStart: (_) {}, |
| ); |
| } on FlutterError catch (e) { |
| error = e; |
| } finally { |
| expect(error, isNotNull); |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' Incorrect GestureDetector arguments.\n' |
| ' Simultaneously having a vertical drag gesture recognizer, a\n' |
| ' horizontal drag gesture recognizer, and a pan gesture recognizer\n' |
| ' will result in the pan gesture recognizer being ignored, since\n' |
| ' the other two will catch all drags.\n', |
| ); |
| } |
| }); |
| |
| testWidgets('replaceGestureRecognizers not during layout', (WidgetTester tester) async { |
| final GlobalKey<RawGestureDetectorState> key = GlobalKey<RawGestureDetectorState>(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: RawGestureDetector( |
| key: key, |
| child: Container( |
| child: const Text('Text'), |
| ), |
| ), |
| ), |
| ); |
| FlutterError error; |
| try { |
| key.currentState.replaceGestureRecognizers( |
| <Type, GestureRecognizerFactory>{}); |
| } on FlutterError catch (e) { |
| error = e; |
| } finally { |
| expect(error, isNotNull); |
| expect(error.diagnostics.last.level, DiagnosticLevel.hint); |
| expect( |
| error.diagnostics.last.toStringDeep(), |
| equalsIgnoringHashCodes( |
| 'To set the gesture recognizers at other times, trigger a new\n' |
| 'build using setState() and provide the new gesture recognizers as\n' |
| 'constructor arguments to the corresponding RawGestureDetector or\n' |
| 'GestureDetector object.\n' |
| ), |
| ); |
| expect( |
| error.toStringDeep(), |
| 'FlutterError\n' |
| ' Unexpected call to replaceGestureRecognizers() method of\n' |
| ' RawGestureDetectorState.\n' |
| ' The replaceGestureRecognizers() method can only be called during\n' |
| ' the layout phase.\n' |
| ' To set the gesture recognizers at other times, trigger a new\n' |
| ' build using setState() and provide the new gesture recognizers as\n' |
| ' constructor arguments to the corresponding RawGestureDetector or\n' |
| ' GestureDetector object.\n', |
| ); |
| } |
| }); |
| }); |
| }); |
| } |
| |
| class _EmptySemanticsGestureDelegate extends SemanticsGestureDelegate { |
| @override |
| void assignSemantics(RenderSemanticsGestureHandler renderObject) { |
| } |
| } |