revert 26001 (#28032)

diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart
index f759b0c..4e1f5d9 100644
--- a/packages/flutter/lib/src/cupertino/text_field.dart
+++ b/packages/flutter/lib/src/cupertino/text_field.dart
@@ -478,25 +478,8 @@
     _requestKeyboard();
   }
 
-  void _handleSingleLongTapStart(GestureLongPressDragStartDetails details) {
-    _renderEditable.selectPositionAt(
-      from: details.globalPosition,
-      cause: SelectionChangedCause.longPress,
-    );
-  }
-
-  void _handleSingleLongTapDragUpdate(GestureLongPressDragUpdateDetails details) {
-    _renderEditable.selectPositionAt(
-      from: details.globalPosition,
-      cause: SelectionChangedCause.longPress,
-    );
-  }
-
-  void _handleSingleLongTapUp(GestureLongPressDragUpDetails details) {
-    _renderEditable.selectPositionAt(
-      from: details.globalPosition,
-      cause: SelectionChangedCause.longPress
-    );
+  void _handleSingleLongTapDown() {
+    _renderEditable.selectPosition(cause: SelectionChangedCause.longPress);
     _editableTextKey.currentState.showToolbar();
   }
 
@@ -505,12 +488,6 @@
     _editableTextKey.currentState.showToolbar();
   }
 
-  void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) {
-    if (cause == SelectionChangedCause.longPress) {
-      _editableTextKey.currentState?.bringIntoView(selection.base);
-    }
-  }
-
   @override
   bool get wantKeepAlive => _controller?.text?.isNotEmpty == true;
 
@@ -664,7 +641,6 @@
           selectionColor: _kSelectionHighlightColor,
           selectionControls: cupertinoTextSelectionControls,
           onChanged: widget.onChanged,
-          onSelectionChanged: _handleSelectionChanged,
           onEditingComplete: widget.onEditingComplete,
           onSubmitted: widget.onSubmitted,
           inputFormatters: formatters,
@@ -705,9 +681,7 @@
               onForcePressStart: _handleForcePressStarted,
               onForcePressEnd: _handleForcePressEnded,
               onSingleTapUp: _handleSingleTapUp,
-              onSingleLongTapStart: _handleSingleLongTapStart,
-              onSingleLongTapDragUpdate: _handleSingleLongTapDragUpdate,
-              onSingleLongTapUp: _handleSingleLongTapUp,
+              onSingleLongTapDown: _handleSingleLongTapDown,
               onDoubleTapDown: _handleDoubleTapDown,
               behavior: HitTestBehavior.translucent,
               child: _addTextDependentAttachments(paddedEditable, textStyle),
diff --git a/packages/flutter/lib/src/gestures/long_press.dart b/packages/flutter/lib/src/gestures/long_press.dart
index 7fa6921..f9f7239 100644
--- a/packages/flutter/lib/src/gestures/long_press.dart
+++ b/packages/flutter/lib/src/gestures/long_press.dart
@@ -11,117 +11,9 @@
 /// same location for a long period of time.
 typedef GestureLongPressCallback = void Function();
 
-/// Signature for when a pointer stops contacting the screen after a long press
-/// gesture was detected.
+/// Signature for when a pointer stops contacting the screen after a long press gesture was detected.
 typedef GestureLongPressUpCallback = void Function();
 
-/// Signature from a [LongPressDragGestureRecognizer] when a pointer has remained
-/// in contact with the screen at the same location for a long period of time.
-typedef GestureLongPressDragStartCallback = void Function(GestureLongPressDragStartDetails details);
-
-/// Signature from a [LongPressDragGestureRecognizer] when a pointer is moving
-/// after being held in contact at the same location for a long period of time.
-typedef GestureLongPressDragUpdateCallback = void Function(GestureLongPressDragUpdateDetails details);
-
-/// Signature from a [LongPressDragGestureRecognizer] after a pointer stops
-/// contacting the screen.
-///
-/// The contact stop position may be different from the contact start position.
-typedef GestureLongPressDragUpCallback = void Function(GestureLongPressDragUpDetails details);
-
-/// Details for callbacks that use [GestureLongPressDragStartCallback].
-///
-/// See also:
-///
-///  * [LongPressDragGestureRecognizer.onLongPressStart], which uses [GestureLongPressDragStartCallback].
-///  * [GestureLongPressDragUpdateDetails], the details for [GestureLongPressDragUpdateCallback]
-///  * [GestureLongPressDragUpDetails], the details for [GestureLongPressDragUpCallback].
-class GestureLongPressDragStartDetails {
-  /// Creates the details for a [GestureLongPressDragStartCallback].
-  ///
-  /// The [globalPosition] argument must not be null.
-  const GestureLongPressDragStartDetails({ this.sourceTimeStamp, this.globalPosition = Offset.zero })
-    : assert(globalPosition != null);
-
-  /// Recorded timestamp of the source pointer event that triggered the press
-  /// event.
-  ///
-  /// Could be null if triggered by proxied events such as accessibility.
-  ///
-  /// See also:
-  ///
-  ///  * [PointerEvent.synthesized] for details on synthesized pointer events.
-  final Duration sourceTimeStamp;
-
-  /// The global position at which the pointer contacted the screen.
-  final Offset globalPosition;
-}
-
-/// Details for callbacks that use [GestureLongPressDragUpdateCallback].
-///
-/// See also:
-///
-///  * [LongPressDragGestureRecognizer.onLongPressDragUpdate], which uses [GestureLongPressDragUpdateCallback].
-///  * [GestureLongPressDragUpDetails], the details for [GestureLongPressDragUpCallback]
-///  * [GestureLongPressDragStartDetails], the details for [GestureLongPressDragStartCallback].
-class GestureLongPressDragUpdateDetails {
-  /// Creates the details for a [GestureLongPressDragUpdateCallback].
-  ///
-  /// The [globalPosition] and [offsetFromOrigin] arguments must not be null.
-  const GestureLongPressDragUpdateDetails({
-    this.sourceTimeStamp,
-    this.globalPosition = Offset.zero,
-    this.offsetFromOrigin = Offset.zero,
-  }) : assert(globalPosition != null),
-       assert(offsetFromOrigin != null);
-
-  /// Recorded timestamp of the source pointer event that triggered the press
-  /// event.
-  ///
-  /// Could be null if triggered by proxied events such as accessibility.
-  ///
-  /// See also:
-  ///
-  ///  * [PointerEvent.synthesized] for details on synthesized pointer events.
-  final Duration sourceTimeStamp;
-
-  /// The global position of the pointer when it triggered this update.
-  final Offset globalPosition;
-
-  /// A delta offset from the point where the long press drag initially contacted
-  /// the screen to the point where the pointer is currently located when
-  /// this callback is triggered.
-  final Offset offsetFromOrigin;
-}
-
-/// Details for callbacks that use [GestureLongPressDragUpCallback].
-///
-/// See also:
-///
-///  * [LongPressDragGestureRecognizer.onLongPressUp], which uses [GestureLongPressDragUpCallback].
-///  * [GestureLongPressDragUpdateDetails], the details for [GestureLongPressDragUpdateCallback]
-///  * [GestureLongPressDragStartDetails], the details for [GestureLongPressDragStartCallback].
-class GestureLongPressDragUpDetails {
-  /// Creates the details for a [GestureLongPressDragUpCallback].
-  ///
-  /// The [globalPosition] argument must not be null.
-  const GestureLongPressDragUpDetails({ this.sourceTimeStamp, this.globalPosition = Offset.zero })
-    : assert(globalPosition != null);
-
-  /// Recorded timestamp of the source pointer event that triggered the press
-  /// event.
-  ///
-  /// Could be null if triggered by proxied events such as accessibility.
-  ///
-  /// See also:
-  ///
-  ///  * [PointerEvent.synthesized] for details on synthesized pointer events.
-  final Duration sourceTimeStamp;
-
-  /// The global position at which the pointer lifted from the screen.
-  final Offset globalPosition;
-}
-
 /// Recognizes when the user has pressed down at the same location for a long
 /// period of time.
 class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
@@ -136,8 +28,7 @@
   /// Called when a long press gesture has been recognized.
   GestureLongPressCallback onLongPress;
 
-  /// Called when the pointer stops contacting the screen after the long-press
-  /// gesture has been recognized.
+  /// Called when the pointer stops contacting the screen after the long-press gesture has been recognized.
   GestureLongPressUpCallback onLongPressUp;
 
   @override
@@ -167,107 +58,3 @@
   @override
   String get debugDescription => 'long press';
 }
-
-/// Recognizes long presses that can be subsequently dragged around.
-///
-/// Similar to a [LongPressGestureRecognizer] where a press has to be held down
-/// at the same location for a long period of time. However drag events that
-/// occur after the long-press hold threshold has past will not cancel the
-/// gesture. The [onLongPressDragUpdate] callback will be called until an up
-/// event occurs.
-///
-/// See also:
-///
-///  * [LongPressGestureRecognizer], which cancels its gesture if a drag event
-///    occurs at any point during the long-press.
-class LongPressDragGestureRecognizer extends PrimaryPointerGestureRecognizer {
-  /// Creates a long-press-drag gesture recognizer.
-  ///
-  /// Consider assigning the [onLongPressStart], [onLongPressDragUpdate] and
-  /// the [onLongPressUp] callbacks after creating this object.
-  LongPressDragGestureRecognizer({ Object debugOwner }) : super(
-    deadline: kLongPressTimeout,
-    // Since it's a drag gesture, no travel distance will cause it to get
-    // rejected after the long-press is accepted.
-    postAcceptSlopTolerance: null,
-    debugOwner: debugOwner,
-  );
-
-  bool _longPressAccepted = false;
-
-  Offset _longPressOrigin;
-
-  Duration _longPressStartTimestamp;
-
-  /// Called when a long press gesture has been recognized.
-  GestureLongPressDragStartCallback onLongPressStart;
-
-  /// Called as the primary pointer is dragged after the long press.
-  GestureLongPressDragUpdateCallback onLongPressDragUpdate;
-
-  /// Called when the pointer stops contacting the screen after the
-  /// long-press gesture has been recognized.
-  GestureLongPressDragUpCallback onLongPressUp;
-
-  @override
-  void didExceedDeadline() {
-    resolve(GestureDisposition.accepted);
-    _longPressAccepted = true;
-    super.acceptGesture(primaryPointer);
-    if (onLongPressStart != null) {
-      invokeCallback<void>('onLongPressStart', () {
-        onLongPressStart(GestureLongPressDragStartDetails(
-          sourceTimeStamp: _longPressStartTimestamp,
-          globalPosition: _longPressOrigin,
-        ));
-      });
-    }
-  }
-
-  @override
-  void handlePrimaryPointer(PointerEvent event) {
-    if (event is PointerUpEvent) {
-      if (_longPressAccepted == true && onLongPressUp != null) {
-        _longPressAccepted = false;
-        invokeCallback<void>('onLongPressUp', () {
-          onLongPressUp(GestureLongPressDragUpDetails(
-            sourceTimeStamp: event.timeStamp,
-            globalPosition: event.position,
-          ));
-        });
-      } else {
-        resolve(GestureDisposition.rejected);
-      }
-    } else if (event is PointerDownEvent) {
-      // The first touch.
-      _longPressAccepted = false;
-      _longPressStartTimestamp = event.timeStamp;
-      _longPressOrigin = event.position;
-    } else if (event is PointerMoveEvent && _longPressAccepted && onLongPressDragUpdate != null) {
-      invokeCallback<void>('onLongPressDrag', () {
-        onLongPressDragUpdate(GestureLongPressDragUpdateDetails(
-          sourceTimeStamp: event.timeStamp,
-          globalPosition: event.position,
-          offsetFromOrigin: event.position - _longPressOrigin,
-        ));
-      });
-    }
-  }
-
-  @override
-  void acceptGesture(int pointer) {
-    // Winning the arena isn't important here since it may happen from a sweep.
-    // Explicitly exceeding the deadline puts the gesture in accepted state.
-  }
-
-  @override
-  void didStopTrackingLastPointer(int pointer) {
-    _longPressAccepted = false;
-    _longPressOrigin = null;
-    _longPressStartTimestamp = null;
-    super.didStopTrackingLastPointer(pointer);
-  }
-
-  @override
-  String get debugDescription => 'long press drag';
-}
diff --git a/packages/flutter/lib/src/gestures/recognizer.dart b/packages/flutter/lib/src/gestures/recognizer.dart
index 71b4beb..d5ddbd6 100644
--- a/packages/flutter/lib/src/gestures/recognizer.dart
+++ b/packages/flutter/lib/src/gestures/recognizer.dart
@@ -262,10 +262,10 @@
 /// The possible states of a [PrimaryPointerGestureRecognizer].
 ///
 /// The recognizer advances from [ready] to [possible] when starts tracking a
-/// primary pointer. The recognizer moves to [accepted] when resolved to win the
-/// gesture arena. It may then move to [defunct] from [accepted] or go to
-/// [defunct] directly when rejected. Once the recognizer has stopped tracking
-/// any remaining pointers, the recognizer returns to [ready].
+/// primary pointer. When the primary pointer is resolve (either accepted or
+/// or rejected), the recognizers advances to [defunct]. Once the recognizer
+/// has stopped tracking any remaining pointers, the recognizer returns to
+/// [ready].
 enum GestureRecognizerState {
   /// The recognizer is ready to start recognizing a gesture.
   ready,
@@ -275,9 +275,6 @@
   /// been accepted definitively.
   possible,
 
-  /// The gesture has been definitively accepted by the recognizer.
-  accepted,
-
   /// Further pointer events cannot cause this recognizer to recognize the
   /// gesture until the recognizer returns to the [ready] state (typically when
   /// all the pointers the recognizer is tracking are removed from the screen).
@@ -287,43 +284,18 @@
 /// A base class for gesture recognizers that track a single primary pointer.
 ///
 /// Gestures based on this class will reject the gesture if the primary pointer
-/// travels beyond [preAcceptSlopTolerance] pixels from the original contact
-/// point before the gesture is accepted or beyond [postAcceptSlopTolerance]
-/// from where the pointer was after the gesture was accepted.
+/// travels beyond [kTouchSlop] pixels from the original contact point.
 abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecognizer {
   /// Initializes the [deadline] field during construction of subclasses.
   PrimaryPointerGestureRecognizer({
     this.deadline,
-    this.preAcceptSlopTolerance = kTouchSlop,
-    this.postAcceptSlopTolerance = kTouchSlop,
     Object debugOwner,
-  }) : assert(
-         preAcceptSlopTolerance == null || preAcceptSlopTolerance >= 0,
-         'The preAcceptSlopTolerance must be positive or null',
-       ),
-       assert(
-         postAcceptSlopTolerance == null || postAcceptSlopTolerance >= 0,
-         'The postAcceptSlopTolerance must be positive or null',
-       ),
-       super(debugOwner: debugOwner);
+  }) : super(debugOwner: debugOwner);
 
   /// If non-null, the recognizer will call [didExceedDeadline] after this
   /// amount of time has elapsed since starting to track the primary pointer.
   final Duration deadline;
 
-  /// The maximum distance in logical pixels the gesture is allowed to drift
-  /// from the initial touch down position before the gesture is accepted.
-  ///
-  /// Drifting past the allowed slop amount causes the gesture to be rejected.
-  final double preAcceptSlopTolerance;
-
-  /// The maximum distance in logical pixels the gesture is allowed to drift
-  /// after the gesture has been accepted.
-  ///
-  /// Drifting past the allowed slop amount causes the gesture to be rejected,
-  /// even after being accepted.
-  final double postAcceptSlopTolerance;
-
   /// The current state of the recognizer.
   ///
   /// See [GestureRecognizerState] for a description of the states.
@@ -352,17 +324,9 @@
   @override
   void handleEvent(PointerEvent event) {
     assert(state != GestureRecognizerState.ready);
-    if (event.pointer == primaryPointer) {
-      final bool isPreAcceptSlopPastTolerance =
-          state == GestureRecognizerState.possible &&
-          preAcceptSlopTolerance != null &&
-          _getDistance(event) > preAcceptSlopTolerance;
-      final bool isPostAcceptSlopPastTolerance =
-          state == GestureRecognizerState.accepted &&
-          postAcceptSlopTolerance != null &&
-          _getDistance(event) > postAcceptSlopTolerance;
-
-      if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) {
+    if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
+      // TODO(abarth): Maybe factor the slop handling out into a separate class?
+      if (event is PointerMoveEvent && _getDistance(event) > kTouchSlop) {
         resolve(GestureDisposition.rejected);
         stopTrackingPointer(primaryPointer);
       } else {
@@ -385,19 +349,8 @@
   }
 
   @override
-  void acceptGesture(int pointer) {
-    // Ignore state 'ready' here because that would happen if this recognizer
-    // won by a sweep.
-    if (pointer == primaryPointer && state == GestureRecognizerState.possible) {
-      state = GestureRecognizerState.accepted;
-    }
-  }
-
-  @override
   void rejectGesture(int pointer) {
-    if (pointer == primaryPointer
-        && (state == GestureRecognizerState.possible ||
-            state == GestureRecognizerState.accepted)) {
+    if (pointer == primaryPointer && state == GestureRecognizerState.possible) {
       _stopTimer();
       state = GestureRecognizerState.defunct;
     }
diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart
index 6f01f6b..6619290 100644
--- a/packages/flutter/lib/src/material/text_field.dart
+++ b/packages/flutter/lib/src/material/text_field.dart
@@ -566,15 +566,6 @@
     _editableTextKey.currentState?.requestKeyboard();
   }
 
-  void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) {
-    // iOS cursor doesn't move via a selection handle. The scroll happens
-    // directly from new text selection changes.
-    if (Theme.of(context).platform == TargetPlatform.iOS
-        && cause == SelectionChangedCause.longPress) {
-      _editableTextKey.currentState?.bringIntoView(selection.base);
-    }
-  }
-
   InteractiveInkFeature _createInkFeature(TapDownDetails details) {
     final MaterialInkController inkController = Material.of(context);
     final ThemeData themeData = Theme.of(context);
@@ -648,14 +639,11 @@
     _cancelCurrentSplash();
   }
 
-  void _handleSingleLongTapStart(GestureLongPressDragStartDetails details) {
+  void _handleSingleLongTapDown() {
     if (widget.selectionEnabled) {
       switch (Theme.of(context).platform) {
         case TargetPlatform.iOS:
-          _renderEditable.selectPositionAt(
-            from: details.globalPosition,
-            cause: SelectionChangedCause.longPress,
-          );
+          _renderEditable.selectPosition(cause: SelectionChangedCause.longPress);
           break;
         case TargetPlatform.android:
         case TargetPlatform.fuchsia:
@@ -663,35 +651,11 @@
           Feedback.forLongPress(context);
           break;
       }
+      _editableTextKey.currentState.showToolbar();
     }
     _confirmCurrentSplash();
   }
 
-  void _handleSingleLongTapDragUpdate(GestureLongPressDragUpdateDetails details) {
-    if (widget.selectionEnabled) {
-      switch (Theme.of(context).platform) {
-        case TargetPlatform.iOS:
-          _renderEditable.selectPositionAt(
-            from: details.globalPosition,
-            cause: SelectionChangedCause.longPress,
-          );
-          break;
-        case TargetPlatform.android:
-        case TargetPlatform.fuchsia:
-          _renderEditable.selectWordsInRange(
-            from: details.globalPosition - details.offsetFromOrigin,
-            to: details.globalPosition,
-            cause: SelectionChangedCause.longPress,
-          );
-          break;
-      }
-    }
-  }
-
-  void _handleSingleLongTapUp(GestureLongPressDragUpDetails details) {
-    _editableTextKey.currentState.showToolbar();
-  }
-
   void _handleDoubleTapDown(TapDownDetails details) {
     if (widget.selectionEnabled) {
       _renderEditable.selectWord(cause: SelectionChangedCause.doubleTap);
@@ -810,7 +774,6 @@
         selectionColor: themeData.textSelectionColor,
         selectionControls: widget.selectionEnabled ? textSelectionControls : null,
         onChanged: widget.onChanged,
-        onSelectionChanged: _handleSelectionChanged,
         onEditingComplete: widget.onEditingComplete,
         onSubmitted: widget.onSubmitted,
         inputFormatters: formatters,
@@ -859,9 +822,7 @@
           onForcePressStart: forcePressEnabled ? _handleForcePressStarted : null,
           onSingleTapUp: _handleSingleTapUp,
           onSingleTapCancel: _handleSingleTapCancel,
-          onSingleLongTapStart: _handleSingleLongTapStart,
-          onSingleLongTapDragUpdate: _handleSingleLongTapDragUpdate,
-          onSingleLongTapUp: _handleSingleLongTapUp,
+          onSingleLongTapDown: _handleSingleLongTapDown,
           onDoubleTapDown: _handleDoubleTapDown,
           behavior: HitTestBehavior.translucent,
           child: child,
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index c7633ab..08a8f9b 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -1191,7 +1191,7 @@
   /// When [ignorePointer] is true, an ancestor widget must respond to tap
   /// down events by calling this method.
   void handleTapDown(TapDownDetails details) {
-    _lastTapDownPosition = details.globalPosition - _paintOffset;
+    _lastTapDownPosition = details.globalPosition + -_paintOffset;
   }
   void _handleTapDown(TapDownDetails details) {
     assert(!ignorePointer);
@@ -1247,27 +1247,12 @@
   /// programmatically manipulate its `value` or `selection` directly.
   /// {@endtemplate}
   void selectPosition({@required SelectionChangedCause cause}) {
-    selectPositionAt(from: _lastTapDownPosition, cause: cause);
-  }
-
-  /// Select text between the global positions [from] and [to].
-  void selectPositionAt({@required Offset from, Offset to, @required SelectionChangedCause cause}) {
     assert(cause != null);
     _layoutText(constraints.maxWidth);
+    assert(_lastTapDownPosition != null);
     if (onSelectionChanged != null) {
-      final TextPosition fromPosition = _textPainter.getPositionForOffset(globalToLocal(from - _paintOffset));
-      final TextPosition toPosition = to == null
-        ? null
-        : _textPainter.getPositionForOffset(globalToLocal(to - _paintOffset));
-      onSelectionChanged(
-        TextSelection(
-          baseOffset: fromPosition.offset,
-          extentOffset: toPosition?.offset ?? fromPosition.offset,
-          affinity: fromPosition.affinity,
-        ),
-        this,
-        cause,
-      );
+      final TextPosition position = _textPainter.getPositionForOffset(globalToLocal(_lastTapDownPosition));
+      onSelectionChanged(TextSelection.fromPosition(position), this, cause);
     }
   }
 
@@ -1288,10 +1273,10 @@
     assert(cause != null);
     _layoutText(constraints.maxWidth);
     if (onSelectionChanged != null) {
-      final TextPosition firstPosition = _textPainter.getPositionForOffset(globalToLocal(from - _paintOffset));
+      final TextPosition firstPosition = _textPainter.getPositionForOffset(globalToLocal(from + -_paintOffset));
       final TextSelection firstWord = _selectWordAtOffset(firstPosition);
       final TextSelection lastWord = to == null ?
-        firstWord : _selectWordAtOffset(_textPainter.getPositionForOffset(globalToLocal(to - _paintOffset)));
+        firstWord : _selectWordAtOffset(_textPainter.getPositionForOffset(globalToLocal(to + -_paintOffset)));
 
       onSelectionChanged(
         TextSelection(
diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart
index 8c064a0..1c50ee1 100644
--- a/packages/flutter/lib/src/widgets/gesture_detector.dart
+++ b/packages/flutter/lib/src/widgets/gesture_detector.dart
@@ -19,10 +19,6 @@
   GestureTapCallback,
   GestureTapCancelCallback,
   GestureLongPressCallback,
-  GestureLongPressUpCallback,
-  GestureLongPressDragStartCallback,
-  GestureLongPressDragUpdateCallback,
-  GestureLongPressDragUpCallback,
   GestureDragDownCallback,
   GestureDragStartCallback,
   GestureDragUpdateCallback,
@@ -35,9 +31,6 @@
   GestureForcePressPeakCallback,
   GestureForcePressEndCallback,
   GestureForcePressUpdateCallback,
-  GestureLongPressDragStartDetails,
-  GestureLongPressDragUpdateDetails,
-  GestureLongPressDragUpDetails,
   ScaleStartDetails,
   ScaleUpdateDetails,
   ScaleEndDetails,
@@ -157,10 +150,6 @@
   /// because a combination of a horizontal and vertical drag is a pan. Simply
   /// use the pan callbacks instead.
   ///
-  /// Long press and long press drag callbacks cannot be used simultaneously
-  /// since they overlap. A long press cannot be subsequently dragged while a
-  /// long press drag can be dragged after a long press.
-  ///
   /// By default, gesture detectors contribute semantic information to the tree
   /// that is used by assistive technology.
   GestureDetector({
@@ -173,9 +162,6 @@
     this.onDoubleTap,
     this.onLongPress,
     this.onLongPressUp,
-    this.onLongPressDragStart,
-    this.onLongPressDragUpdate,
-    this.onLongPressDragUp,
     this.onVerticalDragDown,
     this.onVerticalDragStart,
     this.onVerticalDragUpdate,
@@ -208,8 +194,6 @@
          final bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null;
          final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
          final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
-         final bool haveLongPress = onLongPress != null || onLongPressUp != null;
-         final bool haveLongPressDrag = onLongPressDragStart != null || onLongPressDragUpdate != null || onLongPressDragUp != null;
          if (havePan || haveScale) {
            if (havePan && haveScale) {
              throw FlutterError(
@@ -226,15 +210,6 @@
              );
            }
          }
-         if (haveLongPress && haveLongPressDrag) {
-           throw FlutterError(
-             'Incorrect GestureDetector arguments.\n'
-             'Having both a long press and a long press drag recognizer is '
-             'redundant as the long press drag is a superset of long press. '
-             'Except long press drag allows for drags after the long press is '
-             'triggered.'
-           );
-         }
          return true;
        }()),
        super(key: key);
@@ -283,37 +258,11 @@
 
   /// A pointer has remained in contact with the screen at the same location for
   /// a long period of time.
-  ///
-  /// The long press drag callbacks [onLongPressDragStart], [onLongPressDragUpdate]
-  /// and [onLongPressDragUp] cannot be set while this callback is set.
   final GestureLongPressCallback onLongPress;
 
   /// A pointer that has triggered a long-press has stopped contacting the screen.
-  ///
-  /// The long press drag callbacks [onLongPressDragStart], [onLongPressDragUpdate]
-  /// and [onLongPressDragUp] cannot be set while this callback is set.
   final GestureLongPressUpCallback onLongPressUp;
 
-  /// A pointer has remained in contact with the screen at the same location for
-  /// a long period of time and can subsequently be dragged.
-  ///
-  /// The non-drag long press callbacks [onLongPress] and [onLongPressUp] cannot
-  /// be set while this callback is set.
-  final GestureLongPressDragStartCallback onLongPressDragStart;
-
-  /// A pointer has been drag-moved after a long press.
-  ///
-  /// The non-drag long press callbacks [onLongPress] and [onLongPressUp] cannot
-  /// be set while this callback is set.
-  final GestureLongPressDragUpdateCallback onLongPressDragUpdate;
-
-  /// A pointer that has triggered a long press has stopped contacting the screen
-  /// regardless of whether the pointer is dragged after the long press.
-  ///
-  /// The non-drag long press callbacks [onLongPress] and [onLongPressUp] cannot
-  /// be set while this callback is set.
-  final GestureLongPressDragUpCallback onLongPressDragUp;
-
   /// A pointer has contacted the screen and might begin to move vertically.
   final GestureDragDownCallback onVerticalDragDown;
 
@@ -473,7 +422,7 @@
       );
     }
 
-    if (onLongPress != null || onLongPressUp != null) {
+    if (onLongPress != null || onLongPressUp !=null) {
       gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
         () => LongPressGestureRecognizer(debugOwner: this),
         (LongPressGestureRecognizer instance) {
@@ -484,18 +433,6 @@
       );
     }
 
-    if (onLongPressDragStart != null || onLongPressDragUpdate != null || onLongPressDragUp != null) {
-      gestures[LongPressDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<LongPressDragGestureRecognizer>(
-        () => LongPressDragGestureRecognizer(debugOwner: this),
-        (LongPressDragGestureRecognizer instance) {
-          instance
-            ..onLongPressStart = onLongPressDragStart
-            ..onLongPressDragUpdate = onLongPressDragUpdate
-            ..onLongPressUp = onLongPressDragUp;
-        },
-      );
-    }
-
     if (onVerticalDragDown != null ||
         onVerticalDragStart != null ||
         onVerticalDragUpdate != null ||
diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart
index 02b2b95..0351213 100644
--- a/packages/flutter/lib/src/widgets/text_selection.dart
+++ b/packages/flutter/lib/src/widgets/text_selection.dart
@@ -617,9 +617,7 @@
     this.onForcePressEnd,
     this.onSingleTapUp,
     this.onSingleTapCancel,
-    this.onSingleLongTapStart,
-    this.onSingleLongTapDragUpdate,
-    this.onSingleLongTapUp,
+    this.onSingleLongTapDown,
     this.onDoubleTapDown,
     this.behavior,
     @required this.child,
@@ -653,13 +651,7 @@
   /// Called for a single long tap that's sustained for longer than
   /// [kLongPressTimeout] but not necessarily lifted. Not called for a
   /// double-tap-hold, which calls [onDoubleTapDown] instead.
-  final GestureLongPressDragStartCallback onSingleLongTapStart;
-
-  /// Called after [onSingleLongTapStart] when the pointer is dragged.
-  final GestureLongPressDragUpdateCallback onSingleLongTapDragUpdate;
-
-  /// Called after [onSingleLongTapStart] when the pointer is lifted.
-  final GestureLongPressDragUpCallback onSingleLongTapUp;
+  final GestureLongPressCallback onSingleLongTapDown;
 
   /// Called after a momentary hold or a short tap that is close in space and
   /// time (within [kDoubleTapTimeout]) to a previous short tap.
@@ -743,21 +735,9 @@
       widget.onForcePressEnd(details);
   }
 
-  void _handleLongDragStart(GestureLongPressDragStartDetails details) {
-    if (!_isDoubleTap && widget.onSingleLongTapStart != null) {
-      widget.onSingleLongTapStart(details);
-    }
-  }
-
-  void _handleLongDragUpdate(GestureLongPressDragUpdateDetails details) {
-    if (!_isDoubleTap && widget.onSingleLongTapDragUpdate != null) {
-      widget.onSingleLongTapDragUpdate(details);
-    }
-  }
-
-  void _handleLongDragUp(GestureLongPressDragUpDetails details) {
-    if (!_isDoubleTap && widget.onSingleLongTapUp != null) {
-      widget.onSingleLongTapUp(details);
+  void _handleLongPress() {
+    if (!_isDoubleTap && widget.onSingleLongTapDown != null) {
+      widget.onSingleLongTapDown();
     }
     _isDoubleTap = false;
   }
@@ -785,9 +765,7 @@
       onForcePressStart: widget.onForcePressStart != null ? _forcePressStarted : null,
       onForcePressEnd: widget.onForcePressEnd != null ? _forcePressEnded : null,
       onTapCancel: _handleTapCancel,
-      onLongPressDragStart: _handleLongDragStart,
-      onLongPressDragUpdate: _handleLongDragUpdate,
-      onLongPressDragUp: _handleLongDragUp,
+      onLongPress: _handleLongPress,
       excludeFromSemantics: true,
       behavior: widget.behavior,
       child: widget.child,
diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart
index c17fff8..db12b4c 100644
--- a/packages/flutter/test/cupertino/text_field_test.dart
+++ b/packages/flutter/test/cupertino/text_field_test.dart
@@ -1046,7 +1046,7 @@
   );
 
   testWidgets(
-    'long press tap cannot initiate a double tap',
+    'long press tap is not a double tap',
     (WidgetTester tester) async {
       final TextEditingController controller = TextEditingController(
         text: 'Atwater Peel Sherbrooke Bonaventure',
@@ -1076,163 +1076,12 @@
         const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
       );
 
-      // The toolbar from the long press is now dismissed by the second tap.
+      // Collapsed toolbar shows 2 buttons.
       expect(find.byType(CupertinoButton), findsNothing);
     },
   );
 
   testWidgets(
-    'long press drag moves the cursor under the drag and shows toolbar on lift',
-    (WidgetTester tester) async {
-      final TextEditingController controller = TextEditingController(
-        text: 'Atwater Peel Sherbrooke Bonaventure',
-      );
-      await tester.pumpWidget(
-        CupertinoApp(
-          home: Center(
-            child: CupertinoTextField(
-              controller: controller,
-            ),
-          ),
-        ),
-      );
-
-      final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
-
-      final TestGesture gesture =
-          await tester.startGesture(textfieldStart + const Offset(50.0, 5.0));
-      await tester.pump(const Duration(milliseconds: 500));
-
-      // Long press on iOS shows collapsed selection cursor.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 3, affinity: TextAffinity.upstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.moveBy(const Offset(50, 0));
-      await tester.pump();
-
-      // The selection position is now moved with the drag.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 6, affinity: TextAffinity.upstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.moveBy(const Offset(50, 0));
-      await tester.pump();
-
-      // The selection position is now moved with the drag.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.up();
-      await tester.pump();
-
-      // The selection isn't affected by the gesture lift.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 9, affinity: TextAffinity.upstream),
-      );
-      // The toolbar now shows up.
-      expect(find.byType(CupertinoButton), findsNWidgets(2));
-    },
-  );
-
-  testWidgets('long press drag can edge scroll', (WidgetTester tester) async {
-    final TextEditingController controller = TextEditingController(
-        text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges',
-      );
-      await tester.pumpWidget(
-        CupertinoApp(
-          home: Center(
-            child: CupertinoTextField(
-              controller: controller,
-              maxLines: 1,
-            ),
-          ),
-        ),
-      );
-
-      final RenderEditable renderEditable = tester.renderObject<RenderEditable>(
-        find.byElementPredicate((Element element) => element.renderObject is RenderEditable)
-      );
-
-      List<TextSelectionPoint> lastCharEndpoint = renderEditable.getEndpointsForSelection(
-        const TextSelection.collapsed(offset: 66), // Last character's position.
-      );
-
-      expect(lastCharEndpoint.length, 1);
-      // Just testing the test and making sure that the last character is off
-      // the right side of the screen.
-      expect(lastCharEndpoint[0].point.dx, moreOrLessEquals(1094.73486328125));
-
-      final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
-
-      final TestGesture gesture =
-          await tester.startGesture(textfieldStart + const Offset(300, 5));
-      await tester.pump(const Duration(milliseconds: 500));
-
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 18, affinity: TextAffinity.upstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.moveBy(const Offset(600, 0));
-      // To the edge of the screen basically.
-      await tester.pump();
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 54, affinity: TextAffinity.upstream),
-      );
-      // Keep moving out.
-      await gesture.moveBy(const Offset(1, 0));
-      await tester.pump();
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 61, affinity: TextAffinity.upstream),
-      );
-      await gesture.moveBy(const Offset(1, 0));
-      await tester.pump();
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
-      ); // We're at the edge now.
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.up();
-      await tester.pump();
-
-      // The selection isn't affected by the gesture lift.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
-      );
-      // The toolbar now shows up.
-      expect(find.byType(CupertinoButton), findsNWidgets(2));
-
-      lastCharEndpoint = renderEditable.getEndpointsForSelection(
-        const TextSelection.collapsed(offset: 66), // Last character's position.
-      );
-
-      expect(lastCharEndpoint.length, 1);
-      // The last character is now on screen.
-      expect(lastCharEndpoint[0].point.dx, moreOrLessEquals(786.73486328125));
-
-      final List<TextSelectionPoint> firstCharEndpoint = renderEditable.getEndpointsForSelection(
-        const TextSelection.collapsed(offset: 0), // First character's position.
-      );
-      expect(firstCharEndpoint.length, 1);
-      // The first character is now offscreen to the left.
-      expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-308.20499999821186));
-  });
-
-  testWidgets(
     'long tap after a double tap select is not affected',
     (WidgetTester tester) async {
       final TextEditingController controller = TextEditingController(
diff --git a/packages/flutter/test/gestures/long_press_test.dart b/packages/flutter/test/gestures/long_press_test.dart
index c0a6244..10c6c4e 100644
--- a/packages/flutter/test/gestures/long_press_test.dart
+++ b/packages/flutter/test/gestures/long_press_test.dart
@@ -9,275 +9,153 @@
 
 const PointerDownEvent down = PointerDownEvent(
   pointer: 5,
-  position: Offset(10, 10),
+  position: Offset(10.0, 10.0)
 );
 
 const PointerUpEvent up = PointerUpEvent(
   pointer: 5,
-  position: Offset(11, 9),
-);
-
-const PointerMoveEvent move = PointerMoveEvent(
-  pointer: 5,
-  position: Offset(100, 200),
+  position: Offset(11.0, 9.0)
 );
 
 void main() {
   setUp(ensureGestureBinding);
 
-  group('Long press', () {
-    LongPressGestureRecognizer longPress;
-    bool longPressDown;
-    bool longPressUp;
+  testGesture('Should recognize long press', (GestureTester tester) {
+    final LongPressGestureRecognizer longPress = LongPressGestureRecognizer();
 
-    setUp(() {
-      longPress = LongPressGestureRecognizer();
-      longPressDown = false;
-      longPress.onLongPress = () {
-        longPressDown = true;
-      };
-      longPressUp = false;
-      longPress.onLongPressUp = () {
-        longPressUp = true;
-      };
-    });
+    bool longPressRecognized = false;
+    longPress.onLongPress = () {
+      longPressRecognized = true;
+    };
 
-    testGesture('Should recognize long press', (GestureTester tester) {
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressDown, isFalse);
-      tester.route(down);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 700));
-      expect(longPressDown, isTrue);
+    longPress.addPointer(down);
+    tester.closeArena(5);
+    expect(longPressRecognized, isFalse);
+    tester.route(down);
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 300));
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 700));
+    expect(longPressRecognized, isTrue);
 
-      longPress.dispose();
-    });
-
-    testGesture('Up cancels long press', (GestureTester tester) {
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressDown, isFalse);
-      tester.route(down);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressDown, isFalse);
-      tester.route(up);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(seconds: 1));
-      expect(longPressDown, isFalse);
-
-      longPress.dispose();
-    });
-
-    testGesture('Moving before accept cancels', (GestureTester tester) {
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressDown, isFalse);
-      tester.route(down);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressDown, isFalse);
-      tester.route(move);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(seconds: 1));
-      tester.route(up);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressDown, isFalse);
-      expect(longPressUp, isFalse);
-
-      longPress.dispose();
-    });
-
-    testGesture('Moving after accept cancels', (GestureTester tester) {
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressDown, isFalse);
-      tester.route(down);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(seconds: 1));
-      expect(longPressDown, isTrue);
-      tester.route(move);
-      tester.route(up);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      // longPressDown is still true since it already happened but up never
-      // happens because it's been canceled since.
-      expect(longPressDown, isTrue);
-      expect(longPressUp, isFalse);
-
-      longPress.dispose();
-    });
-
-    testGesture('Should recognize both tap down and long press', (GestureTester tester) {
-      final TapGestureRecognizer tap = TapGestureRecognizer();
-
-      bool tapDownRecognized = false;
-      tap.onTapDown = (_) {
-        tapDownRecognized = true;
-      };
-
-      tap.addPointer(down);
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(tapDownRecognized, isFalse);
-      expect(longPressDown, isFalse);
-      tester.route(down);
-      expect(tapDownRecognized, isFalse);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(tapDownRecognized, isTrue);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 700));
-      expect(tapDownRecognized, isTrue);
-      expect(longPressDown, isTrue);
-
-      tap.dispose();
-      longPress.dispose();
-    });
-
-    testGesture('Drag start delayed by microtask', (GestureTester tester) {
-      final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer();
-
-      bool isDangerousStack = false;
-
-      bool dragStartRecognized = false;
-      drag.onStart = (DragStartDetails details) {
-        expect(isDangerousStack, isFalse);
-        dragStartRecognized = true;
-      };
-
-      drag.addPointer(down);
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(dragStartRecognized, isFalse);
-      expect(longPressDown, isFalse);
-      tester.route(down);
-      expect(dragStartRecognized, isFalse);
-      expect(longPressDown, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(dragStartRecognized, isFalse);
-      expect(longPressDown, isFalse);
-      isDangerousStack = true;
-      longPress.dispose();
-      isDangerousStack = false;
-      expect(dragStartRecognized, isFalse);
-      expect(longPressDown, isFalse);
-      tester.async.flushMicrotasks();
-      expect(dragStartRecognized, isTrue);
-      expect(longPressDown, isFalse);
-      drag.dispose();
-    });
-
-    testGesture('Should recognize long press up', (GestureTester tester) {
-      bool longPressUpRecognized = false;
-      longPress.onLongPressUp = () {
-        longPressUpRecognized = true;
-      };
-
-      longPress.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressUpRecognized, isFalse);
-      tester.route(down); // kLongPressTimeout = 500;
-      expect(longPressUpRecognized, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressUpRecognized, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 700));
-      tester.route(up);
-      expect(longPressUpRecognized, isTrue);
-
-      longPress.dispose();
-    });
+    longPress.dispose();
   });
 
-  group('long press drag', () {
-    LongPressDragGestureRecognizer longPressDrag;
-    bool longPressStart;
-    bool longPressUp;
-    Offset longPressDragUpdate;
+  testGesture('Up cancels long press', (GestureTester tester) {
+    final LongPressGestureRecognizer longPress = LongPressGestureRecognizer();
 
-    setUp(() {
-      longPressDrag = LongPressDragGestureRecognizer();
-      longPressStart = false;
-      longPressDrag.onLongPressStart = (GestureLongPressDragStartDetails details) {
-        longPressStart = true;
-      };
-      longPressUp = false;
-      longPressDrag.onLongPressUp = (GestureLongPressDragUpDetails details) {
-        longPressUp = true;
-      };
-      longPressDragUpdate = null;
-      longPressDrag.onLongPressDragUpdate = (GestureLongPressDragUpdateDetails details) {
-        longPressDragUpdate = details.globalPosition;
-      };
-    });
+    bool longPressRecognized = false;
+    longPress.onLongPress = () {
+      longPressRecognized = true;
+    };
 
-    testGesture('Should recognize long press down', (GestureTester tester) {
-      longPressDrag.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressStart, isFalse);
-      tester.route(down);
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 700));
-      expect(longPressStart, isTrue);
+    longPress.addPointer(down);
+    tester.closeArena(5);
+    expect(longPressRecognized, isFalse);
+    tester.route(down);
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 300));
+    expect(longPressRecognized, isFalse);
+    tester.route(up);
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(seconds: 1));
+    expect(longPressRecognized, isFalse);
 
-      longPressDrag.dispose();
-    });
+    longPress.dispose();
+  });
 
-    testGesture('Short up cancels long press', (GestureTester tester) {
-      longPressDrag.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressStart, isFalse);
-      tester.route(down);
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressStart, isFalse);
-      tester.route(up);
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(seconds: 1));
-      expect(longPressStart, isFalse);
+  testGesture('Should recognize both tap down and long press', (GestureTester tester) {
+    final LongPressGestureRecognizer longPress = LongPressGestureRecognizer();
+    final TapGestureRecognizer tap = TapGestureRecognizer();
 
-      longPressDrag.dispose();
-    });
+    bool tapDownRecognized = false;
+    tap.onTapDown = (_) {
+      tapDownRecognized = true;
+    };
 
-    testGesture('Moving before accept cancels', (GestureTester tester) {
-      longPressDrag.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressStart, isFalse);
-      tester.route(down);
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressStart, isFalse);
-      tester.route(move);
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(seconds: 1));
-      tester.route(up);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressStart, isFalse);
-      expect(longPressUp, isFalse);
+    bool longPressRecognized = false;
+    longPress.onLongPress = () {
+      longPressRecognized = true;
+    };
 
-      longPressDrag.dispose();
-    });
+    tap.addPointer(down);
+    longPress.addPointer(down);
+    tester.closeArena(5);
+    expect(tapDownRecognized, isFalse);
+    expect(longPressRecognized, isFalse);
+    tester.route(down);
+    expect(tapDownRecognized, isFalse);
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 300));
+    expect(tapDownRecognized, isTrue);
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 700));
+    expect(tapDownRecognized, isTrue);
+    expect(longPressRecognized, isTrue);
 
-    testGesture('Moving after accept does not cancel', (GestureTester tester) {
-      longPressDrag.addPointer(down);
-      tester.closeArena(5);
-      expect(longPressStart, isFalse);
-      tester.route(down);
-      expect(longPressStart, isFalse);
-      tester.async.elapse(const Duration(seconds: 1));
-      expect(longPressStart, isTrue);
-      tester.route(move);
-      expect(longPressDragUpdate, const Offset(100, 200));
-      tester.route(up);
-      tester.async.elapse(const Duration(milliseconds: 300));
-      expect(longPressStart, isTrue);
-      expect(longPressUp, isTrue);
+    tap.dispose();
+    longPress.dispose();
+  });
 
-      longPressDrag.dispose();
-    });
+  testGesture('Drag start delayed by microtask', (GestureTester tester) {
+    final LongPressGestureRecognizer longPress = LongPressGestureRecognizer();
+    final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer();
+
+    bool isDangerousStack = false;
+
+    bool dragStartRecognized = false;
+    drag.onStart = (DragStartDetails details) {
+      expect(isDangerousStack, isFalse);
+      dragStartRecognized = true;
+    };
+
+    bool longPressRecognized = false;
+    longPress.onLongPress = () {
+      expect(isDangerousStack, isFalse);
+      longPressRecognized = true;
+    };
+
+    drag.addPointer(down);
+    longPress.addPointer(down);
+    tester.closeArena(5);
+    expect(dragStartRecognized, isFalse);
+    expect(longPressRecognized, isFalse);
+    tester.route(down);
+    expect(dragStartRecognized, isFalse);
+    expect(longPressRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 300));
+    expect(dragStartRecognized, isFalse);
+    expect(longPressRecognized, isFalse);
+    isDangerousStack = true;
+    longPress.dispose();
+    isDangerousStack = false;
+    expect(dragStartRecognized, isFalse);
+    expect(longPressRecognized, isFalse);
+    tester.async.flushMicrotasks();
+    expect(dragStartRecognized, isTrue);
+    expect(longPressRecognized, isFalse);
+    drag.dispose();
+  });
+
+  testGesture('Should recognize long press up', (GestureTester tester) {
+    final LongPressGestureRecognizer longPress = LongPressGestureRecognizer();
+
+    bool longPressUpRecognized = false;
+    longPress.onLongPressUp = () {
+      longPressUpRecognized = true;
+    };
+
+    longPress.addPointer(down);
+    tester.closeArena(5);
+    expect(longPressUpRecognized, isFalse);
+    tester.route(down); // kLongPressTimeout = 500;
+    expect(longPressUpRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 300));
+    expect(longPressUpRecognized, isFalse);
+    tester.async.elapse(const Duration(milliseconds: 700));
+    tester.route(up);
+    expect(longPressUpRecognized, isTrue);
+
+    longPress.dispose();
   });
 }
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 3bdc359..c9e23cd 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -4143,7 +4143,7 @@
   );
 
   testWidgets(
-    'long press tap cannot initiate a double tap (iOS)',
+    'long press tap is not a double tap (iOS)',
     (WidgetTester tester) async {
       final TextEditingController controller = TextEditingController(
         text: 'Atwater Peel Sherbrooke Bonaventure',
@@ -4182,161 +4182,6 @@
   );
 
   testWidgets(
-    'long press drag moves the cursor under the drag and shows toolbar on lift (iOS)',
-    (WidgetTester tester) async {
-      final TextEditingController controller = TextEditingController(
-        text: 'Atwater Peel Sherbrooke Bonaventure',
-      );
-      await tester.pumpWidget(
-        MaterialApp(
-          theme: ThemeData(platform: TargetPlatform.iOS),
-          home: Material(
-            child: Center(
-              child: TextField(
-                controller: controller,
-              ),
-            ),
-          ),
-        ),
-      );
-
-      final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
-
-      final TestGesture gesture =
-          await tester.startGesture(textfieldStart + const Offset(50.0, 5.0));
-      await tester.pump(const Duration(milliseconds: 500));
-
-      // Long press on iOS shows collapsed selection cursor.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 3, affinity: TextAffinity.downstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.moveBy(const Offset(50, 0));
-      await tester.pump();
-
-      // The selection position is now moved with the drag.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 6, affinity: TextAffinity.downstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.moveBy(const Offset(50, 0));
-      await tester.pump();
-
-      // The selection position is now moved with the drag.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 9, affinity: TextAffinity.downstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.up();
-      await tester.pump();
-
-      // The selection isn't affected by the gesture lift.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 9, affinity: TextAffinity.downstream),
-      );
-      // The toolbar now shows up.
-      expect(find.byType(CupertinoButton), findsNWidgets(2));
-    },
-  );
-
-  testWidgets('long press drag can edge scroll (iOS)', (WidgetTester tester) async {
-    final TextEditingController controller = TextEditingController(
-        text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges',
-      );
-      await tester.pumpWidget(
-        MaterialApp(
-          theme: ThemeData(platform: TargetPlatform.iOS),
-          home: Material(
-            child: Center(
-              child: TextField(
-                controller: controller,
-                maxLines: 1,
-              ),
-            ),
-          ),
-        ),
-      );
-
-      final RenderEditable renderEditable = findRenderEditable(tester);
-
-      List<TextSelectionPoint> lastCharEndpoint = renderEditable.getEndpointsForSelection(
-        const TextSelection.collapsed(offset: 66), // Last character's position.
-      );
-
-      expect(lastCharEndpoint.length, 1);
-      // Just testing the test and making sure that the last character is off
-      // the right side of the screen.
-      expect(lastCharEndpoint[0].point.dx, 1056);
-
-      final Offset textfieldStart = tester.getTopLeft(find.byType(TextField));
-
-      final TestGesture gesture =
-          await tester.startGesture(textfieldStart + const Offset(300, 5));
-      await tester.pump(const Duration(milliseconds: 500));
-
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 19, affinity: TextAffinity.upstream),
-      );
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.moveBy(const Offset(600, 0));
-      // To the edge of the screen basically.
-      await tester.pump();
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 56, affinity: TextAffinity.downstream),
-      );
-      // Keep moving out.
-      await gesture.moveBy(const Offset(1, 0));
-      await tester.pump();
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 62, affinity: TextAffinity.downstream),
-      );
-      await gesture.moveBy(const Offset(1, 0));
-      await tester.pump();
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
-      ); // We're at the edge now.
-      expect(find.byType(CupertinoButton), findsNothing);
-
-      await gesture.up();
-      await tester.pump();
-
-      // The selection isn't affected by the gesture lift.
-      expect(
-        controller.selection,
-        const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
-      );
-      // The toolbar now shows up.
-      expect(find.byType(CupertinoButton), findsNWidgets(2));
-
-      lastCharEndpoint = renderEditable.getEndpointsForSelection(
-        const TextSelection.collapsed(offset: 66), // Last character's position.
-      );
-
-      expect(lastCharEndpoint.length, 1);
-      // The last character is now on screen.
-      expect(lastCharEndpoint[0].point.dx, moreOrLessEquals(798.6666870117188));
-
-      final List<TextSelectionPoint> firstCharEndpoint = renderEditable.getEndpointsForSelection(
-        const TextSelection.collapsed(offset: 0), // First character's position.
-      );
-      expect(firstCharEndpoint.length, 1);
-      // The first character is now offscreen to the left.
-      expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-257.33331298828125));
-  });
-
-  testWidgets(
     'long tap after a double tap select is not affected (iOS)',
     (WidgetTester tester) async {
       final TextEditingController controller = TextEditingController(
diff --git a/packages/flutter/test/widgets/gesture_detector_test.dart b/packages/flutter/test/widgets/gesture_detector_test.dart
index fc94a91..922126b 100644
--- a/packages/flutter/test/widgets/gesture_detector_test.dart
+++ b/packages/flutter/test/widgets/gesture_detector_test.dart
@@ -486,19 +486,4 @@
     expect(horizontalDragStart, 1);
     expect(forcePressStart, 0);
   });
-
-  testWidgets('Cannot set both a long press and a long press drag callback', (WidgetTester tester) async {
-    try {
-      GestureDetector(
-        onLongPress: () {},
-        onLongPressDragUpdate: (GestureLongPressDragUpdateDetails details) {},
-        child: Container(
-          color: const Color(0xFF00FF00),
-        ),
-      );
-      throw 'setting long press and long press drag should throw';
-    } on FlutterError catch (_) {
-      // Should throw.
-    }
-  });
 }
diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart
index 937d5d9..084c1ba 100644
--- a/packages/flutter/test/widgets/text_selection_test.dart
+++ b/packages/flutter/test/widgets/text_selection_test.dart
@@ -9,7 +9,7 @@
   int tapCount;
   int singleTapUpCount;
   int singleTapCancelCount;
-  int singleLongTapStartCount;
+  int singleLongTapDownCount;
   int doubleTapDownCount;
   int forcePressStartCount;
   int forcePressEndCount;
@@ -17,7 +17,7 @@
   void _handleTapDown(TapDownDetails details) { tapCount++; }
   void _handleSingleTapUp(TapUpDetails details) { singleTapUpCount++; }
   void _handleSingleTapCancel() { singleTapCancelCount++; }
-  void _handleSingleLongTapStart(GestureLongPressDragStartDetails details) { singleLongTapStartCount++; }
+  void _handleSingleLongTapDown() { singleLongTapDownCount++; }
   void _handleDoubleTapDown(TapDownDetails details) { doubleTapDownCount++; }
   void _handleForcePressStart(ForcePressDetails details) { forcePressStartCount++; }
   void _handleForcePressEnd(ForcePressDetails details) { forcePressEndCount++; }
@@ -26,7 +26,7 @@
     tapCount = 0;
     singleTapUpCount = 0;
     singleTapCancelCount = 0;
-    singleLongTapStartCount = 0;
+    singleLongTapDownCount = 0;
     doubleTapDownCount = 0;
     forcePressStartCount = 0;
     forcePressEndCount = 0;
@@ -39,7 +39,7 @@
         onTapDown: _handleTapDown,
         onSingleTapUp: _handleSingleTapUp,
         onSingleTapCancel: _handleSingleTapCancel,
-        onSingleLongTapStart: _handleSingleLongTapStart,
+        onSingleLongTapDown: _handleSingleLongTapDown,
         onDoubleTapDown: _handleDoubleTapDown,
         onForcePressStart: _handleForcePressStart,
         onForcePressEnd: _handleForcePressEnd,
@@ -106,7 +106,7 @@
     expect(singleTapCancelCount, 0);
     expect(doubleTapDownCount, 1);
     // The double tap down hold supersedes the single tap down.
-    expect(singleLongTapStartCount, 0);
+    expect(singleLongTapDownCount, 0);
 
     await gesture.up();
     // Nothing else happens on up.
@@ -114,7 +114,7 @@
     expect(tapCount, 2);
     expect(singleTapCancelCount, 0);
     expect(doubleTapDownCount, 1);
-    expect(singleLongTapStartCount, 0);
+    expect(singleLongTapDownCount, 0);
   });
 
   testWidgets('a very quick swipe is just a canceled tap', (WidgetTester tester) async {
@@ -127,7 +127,7 @@
     expect(tapCount, 0);
     expect(singleTapCancelCount, 1);
     expect(doubleTapDownCount, 0);
-    expect(singleLongTapStartCount, 0);
+    expect(singleLongTapDownCount, 0);
 
     await gesture.up();
     // Nothing else happens on up.
@@ -135,7 +135,7 @@
     expect(tapCount, 0);
     expect(singleTapCancelCount, 1);
     expect(doubleTapDownCount, 0);
-    expect(singleLongTapStartCount, 0);
+    expect(singleLongTapDownCount, 0);
   });
 
   testWidgets('a slower swipe has a tap down and a canceled tap', (WidgetTester tester) async {
@@ -148,7 +148,7 @@
     expect(tapCount, 1);
     expect(singleTapCancelCount, 1);
     expect(doubleTapDownCount, 0);
-    expect(singleLongTapStartCount, 0);
+    expect(singleLongTapDownCount, 0);
   });
 
   testWidgets('a force press intiates a force press', (WidgetTester tester) async {