Revert "Support Scribble Handwriting" (#96615)

diff --git a/AUTHORS b/AUTHORS
index f79d1cc..c6f1a90 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -89,4 +89,3 @@
 Kai Yu <yk3372@gmail.com>
 Denis Grafov <grafov.denis@gmail.com>
 TheOneWithTheBraid <the-one@with-the-braid.cf>
-Twin Sun, LLC <google-contrib@twinsunsolutions.com>
diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart
index a1e37a9..c2efc04 100644
--- a/packages/flutter/lib/src/cupertino/text_field.dart
+++ b/packages/flutter/lib/src/cupertino/text_field.dart
@@ -297,7 +297,6 @@
     this.autofillHints = const <String>[],
     this.clipBehavior = Clip.hardEdge,
     this.restorationId,
-    this.scribbleEnabled = true,
     this.enableIMEPersonalizedLearning = true,
   }) : assert(textAlign != null),
        assert(readOnly != null),
@@ -455,7 +454,6 @@
     this.autofillHints = const <String>[],
     this.clipBehavior = Clip.hardEdge,
     this.restorationId,
-    this.scribbleEnabled = true,
     this.enableIMEPersonalizedLearning = true,
   }) : assert(textAlign != null),
        assert(readOnly != null),
@@ -800,9 +798,6 @@
   /// {@macro flutter.material.textfield.restorationId}
   final String? restorationId;
 
-  /// {@macro flutter.widgets.editableText.scribbleEnabled}
-  final bool scribbleEnabled;
-
   /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning}
   final bool enableIMEPersonalizedLearning;
 
@@ -848,7 +843,6 @@
     properties.add(DiagnosticsProperty<TextAlignVertical>('textAlignVertical', textAlignVertical, defaultValue: null));
     properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
     properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge));
-    properties.add(DiagnosticsProperty<bool>('scribbleEnabled', scribbleEnabled, defaultValue: true));
     properties.add(DiagnosticsProperty<bool>('enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true));
   }
 }
@@ -969,9 +963,6 @@
     if (cause == SelectionChangedCause.keyboard)
       return false;
 
-    if (cause == SelectionChangedCause.scribble)
-      return true;
-
     if (_effectiveController.text.isNotEmpty)
       return true;
 
@@ -1301,7 +1292,6 @@
             autofillClient: this,
             clipBehavior: widget.clipBehavior,
             restorationId: 'editable',
-            scribbleEnabled: widget.scribbleEnabled,
             enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning,
           ),
         ),
diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart
index 076f084..0fc2a87 100644
--- a/packages/flutter/lib/src/material/text_field.dart
+++ b/packages/flutter/lib/src/material/text_field.dart
@@ -329,7 +329,6 @@
     this.autofillHints = const <String>[],
     this.clipBehavior = Clip.hardEdge,
     this.restorationId,
-    this.scribbleEnabled = true,
     this.enableIMEPersonalizedLearning = true,
   }) : assert(textAlign != null),
        assert(readOnly != null),
@@ -769,9 +768,6 @@
   /// {@endtemplate}
   final String? restorationId;
 
-  /// {@macro flutter.widgets.editableText.scribbleEnabled}
-  final bool scribbleEnabled;
-
   /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning}
   final bool enableIMEPersonalizedLearning;
 
@@ -816,7 +812,6 @@
     properties.add(DiagnosticsProperty<ScrollController>('scrollController', scrollController, defaultValue: null));
     properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
     properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge));
-    properties.add(DiagnosticsProperty<bool>('scribbleEnabled', scribbleEnabled, defaultValue: true));
     properties.add(DiagnosticsProperty<bool>('enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true));
   }
 }
@@ -1034,7 +1029,7 @@
     if (!_isEnabled)
       return false;
 
-    if (cause == SelectionChangedCause.longPress || cause == SelectionChangedCause.scribble)
+    if (cause == SelectionChangedCause.longPress)
       return true;
 
     if (_effectiveController.text.isNotEmpty)
@@ -1278,7 +1273,6 @@
           autocorrectionTextRectColor: autocorrectionTextRectColor,
           clipBehavior: widget.clipBehavior,
           restorationId: 'editable',
-          scribbleEnabled: widget.scribbleEnabled,
           enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning,
         ),
       ),
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index 5f8cebf..f8113e3 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -1265,16 +1265,6 @@
   // [assembleSemanticsNode] invocations.
   Queue<SemanticsNode>? _cachedChildNodes;
 
-  /// Returns a list of rects that bound the given selection.
-  ///
-  /// See [TextPainter.getBoxesForSelection] for more details.
-  List<Rect> getBoxesForSelection(TextSelection selection) {
-    _computeTextMetricsIfNeeded();
-    return _textPainter.getBoxesForSelection(selection)
-                       .map((TextBox textBox) => textBox.toRect().shift(_paintOffset))
-                       .toList();
-  }
-
   @override
   void describeSemanticsConfiguration(SemanticsConfiguration config) {
     super.describeSemanticsConfiguration(config);
diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart
index 7bf468a..aaddbf5 100644
--- a/packages/flutter/lib/src/services/text_input.dart
+++ b/packages/flutter/lib/src/services/text_input.dart
@@ -955,9 +955,6 @@
   /// The user used the mouse to change the selection by dragging over a piece
   /// of text.
   drag,
-
-  /// The user used iPadOS 14+ Scribble to change the selection.
-  scribble,
 }
 
 /// A mixin for manipulating the selection, provided for toolbar or shortcut
@@ -1108,76 +1105,6 @@
   ///
   /// [TextInputClient] should cleanup its connection and finalize editing.
   void connectionClosed();
-
-  /// Requests that the client show the editing toolbar, for example when the
-  /// platform changes the selection through a non-flutter method such as
-  /// scribble.
-  void showToolbar() {}
-
-  /// Requests that the client add a text placeholder to reserve visual space
-  /// in the text.
-  ///
-  /// For example, this is called when responding to UIKit requesting
-  /// a text placeholder be added at the current selection, such as when
-  /// requesting additional writing space with iPadOS14 Scribble.
-  void insertTextPlaceholder(Size size) {}
-
-  /// Requests that the client remove the text placeholder.
-  void removeTextPlaceholder() {}
-}
-
-/// An interface to receive focus from the engine.
-///
-/// This is currently only used to handle UIIndirectScribbleInteraction.
-abstract class ScribbleClient {
-  /// A unique identifier for this element.
-  String get elementIdentifier;
-
-  /// Called by the engine when the [ScribbleClient] should receive focus.
-  ///
-  /// For example, this method is called during a UIIndirectScribbleInteraction.
-  void onScribbleFocus(Offset offset);
-
-  /// Tests whether the [ScribbleClient] overlaps the given rectangle bounds.
-  bool isInScribbleRect(Rect rect);
-
-  /// The current bounds of the [ScribbleClient].
-  Rect get bounds;
-}
-
-/// Represents a selection rect for a character and it's position in the text.
-///
-/// This is used to report the current text selection rect and position data
-/// to the engine for Scribble support on iPadOS 14.
-@immutable
-class SelectionRect {
-  /// Constructor for creating a [SelectionRect] from a text [position] and
-  /// [bounds].
-  const SelectionRect({required this.position, required this.bounds});
-
-  /// The position of this selection rect within the text String.
-  final int position;
-
-  /// The rectangle representing the bounds of this selection rect within the
-  /// currently focused [RenderEditable]'s coordinate space.
-  final Rect bounds;
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other))
-      return true;
-    if (runtimeType != other.runtimeType)
-      return false;
-    return other is SelectionRect
-        && other.position == position
-        && other.bounds   == bounds;
-  }
-
-  @override
-  int get hashCode => hashValues(position, bounds);
-
-  @override
-  String toString() => 'SelectionRect($position, $bounds)';
 }
 
 /// An interface to receive granular information from [TextInput].
@@ -1227,7 +1154,6 @@
   Matrix4? _cachedTransform;
   Rect? _cachedRect;
   Rect? _cachedCaretRect;
-  List<SelectionRect> _cachedSelectionRects = <SelectionRect>[];
 
   static int _nextId = 1;
   final int _id;
@@ -1250,12 +1176,6 @@
   /// Whether this connection is currently interacting with the text input control.
   bool get attached => TextInput._instance._currentConnection == this;
 
-  /// Whether there is currently a Scribble interaction in progress.
-  ///
-  /// This is used to make sure selection handles are shown when UIKit changes
-  /// the selection during a Scribble interaction.
-  bool get scribbleInProgress => TextInput._instance.scribbleInProgress;
-
   /// Requests that the text input control become visible.
   void show() {
     assert(attached);
@@ -1354,19 +1274,6 @@
     );
   }
 
-  /// Send the bounding boxes of the current selected glyphs in the client to
-  /// the platform's text input plugin.
-  ///
-  /// These are used by the engine during a UIDirectScribbleInteraction.
-  void setSelectionRects(List<SelectionRect> selectionRects) {
-    if (!listEquals(_cachedSelectionRects, selectionRects)) {
-      _cachedSelectionRects = selectionRects;
-      TextInput._instance._setSelectionRects(selectionRects.map((SelectionRect rect) {
-        return <num>[rect.bounds.left, rect.bounds.top, rect.bounds.width, rect.bounds.height, rect.position];
-      }).toList());
-    }
-  }
-
   /// Send text styling information.
   ///
   /// This information is used by the Flutter Web Engine to change the style
@@ -1628,43 +1535,10 @@
   TextInputConnection? _currentConnection;
   late TextInputConfiguration _currentConfiguration;
 
-  final Map<String, ScribbleClient> _scribbleClients = <String, ScribbleClient>{};
-  bool _scribbleInProgress = false;
-
-  /// Used for testing within the Flutter SDK to get the currently registered [ScribbleClient] list.
-  @visibleForTesting
-  static Map<String, ScribbleClient> get scribbleClients => TextInput._instance._scribbleClients;
-
-  /// Returns true if a scribble interaction is currently happening.
-  bool get scribbleInProgress => _scribbleInProgress;
-
   Future<dynamic> _handleTextInputInvocation(MethodCall methodCall) async {
-    final String method = methodCall.method;
-    if (method == 'TextInputClient.focusElement') {
-      final List<dynamic> args = methodCall.arguments as List<dynamic>;
-      _scribbleClients[args[0]]?.onScribbleFocus(Offset((args[1] as num).toDouble(), (args[2] as num).toDouble()));
-      return;
-    } else if (method == 'TextInputClient.requestElementsInRect') {
-      final List<double> args = (methodCall.arguments as List<dynamic>).cast<num>().map<double>((num value) => value.toDouble()).toList();
-      return _scribbleClients.keys.where((String elementIdentifier) {
-        final Rect rect = Rect.fromLTWH(args[0], args[1], args[2], args[3]);
-        if (!(_scribbleClients[elementIdentifier]?.isInScribbleRect(rect) ?? false))
-          return false;
-        final Rect bounds = _scribbleClients[elementIdentifier]?.bounds ?? Rect.zero;
-        return !(bounds == Rect.zero || bounds.hasNaN || bounds.isInfinite);
-      }).map((String elementIdentifier) {
-        final Rect bounds = _scribbleClients[elementIdentifier]!.bounds;
-        return <dynamic>[elementIdentifier, ...<dynamic>[bounds.left, bounds.top, bounds.width, bounds.height]];
-      }).toList();
-    } else if (method == 'TextInputClient.scribbleInteractionBegan') {
-      _scribbleInProgress = true;
-      return;
-    } else if (method == 'TextInputClient.scribbleInteractionFinished') {
-      _scribbleInProgress = false;
-      return;
-    }
     if (_currentConnection == null)
       return;
+    final String method = methodCall.method;
 
     // The requestExistingInputState request needs to be handled regardless of
     // the client ID, as long as we have a _currentConnection.
@@ -1756,15 +1630,6 @@
       case 'TextInputClient.showAutocorrectionPromptRect':
         _currentConnection!._client.showAutocorrectionPromptRect(args[1] as int, args[2] as int);
         break;
-      case 'TextInputClient.showToolbar':
-        _currentConnection!._client.showToolbar();
-        break;
-      case 'TextInputClient.insertTextPlaceholder':
-        _currentConnection!._client.insertTextPlaceholder(Size((args[1] as num).toDouble(), (args[2] as num).toDouble()));
-        break;
-      case 'TextInputClient.removeTextPlaceholder':
-        _currentConnection!._client.removeTextPlaceholder();
-        break;
       default:
         throw MissingPluginException();
     }
@@ -1838,13 +1703,6 @@
     );
   }
 
-  void _setSelectionRects(List<List<num>> args) {
-    _channel.invokeMethod<void>(
-      'TextInput.setSelectionRects',
-      args,
-    );
-  }
-
   void _setStyle(Map<String, dynamic> args) {
     _channel.invokeMethod<void>(
       'TextInput.setStyle',
@@ -1907,18 +1765,4 @@
       shouldSave,
     );
   }
-
-  /// Registers a [ScribbleClient] with [elementIdentifier] that can be focused
-  /// by the engine.
-  ///
-  /// For example, the registered [ScribbleClient] list is used to respond to
-  /// UIIndirectScribbleInteraction on an iPad.
-  static void registerScribbleElement(String elementIdentifier, ScribbleClient scribbleClient) {
-    TextInput._instance._scribbleClients[elementIdentifier] = scribbleClient;
-  }
-
-  /// Unregisters a [ScribbleClient] with [elementIdentifier].
-  static void unregisterScribbleElement(String elementIdentifier) {
-    TextInput._instance._scribbleClients.remove(elementIdentifier);
-  }
 }
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index fe13c45..67f5674 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -6,7 +6,7 @@
 import 'dart:math' as math;
 import 'dart:ui' as ui hide TextStyle;
 
-import 'package:characters/characters.dart' show CharacterRange, StringCharacters;
+import 'package:characters/characters.dart' show CharacterRange;
 import 'package:flutter/foundation.dart';
 import 'package:flutter/gestures.dart' show DragStartBehavior;
 import 'package:flutter/rendering.dart';
@@ -58,10 +58,6 @@
 // is shown in an obscured text field.
 const int _kObscureShowLatestCharCursorTicks = 3;
 
-// The minimum width of an iPad screen. The smallest iPad is currently the
-// iPad Mini 6th Gen according to ios-resolution.com.
-const double _kIPadWidth = 1488.0;
-
 /// A controller for an editable text field.
 ///
 /// Whenever the user modifies a text field with an associated
@@ -527,7 +523,6 @@
     this.clipBehavior = Clip.hardEdge,
     this.restorationId,
     this.scrollBehavior,
-    this.scribbleEnabled = true,
     this.enableIMEPersonalizedLearning = true,
   }) : assert(controller != null),
        assert(focusNode != null),
@@ -1215,15 +1210,6 @@
   /// [scrollPhysics].
   final ScrollPhysics? scrollPhysics;
 
-  /// {@template flutter.widgets.editableText.scribbleEnabled}
-  /// Whether iOS 14 Scribble features are enabled for this widget.
-  ///
-  /// Only available on iPads.
-  ///
-  /// Defaults to true.
-  /// {@endtemplate}
-  final bool scribbleEnabled;
-
   /// {@template flutter.widgets.editableText.selectionEnabled}
   /// Same as [enableInteractiveSelection].
   ///
@@ -1524,7 +1510,6 @@
     properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
     properties.add(DiagnosticsProperty<Iterable<String>>('autofillHints', autofillHints, defaultValue: null));
     properties.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
-    properties.add(DiagnosticsProperty<bool>('scribbleEnabled', scribbleEnabled, defaultValue: true));
     properties.add(DiagnosticsProperty<bool>('enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true));
   }
 }
@@ -1888,7 +1873,7 @@
 
     if (value.text == _value.text && value.composing == _value.composing) {
       // `selection` is the only change.
-      _handleSelectionChanged(value.selection, (_textInputConnection?.scribbleInProgress ?? false) ? SelectionChangedCause.scribble : SelectionChangedCause.keyboard);
+      _handleSelectionChanged(value.selection, SelectionChangedCause.keyboard);
     } else {
       hideToolbar();
       _currentPromptRectRange = null;
@@ -2671,11 +2656,6 @@
         // Place cursor at the end if the selection is invalid when we receive focus.
         _handleSelectionChanged(TextSelection.collapsed(offset: _value.text.length), null);
       }
-
-      _cachedText = '';
-      _cachedFirstRect = null;
-      _cachedSize = Size.zero;
-      _cachedPlaceholder = -1;
     } else {
       WidgetsBinding.instance!.removeObserver(this);
       setState(() { _currentPromptRectRange = null; });
@@ -2683,78 +2663,13 @@
     updateKeepAlive();
   }
 
-  String _cachedText = '';
-  Rect? _cachedFirstRect;
-  Size _cachedSize = Size.zero;
-  int _cachedPlaceholder = -1;
-  TextStyle? _cachedTextStyle;
-
-  void _updateSelectionRects({bool force = false}) {
-    if (!widget.scribbleEnabled)
-      return;
-    if (defaultTargetPlatform != TargetPlatform.iOS)
-      return;
-    // This is to avoid sending selection rects on non-iPad devices.
-    if (WidgetsBinding.instance!.window.physicalSize.shortestSide < _kIPadWidth)
-      return;
-
-    final String text = renderEditable.text?.toPlainText(includeSemanticsLabels: false, includePlaceholders: false) ?? '';
-    final List<Rect> firstSelectionBoxes = renderEditable.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 1));
-    final Rect? firstRect = firstSelectionBoxes.isNotEmpty ? firstSelectionBoxes.first : null;
-    final ScrollDirection scrollDirection = _scrollController.position.userScrollDirection;
-    final Size size = renderEditable.size;
-    final bool textChanged = text != _cachedText;
-    final bool textStyleChanged = _cachedTextStyle != widget.style;
-    final bool firstRectChanged = _cachedFirstRect != firstRect;
-    final bool sizeChanged = _cachedSize != size;
-    final bool placeholderChanged = _cachedPlaceholder != _placeholderLocation;
-    if (scrollDirection == ScrollDirection.idle && (force || textChanged || textStyleChanged || firstRectChanged || sizeChanged || placeholderChanged)) {
-      _cachedText = text;
-      _cachedFirstRect = firstRect;
-      _cachedTextStyle = widget.style;
-      _cachedSize = size;
-      _cachedPlaceholder = _placeholderLocation;
-      bool belowRenderEditableBottom = false;
-      final List<SelectionRect> rects = List<SelectionRect?>.generate(
-        _cachedText.characters.length,
-        (int i) {
-          if (belowRenderEditableBottom)
-            return null;
-
-          final int offset = _cachedText.characters.getRange(0, i).string.length;
-          final SelectionRect selectionRect = SelectionRect(
-            bounds: renderEditable.getBoxesForSelection(TextSelection(baseOffset: offset, extentOffset: offset + _cachedText.characters.characterAt(i).string.length)).first,
-            position: offset,
-          );
-          if (renderEditable.paintBounds.bottom < selectionRect.bounds.top) {
-            belowRenderEditableBottom = true;
-            return null;
-          }
-          return selectionRect;
-        },
-      ).where((SelectionRect? selectionRect) {
-        if (selectionRect == null)
-          return false;
-        if (renderEditable.paintBounds.right < selectionRect.bounds.left || selectionRect.bounds.right < renderEditable.paintBounds.left)
-          return false;
-        if (renderEditable.paintBounds.bottom < selectionRect.bounds.top || selectionRect.bounds.bottom < renderEditable.paintBounds.top)
-          return false;
-        return true;
-      }).map<SelectionRect>((SelectionRect? selectionRect) => selectionRect!).toList();
-      _textInputConnection!.setSelectionRects(rects);
-    }
-  }
-
   void _updateSizeAndTransform() {
     if (_hasInputConnection) {
       final Size size = renderEditable.size;
       final Matrix4 transform = renderEditable.getTransformTo(null);
       _textInputConnection!.setEditableSizeAndTransform(size, transform);
-      _updateSelectionRects();
       SchedulerBinding.instance!
           .addPostFrameCallback((Duration _) => _updateSizeAndTransform());
-    } else if (_placeholderLocation != -1) {
-      removeTextPlaceholder();
     }
   }
 
@@ -2837,7 +2752,6 @@
   ///
   /// Returns `false` if a toolbar couldn't be shown, such as when the toolbar
   /// is already shown, or when no text selection currently exists.
-  @override
   bool showToolbar() {
     // Web is using native dom elements to enable clipboard functionality of the
     // toolbar: copy, paste, select, cut. It might also provide additional
@@ -2876,36 +2790,6 @@
     }
   }
 
-  // Tracks the location a [_ScribblePlaceholder] should be rendered in the
-  // text.
-  //
-  // A value of -1 indicates there should be no placeholder, otherwise the
-  // value should be between 0 and the length of the text, inclusive.
-  int _placeholderLocation = -1;
-
-  @override
-  void insertTextPlaceholder(Size size) {
-    if (!widget.scribbleEnabled)
-      return;
-
-    if (!widget.controller.selection.isValid)
-      return;
-
-    setState(() {
-      _placeholderLocation = _value.text.length - widget.controller.selection.end;
-    });
-  }
-
-  @override
-  void removeTextPlaceholder() {
-    if (!widget.scribbleEnabled)
-      return;
-
-    setState(() {
-      _placeholderLocation = -1;
-    });
-  }
-
   @override
   String get autofillId => 'EditableText-$hashCode';
 
@@ -3107,62 +2991,53 @@
                   onCopy: _semanticsOnCopy(controls),
                   onCut: _semanticsOnCut(controls),
                   onPaste: _semanticsOnPaste(controls),
-                  child: _ScribbleFocusable(
-                    focusNode: widget.focusNode,
-                    editableKey: _editableKey,
-                    enabled: widget.scribbleEnabled,
-                    updateSelectionRects: () {
-                      _openInputConnection();
-                      _updateSelectionRects(force: true);
-                    },
-                    child: _Editable(
-                      key: _editableKey,
-                      startHandleLayerLink: _startHandleLayerLink,
-                      endHandleLayerLink: _endHandleLayerLink,
-                      inlineSpan: buildTextSpan(),
-                      value: _value,
-                      cursorColor: _cursorColor,
-                      backgroundCursorColor: widget.backgroundCursorColor,
-                      showCursor: EditableText.debugDeterministicCursor
-                          ? ValueNotifier<bool>(widget.showCursor)
-                          : _cursorVisibilityNotifier,
-                      forceLine: widget.forceLine,
-                      readOnly: widget.readOnly,
-                      hasFocus: _hasFocus,
-                      maxLines: widget.maxLines,
-                      minLines: widget.minLines,
-                      expands: widget.expands,
-                      strutStyle: widget.strutStyle,
-                      selectionColor: widget.selectionColor,
-                      textScaleFactor: widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
-                      textAlign: widget.textAlign,
-                      textDirection: _textDirection,
-                      locale: widget.locale,
-                      textHeightBehavior: widget.textHeightBehavior ?? DefaultTextHeightBehavior.of(context),
-                      textWidthBasis: widget.textWidthBasis,
-                      obscuringCharacter: widget.obscuringCharacter,
-                      obscureText: widget.obscureText,
-                      autocorrect: widget.autocorrect,
-                      smartDashesType: widget.smartDashesType,
-                      smartQuotesType: widget.smartQuotesType,
-                      enableSuggestions: widget.enableSuggestions,
-                      offset: offset,
-                      onCaretChanged: _handleCaretChanged,
-                      rendererIgnoresPointer: widget.rendererIgnoresPointer,
-                      cursorWidth: widget.cursorWidth,
-                      cursorHeight: widget.cursorHeight,
-                      cursorRadius: widget.cursorRadius,
-                      cursorOffset: widget.cursorOffset ?? Offset.zero,
-                      selectionHeightStyle: widget.selectionHeightStyle,
-                      selectionWidthStyle: widget.selectionWidthStyle,
-                      paintCursorAboveText: widget.paintCursorAboveText,
-                      enableInteractiveSelection: widget.enableInteractiveSelection,
-                      textSelectionDelegate: this,
-                      devicePixelRatio: _devicePixelRatio,
-                      promptRectRange: _currentPromptRectRange,
-                      promptRectColor: widget.autocorrectionTextRectColor,
-                      clipBehavior: widget.clipBehavior,
-                    ),
+                  child: _Editable(
+                    key: _editableKey,
+                    startHandleLayerLink: _startHandleLayerLink,
+                    endHandleLayerLink: _endHandleLayerLink,
+                    inlineSpan: buildTextSpan(),
+                    value: _value,
+                    cursorColor: _cursorColor,
+                    backgroundCursorColor: widget.backgroundCursorColor,
+                    showCursor: EditableText.debugDeterministicCursor
+                        ? ValueNotifier<bool>(widget.showCursor)
+                        : _cursorVisibilityNotifier,
+                    forceLine: widget.forceLine,
+                    readOnly: widget.readOnly,
+                    hasFocus: _hasFocus,
+                    maxLines: widget.maxLines,
+                    minLines: widget.minLines,
+                    expands: widget.expands,
+                    strutStyle: widget.strutStyle,
+                    selectionColor: widget.selectionColor,
+                    textScaleFactor: widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
+                    textAlign: widget.textAlign,
+                    textDirection: _textDirection,
+                    locale: widget.locale,
+                    textHeightBehavior: widget.textHeightBehavior ?? DefaultTextHeightBehavior.of(context),
+                    textWidthBasis: widget.textWidthBasis,
+                    obscuringCharacter: widget.obscuringCharacter,
+                    obscureText: widget.obscureText,
+                    autocorrect: widget.autocorrect,
+                    smartDashesType: widget.smartDashesType,
+                    smartQuotesType: widget.smartQuotesType,
+                    enableSuggestions: widget.enableSuggestions,
+                    offset: offset,
+                    onCaretChanged: _handleCaretChanged,
+                    rendererIgnoresPointer: widget.rendererIgnoresPointer,
+                    cursorWidth: widget.cursorWidth,
+                    cursorHeight: widget.cursorHeight,
+                    cursorRadius: widget.cursorRadius,
+                    cursorOffset: widget.cursorOffset ?? Offset.zero,
+                    selectionHeightStyle: widget.selectionHeightStyle,
+                    selectionWidthStyle: widget.selectionWidthStyle,
+                    paintCursorAboveText: widget.paintCursorAboveText,
+                    enableInteractiveSelection: widget.enableInteractiveSelection,
+                    textSelectionDelegate: this,
+                    devicePixelRatio: _devicePixelRatio,
+                    promptRectRange: _currentPromptRectRange,
+                    promptRectColor: widget.autocorrectionTextRectColor,
+                    clipBehavior: widget.clipBehavior,
                   ),
                 ),
               );
@@ -3192,24 +3067,6 @@
       }
       return TextSpan(style: widget.style, text: text);
     }
-    if (_placeholderLocation >= 0 && _placeholderLocation <= _value.text.length) {
-      final List<_ScribblePlaceholder> placeholders = <_ScribblePlaceholder>[];
-      final int placeholderLocation = _value.text.length - _placeholderLocation;
-      if (_isMultiline) {
-        // The zero size placeholder here allows the line to break and keep the caret on the first line.
-        placeholders.add(const _ScribblePlaceholder(child: SizedBox(), size: Size.zero));
-        placeholders.add(_ScribblePlaceholder(child: const SizedBox(), size: Size(renderEditable.size.width, 0.0)));
-      } else {
-        placeholders.add(const _ScribblePlaceholder(child: SizedBox(), size: Size(100.0, 0.0)));
-      }
-      return TextSpan(style: widget.style, children: <InlineSpan>[
-          TextSpan(text: _value.text.substring(0, placeholderLocation)),
-          ...placeholders,
-          TextSpan(text: _value.text.substring(placeholderLocation)),
-        ],
-      );
-    }
-
     // Read only mode should not paint text composing.
     return widget.controller.buildTextSpan(
       context: context,
@@ -3414,142 +3271,6 @@
   }
 }
 
-class _ScribbleFocusable extends StatefulWidget {
-  const _ScribbleFocusable({
-    Key? key,
-    required this.child,
-    required this.focusNode,
-    required this.editableKey,
-    required this.updateSelectionRects,
-    required this.enabled,
-  }): super(key: key);
-
-  final Widget child;
-  final FocusNode focusNode;
-  final GlobalKey editableKey;
-  final VoidCallback updateSelectionRects;
-  final bool enabled;
-
-  @override
-  _ScribbleFocusableState createState() => _ScribbleFocusableState();
-}
-
-class _ScribbleFocusableState extends State<_ScribbleFocusable> implements ScribbleClient {
-  _ScribbleFocusableState(): _elementIdentifier = (_nextElementIdentifier++).toString();
-
-  @override
-  void initState() {
-    super.initState();
-    if (widget.enabled) {
-      TextInput.registerScribbleElement(elementIdentifier, this);
-    }
-  }
-
-  @override
-  void didUpdateWidget(_ScribbleFocusable oldWidget) {
-    super.didUpdateWidget(oldWidget);
-    if (!oldWidget.enabled && widget.enabled) {
-      TextInput.registerScribbleElement(elementIdentifier, this);
-    }
-
-    if (oldWidget.enabled && !widget.enabled) {
-      TextInput.unregisterScribbleElement(elementIdentifier);
-    }
-  }
-
-  @override
-  void dispose() {
-    TextInput.unregisterScribbleElement(elementIdentifier);
-    super.dispose();
-  }
-
-  RenderEditable? get renderEditable => widget.editableKey.currentContext?.findRenderObject() as RenderEditable?;
-
-  static int _nextElementIdentifier = 1;
-  final String _elementIdentifier;
-
-  @override
-  String get elementIdentifier => _elementIdentifier;
-
-  @override
-  void onScribbleFocus(Offset offset) {
-    widget.focusNode.requestFocus();
-    renderEditable?.selectPositionAt(from: offset, cause: SelectionChangedCause.scribble);
-    widget.updateSelectionRects();
-  }
-
-  @override
-  bool isInScribbleRect(Rect rect) {
-    final Rect _bounds = bounds;
-    if (renderEditable?.readOnly ?? false)
-      return false;
-    if (_bounds == Rect.zero)
-      return false;
-    if (!_bounds.overlaps(rect))
-      return false;
-    final Rect intersection = _bounds.intersect(rect);
-    final HitTestResult result = HitTestResult();
-    WidgetsBinding.instance?.hitTest(result, intersection.center);
-    return result.path.any((HitTestEntry entry) => entry.target == renderEditable);
-  }
-
-  @override
-  Rect get bounds {
-    final RenderBox? box = context.findRenderObject() as RenderBox?;
-    if (box == null || !mounted || !box.attached)
-      return Rect.zero;
-    final Matrix4 transform = box.getTransformTo(null);
-    return MatrixUtils.transformRect(transform, Rect.fromLTWH(0, 0, box.size.width, box.size.height));
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return widget.child;
-  }
-}
-
-class _ScribblePlaceholder extends WidgetSpan {
-  const _ScribblePlaceholder({
-    required Widget child,
-    ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
-    TextBaseline? baseline,
-    TextStyle? style,
-    required this.size,
-  }) : assert(child != null),
-       assert(baseline != null || !(
-         identical(alignment, ui.PlaceholderAlignment.aboveBaseline) ||
-         identical(alignment, ui.PlaceholderAlignment.belowBaseline) ||
-         identical(alignment, ui.PlaceholderAlignment.baseline)
-       )),
-       super(
-         alignment: alignment,
-         baseline: baseline,
-         style: style,
-         child: child,
-       );
-
-  /// The size of the span, used in place of adding a placeholder size to the [TextPainter].
-  final Size size;
-
-  @override
-  void build(ui.ParagraphBuilder builder, { double textScaleFactor = 1.0, List<PlaceholderDimensions>? dimensions }) {
-    assert(debugAssertIsValid());
-    final bool hasStyle = style != null;
-    if (hasStyle) {
-      builder.pushStyle(style!.getTextStyle(textScaleFactor: textScaleFactor));
-    }
-    builder.addPlaceholder(
-      size.width,
-      size.height,
-      alignment,
-      scale: textScaleFactor,
-    );
-    if (hasStyle) {
-      builder.pop();
-    }
-  }
-}
-
 /// An interface for retriving the logical text boundary (left-closed-right-open)
 /// at a given location in a document.
 ///
diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart
index 874aa11..6d66fd1 100644
--- a/packages/flutter/test/cupertino/text_field_test.dart
+++ b/packages/flutter/test/cupertino/text_field_test.dart
@@ -2368,7 +2368,7 @@
     );
 
     final RenderEditable renderEditable = tester.renderObject<RenderEditable>(
-      find.byElementPredicate((Element element) => element.renderObject is RenderEditable).last,
+      find.byElementPredicate((Element element) => element.renderObject is RenderEditable),
     );
 
     List<TextSelectionPoint> lastCharEndpoint = renderEditable.getEndpointsForSelection(
@@ -3166,7 +3166,7 @@
 
       expect(
         tester.renderObject<RenderEditable>(
-          find.byElementPredicate((Element element) => element.renderObject is RenderEditable).last,
+          find.byElementPredicate((Element element) => element.renderObject is RenderEditable),
         ).text!.style!.color,
         isSameColorAs(CupertinoColors.white),
       );
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 7464689..3fef737 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -9255,38 +9255,6 @@
     expect(right.opacity.value, equals(1.0));
   }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS,  TargetPlatform.macOS }));
 
-  testWidgets('iPad Scribble selection change shows selection handles', (WidgetTester tester) async {
-    const String testText = 'lorem ipsum';
-    final TextEditingController controller = TextEditingController(text: testText);
-
-    await tester.pumpWidget(
-      MaterialApp(
-        home: Material(
-          child: TextField(
-            controller: controller,
-          ),
-        ),
-      ),
-    );
-
-    await tester.showKeyboard(find.byType(EditableText));
-    await tester.testTextInput.startScribbleInteraction();
-    tester.testTextInput.updateEditingValue(const TextEditingValue(
-      text: testText,
-      selection: TextSelection(baseOffset: 2, extentOffset: 7),
-    ));
-    await tester.pumpAndSettle();
-
-    final List<FadeTransition> transitions =
-      find.byType(FadeTransition).evaluate().map((Element e) => e.widget).cast<FadeTransition>().toList();
-    expect(transitions.length, 2);
-    final FadeTransition left = transitions[0];
-    final FadeTransition right = transitions[1];
-
-    expect(left.opacity.value, equals(1.0));
-    expect(right.opacity.value, equals(1.0));
-  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
-
   testWidgets('Tap shows handles but not toolbar', (WidgetTester tester) async {
     final TextEditingController controller = TextEditingController(
       text: 'abc def ghi',
diff --git a/packages/flutter/test/services/autofill_test.dart b/packages/flutter/test/services/autofill_test.dart
index e463a08..11fdcd9 100644
--- a/packages/flutter/test/services/autofill_test.dart
+++ b/packages/flutter/test/services/autofill_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:ui';
-
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 
@@ -143,21 +141,6 @@
 
   @override
   void autofill(TextEditingValue newEditingValue) => updateEditingValue(newEditingValue);
-
-  @override
-  void showToolbar() {
-    latestMethodCall = 'showToolbar';
-  }
-
-  @override
-  void insertTextPlaceholder(Size size) {
-    latestMethodCall = 'insertTextPlaceholder';
-  }
-
-  @override
-  void removeTextPlaceholder() {
-    latestMethodCall = 'removeTextPlaceholder';
-  }
 }
 
 class FakeAutofillScope with AutofillScopeMixin implements AutofillScope {
diff --git a/packages/flutter/test/services/delta_text_input_test.dart b/packages/flutter/test/services/delta_text_input_test.dart
index eca5455..e87a665 100644
--- a/packages/flutter/test/services/delta_text_input_test.dart
+++ b/packages/flutter/test/services/delta_text_input_test.dart
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:convert' show jsonDecode;
-import 'dart:ui';
 
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -115,20 +114,5 @@
     latestMethodCall = 'showAutocorrectionPromptRect';
   }
 
-  @override
-  void insertTextPlaceholder(Size size) {
-    latestMethodCall = 'insertTextPlaceholder';
-  }
-
-  @override
-  void removeTextPlaceholder() {
-    latestMethodCall = 'removeTextPlaceholder';
-  }
-
-  @override
-  void showToolbar() {
-    latestMethodCall = 'showToolbar';
-  }
-
   TextInputConfiguration get configuration => const TextInputConfiguration(enableDeltaModel: true);
 }
diff --git a/packages/flutter/test/services/text_input_test.dart b/packages/flutter/test/services/text_input_test.dart
index 8591a04..09e5831 100644
--- a/packages/flutter/test/services/text_input_test.dart
+++ b/packages/flutter/test/services/text_input_test.dart
@@ -4,7 +4,6 @@
 
 
 import 'dart:convert' show jsonDecode;
-import 'dart:ui';
 
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -497,148 +496,6 @@
 
       expect(client.latestMethodCall, 'showAutocorrectionPromptRect');
     });
-
-    test('TextInputClient showToolbar method is called', () async {
-      // Assemble a TextInputConnection so we can verify its change in state.
-      final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty);
-      const TextInputConfiguration configuration = TextInputConfiguration();
-      TextInput.attach(client, configuration);
-
-      expect(client.latestMethodCall, isEmpty);
-
-      // Send showToolbar message.
-      final ByteData? messageBytes =
-          const JSONMessageCodec().encodeMessage(<String, dynamic>{
-        'args': <dynamic>[1, 0, 1],
-        'method': 'TextInputClient.showToolbar',
-      });
-      await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-        'flutter/textinput',
-        messageBytes,
-        (ByteData? _) {},
-      );
-
-      expect(client.latestMethodCall, 'showToolbar');
-    });
-  });
-
-  group('Scribble interactions', () {
-    tearDown(() {
-      TextInputConnection.debugResetId();
-    });
-
-    test('TextInputClient scribbleInteractionBegan and scribbleInteractionFinished', () async {
-      // Assemble a TextInputConnection so we can verify its change in state.
-      final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty);
-      const TextInputConfiguration configuration = TextInputConfiguration();
-      final TextInputConnection connection = TextInput.attach(client, configuration);
-
-      expect(connection.scribbleInProgress, false);
-
-      // Send scribbleInteractionBegan message.
-      ByteData? messageBytes =
-          const JSONMessageCodec().encodeMessage(<String, dynamic>{
-        'args': <dynamic>[1, 0, 1],
-        'method': 'TextInputClient.scribbleInteractionBegan',
-      });
-      await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-        'flutter/textinput',
-        messageBytes,
-        (ByteData? _) {},
-      );
-
-      expect(connection.scribbleInProgress, true);
-
-      // Send scribbleInteractionFinished message.
-      messageBytes =
-          const JSONMessageCodec().encodeMessage(<String, dynamic>{
-        'args': <dynamic>[1, 0, 1],
-        'method': 'TextInputClient.scribbleInteractionFinished',
-      });
-      await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-        'flutter/textinput',
-        messageBytes,
-        (ByteData? _) {},
-      );
-
-      expect(connection.scribbleInProgress, false);
-    });
-
-    test('TextInputClient focusElement', () async {
-      // Assemble a TextInputConnection so we can verify its change in state.
-      final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty);
-      const TextInputConfiguration configuration = TextInputConfiguration();
-      TextInput.attach(client, configuration);
-
-      final FakeScribbleElement targetElement = FakeScribbleElement(elementIdentifier: 'target');
-      TextInput.registerScribbleElement(targetElement.elementIdentifier, targetElement);
-      final FakeScribbleElement otherElement = FakeScribbleElement(elementIdentifier: 'other');
-      TextInput.registerScribbleElement(otherElement.elementIdentifier, otherElement);
-
-      expect(targetElement.latestMethodCall, isEmpty);
-      expect(otherElement.latestMethodCall, isEmpty);
-
-      // Send focusElement message.
-      final ByteData? messageBytes =
-          const JSONMessageCodec().encodeMessage(<String, dynamic>{
-        'args': <dynamic>[targetElement.elementIdentifier, 0.0, 0.0],
-        'method': 'TextInputClient.focusElement',
-      });
-      await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-        'flutter/textinput',
-        messageBytes,
-        (ByteData? _) {},
-      );
-
-      TextInput.unregisterScribbleElement(targetElement.elementIdentifier);
-      TextInput.unregisterScribbleElement(otherElement.elementIdentifier);
-
-      expect(targetElement.latestMethodCall, 'onScribbleFocus');
-      expect(otherElement.latestMethodCall, isEmpty);
-    });
-
-    test('TextInputClient requestElementsInRect', () async {
-      // Assemble a TextInputConnection so we can verify its change in state.
-      final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty);
-      const TextInputConfiguration configuration = TextInputConfiguration();
-      TextInput.attach(client, configuration);
-
-      final List<FakeScribbleElement> targetElements = <FakeScribbleElement>[
-        FakeScribbleElement(elementIdentifier: 'target1', bounds: const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)),
-        FakeScribbleElement(elementIdentifier: 'target2', bounds: const Rect.fromLTWH(0.0, 100.0, 100.0, 100.0)),
-      ];
-      final List<FakeScribbleElement> otherElements = <FakeScribbleElement>[
-        FakeScribbleElement(elementIdentifier: 'other1', bounds: const Rect.fromLTWH(100.0, 0.0, 100.0, 100.0)),
-        FakeScribbleElement(elementIdentifier: 'other2', bounds: const Rect.fromLTWH(100.0, 100.0, 100.0, 100.0)),
-      ];
-
-      void registerElements(FakeScribbleElement element) => TextInput.registerScribbleElement(element.elementIdentifier, element);
-      void unregisterElements(FakeScribbleElement element) => TextInput.unregisterScribbleElement(element.elementIdentifier);
-
-      <FakeScribbleElement>[...targetElements, ...otherElements].forEach(registerElements);
-
-      // Send requestElementsInRect message.
-      final ByteData? messageBytes =
-          const JSONMessageCodec().encodeMessage(<String, dynamic>{
-        'args': <dynamic>[0.0, 50.0, 50.0, 100.0],
-        'method': 'TextInputClient.requestElementsInRect',
-      });
-      ByteData? responseBytes;
-      await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-        'flutter/textinput',
-        messageBytes,
-        (ByteData? response) {
-          responseBytes = response;
-        },
-      );
-
-      <FakeScribbleElement>[...targetElements, ...otherElements].forEach(unregisterElements);
-
-      final List<List<dynamic>> responses = (const JSONMessageCodec().decodeMessage(responseBytes) as List<dynamic>).cast<List<dynamic>>();
-      expect(responses.first.length, 2);
-      expect(responses.first.first, containsAllInOrder(<dynamic>[targetElements.first.elementIdentifier, 0.0, 0.0, 100.0, 100.0]));
-      expect(responses.first.last, containsAllInOrder(<dynamic>[targetElements.last.elementIdentifier, 0.0, 100.0, 100.0, 100.0]));
-    });
   });
 
   test('TextEditingValue.isComposingRangeValid', () async {
@@ -710,20 +567,5 @@
     latestMethodCall = 'showAutocorrectionPromptRect';
   }
 
-  @override
-  void showToolbar() {
-    latestMethodCall = 'showToolbar';
-  }
-
   TextInputConfiguration get configuration => const TextInputConfiguration();
-
-  @override
-  void insertTextPlaceholder(Size size) {
-    latestMethodCall = 'insertTextPlaceholder';
-  }
-
-  @override
-  void removeTextPlaceholder() {
-    latestMethodCall = 'removeTextPlaceholder';
-  }
 }
diff --git a/packages/flutter/test/services/text_input_utils.dart b/packages/flutter/test/services/text_input_utils.dart
index 2598c09..e1e3ddf 100644
--- a/packages/flutter/test/services/text_input_utils.dart
+++ b/packages/flutter/test/services/text_input_utils.dart
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:convert' show utf8;
-import 'dart:ui';
 
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -65,29 +64,3 @@
     }
   }
 }
-
-class FakeScribbleElement implements ScribbleClient {
-  FakeScribbleElement({required String elementIdentifier, Rect bounds = Rect.zero})
-      : _elementIdentifier = elementIdentifier,
-        _bounds = bounds;
-
-  final String _elementIdentifier;
-  final Rect _bounds;
-  String latestMethodCall = '';
-
-  @override
-  Rect get bounds => _bounds;
-
-  @override
-  String get elementIdentifier => _elementIdentifier;
-
-  @override
-  bool isInScribbleRect(Rect rect) {
-    return _bounds.overlaps(rect);
-  }
-
-  @override
-  void onScribbleFocus(Offset offset) {
-    latestMethodCall = 'onScribbleFocus';
-  }
-}
diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart
index 0010656..d94fd00 100644
--- a/packages/flutter/test/widgets/editable_text_test.dart
+++ b/packages/flutter/test/widgets/editable_text_test.dart
@@ -1662,329 +1662,6 @@
     }
   });
 
-  testWidgets('Selection changes during Scribble interaction should have the scribble cause', (WidgetTester tester) async {
-    late SelectionChangedCause selectionCause;
-
-    final TextEditingController controller =
-        TextEditingController(text: 'Lorem ipsum dolor sit amet');
-
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-          onSelectionChanged: (TextSelection selection, SelectionChangedCause? cause) {
-            if (cause != null)
-              selectionCause = cause;
-          },
-        ),
-      ),
-    );
-
-    await tester.showKeyboard(find.byType(EditableText));
-
-    // A normal selection update from the framework has 'keyboard' as the cause.
-    tester.testTextInput.updateEditingValue(TextEditingValue(
-      text: controller.text,
-      selection: const TextSelection(baseOffset: 2, extentOffset: 3),
-    ));
-    await tester.pumpAndSettle();
-
-    expect(selectionCause, SelectionChangedCause.keyboard);
-
-    // A selection update during a scribble interaction has 'scribble' as the cause.
-    await tester.testTextInput.startScribbleInteraction();
-    tester.testTextInput.updateEditingValue(TextEditingValue(
-      text: controller.text,
-      selection: const TextSelection(baseOffset: 3, extentOffset: 4),
-    ));
-    await tester.pumpAndSettle();
-
-    expect(selectionCause, SelectionChangedCause.scribble);
-  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
-
-  testWidgets('Requests focus and changes the selection when onScribbleFocus is called', (WidgetTester tester) async {
-    final TextEditingController controller =
-        TextEditingController(text: 'Lorem ipsum dolor sit amet');
-    late SelectionChangedCause selectionCause;
-
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-          onSelectionChanged: (TextSelection selection, SelectionChangedCause? cause) {
-            if (cause != null)
-              selectionCause = cause;
-          },
-        ),
-      ),
-    );
-
-    await tester.testTextInput.scribbleFocusElement(TextInput.scribbleClients.keys.first, Offset.zero);
-
-    expect(focusNode.hasFocus, true);
-    expect(selectionCause, SelectionChangedCause.scribble);
-
-    // On web, we should rely on the browser's implementation of Scribble, so the selection changed cause
-    // will never be SelectionChangedCause.scribble.
-  }, skip: kIsWeb, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); // [intended]
-
-  testWidgets('Declares itself for Scribble interaction if the bounds overlap the scribble rect and the widget is touchable', (WidgetTester tester) async {
-    final TextEditingController controller =
-        TextEditingController(text: 'Lorem ipsum dolor sit amet');
-
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-        ),
-      ),
-    );
-
-    final List<dynamic> elementEntry = <dynamic>[TextInput.scribbleClients.keys.first, 0.0, 0.0, 800.0, 600.0];
-
-    List<List<dynamic>> elements = await tester.testTextInput.scribbleRequestElementsInRect(const Rect.fromLTWH(0, 0, 1, 1));
-    expect(elements.first, containsAll(elementEntry));
-
-    // Touch is outside the bounds of the widget.
-    elements = await tester.testTextInput.scribbleRequestElementsInRect(const Rect.fromLTWH(-1, -1, 1, 1));
-    expect(elements.length, 0);
-
-    // Widget is read only.
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          readOnly: true,
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-        ),
-      ),
-    );
-
-    elements = await tester.testTextInput.scribbleRequestElementsInRect(const Rect.fromLTWH(0, 0, 1, 1));
-    expect(elements.length, 0);
-
-    // Widget is not touchable.
-    await tester.pumpWidget(
-      MaterialApp(
-        home: Stack(children: <Widget>[
-            EditableText(
-              controller: controller,
-              backgroundCursorColor: Colors.grey,
-              focusNode: focusNode,
-              style: textStyle,
-              cursorColor: cursorColor,
-              selectionControls: materialTextSelectionControls,
-            ),
-            Positioned(
-              left: 0,
-              top: 0,
-              right: 0,
-              bottom: 0,
-              child: Container(color: Colors.black),
-            ),
-          ],
-        ),
-      ),
-    );
-
-    elements = await tester.testTextInput.scribbleRequestElementsInRect(const Rect.fromLTWH(0, 0, 1, 1));
-    expect(elements.length, 0);
-
-    // Widget has scribble disabled.
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-          scribbleEnabled: false,
-        ),
-      ),
-    );
-
-    elements = await tester.testTextInput.scribbleRequestElementsInRect(const Rect.fromLTWH(0, 0, 1, 1));
-    expect(elements.length, 0);
-
-
-    // On web, we should rely on the browser's implementation of Scribble, so the engine will
-    // never request the scribble elements.
-  }, skip: kIsWeb, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); // [intended]
-
-  testWidgets('single line Scribble fields can show a horizontal placeholder', (WidgetTester tester) async {
-    final TextEditingController controller =
-        TextEditingController(text: 'Lorem ipsum dolor sit amet');
-
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-        ),
-      ),
-    );
-
-    await tester.showKeyboard(find.byType(EditableText));
-
-    tester.testTextInput.updateEditingValue(TextEditingValue(
-      text: controller.text,
-      selection: const TextSelection(baseOffset: 5, extentOffset: 5),
-    ));
-    await tester.pumpAndSettle();
-
-    await tester.testTextInput.scribbleInsertPlaceholder();
-    await tester.pumpAndSettle();
-
-    TextSpan textSpan = findRenderEditable(tester).text! as TextSpan;
-    expect(textSpan.children!.length, 3);
-    expect((textSpan.children![0] as TextSpan).text, 'Lorem');
-    expect(textSpan.children![1] is WidgetSpan, true);
-    expect((textSpan.children![2] as TextSpan).text, ' ipsum dolor sit amet');
-
-    await tester.testTextInput.scribbleRemovePlaceholder();
-    await tester.pumpAndSettle();
-
-    textSpan = findRenderEditable(tester).text! as TextSpan;
-    expect(textSpan.children, null);
-    expect(textSpan.text, 'Lorem ipsum dolor sit amet');
-
-    // Widget has scribble disabled.
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-          scribbleEnabled: false,
-        ),
-      ),
-    );
-
-    await tester.showKeyboard(find.byType(EditableText));
-
-    tester.testTextInput.updateEditingValue(TextEditingValue(
-      text: controller.text,
-      selection: const TextSelection(baseOffset: 5, extentOffset: 5),
-    ));
-    await tester.pumpAndSettle();
-
-    await tester.testTextInput.scribbleInsertPlaceholder();
-    await tester.pumpAndSettle();
-
-    textSpan = findRenderEditable(tester).text! as TextSpan;
-    expect(textSpan.children, null);
-    expect(textSpan.text, 'Lorem ipsum dolor sit amet');
-
-    // On web, we should rely on the browser's implementation of Scribble, so the framework
-    // will not handle placeholders.
-  }, skip: kIsWeb, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); // [intended]
-
-  testWidgets('multiline Scribble fields can show a vertical placeholder', (WidgetTester tester) async {
-    final TextEditingController controller =
-        TextEditingController(text: 'Lorem ipsum dolor sit amet');
-
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-          maxLines: 2,
-        ),
-      ),
-    );
-
-    await tester.showKeyboard(find.byType(EditableText));
-
-    tester.testTextInput.updateEditingValue(TextEditingValue(
-      text: controller.text,
-      selection: const TextSelection(baseOffset: 5, extentOffset: 5),
-    ));
-    await tester.pumpAndSettle();
-
-    await tester.testTextInput.scribbleInsertPlaceholder();
-    await tester.pumpAndSettle();
-
-    TextSpan textSpan = findRenderEditable(tester).text! as TextSpan;
-    expect(textSpan.children!.length, 4);
-    expect((textSpan.children![0] as TextSpan).text, 'Lorem');
-    expect(textSpan.children![1] is WidgetSpan, true);
-    expect(textSpan.children![2] is WidgetSpan, true);
-    expect((textSpan.children![3] as TextSpan).text, ' ipsum dolor sit amet');
-
-    await tester.testTextInput.scribbleRemovePlaceholder();
-    await tester.pumpAndSettle();
-
-    textSpan = findRenderEditable(tester).text! as TextSpan;
-    expect(textSpan.children, null);
-    expect(textSpan.text, 'Lorem ipsum dolor sit amet');
-
-    // Widget has scribble disabled.
-    await tester.pumpWidget(
-      MaterialApp(
-        home: EditableText(
-          controller: controller,
-          backgroundCursorColor: Colors.grey,
-          focusNode: focusNode,
-          style: textStyle,
-          cursorColor: cursorColor,
-          selectionControls: materialTextSelectionControls,
-          maxLines: 2,
-          scribbleEnabled: false,
-        ),
-      ),
-    );
-
-    await tester.showKeyboard(find.byType(EditableText));
-
-    tester.testTextInput.updateEditingValue(TextEditingValue(
-      text: controller.text,
-      selection: const TextSelection(baseOffset: 5, extentOffset: 5),
-    ));
-    await tester.pumpAndSettle();
-
-    await tester.testTextInput.scribbleInsertPlaceholder();
-    await tester.pumpAndSettle();
-
-    textSpan = findRenderEditable(tester).text! as TextSpan;
-    expect(textSpan.children, null);
-    expect(textSpan.text, 'Lorem ipsum dolor sit amet');
-
-    // On web, we should rely on the browser's implementation of Scribble, so the framework
-    // will not handle placeholders.
-  }, skip: kIsWeb, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); // [intended]
-
   testWidgets('Sends "updateConfig" when read-only flag is flipped', (WidgetTester tester) async {
     bool readOnly = true;
     late StateSetter setState;
@@ -4017,85 +3694,6 @@
     );
   });
 
-  testWidgets('selection rects are sent when they change', (WidgetTester tester) async {
-    final List<MethodCall> log = <MethodCall>[];
-    SystemChannels.textInput.setMockMethodCallHandler((MethodCall methodCall) async {
-      log.add(methodCall);
-    });
-
-    final TextEditingController controller = TextEditingController();
-    controller.text = 'Text1';
-
-    await tester.pumpWidget(
-      MediaQuery(
-        data: const MediaQueryData(),
-        child: Directionality(
-          textDirection: TextDirection.ltr,
-          child: Column(
-            crossAxisAlignment: CrossAxisAlignment.start,
-            children:  <Widget>[
-              EditableText(
-                key: ValueKey<String>(controller.text),
-                controller: controller,
-                focusNode: FocusNode(),
-                style: Typography.material2018().black.subtitle1!,
-                cursorColor: Colors.blue,
-                backgroundCursorColor: Colors.grey,
-              ),
-            ],
-          ),
-        ),
-      ),
-    );
-    await tester.showKeyboard(find.byKey(ValueKey<String>(controller.text)));
-
-    // There should be a new platform message updating the selection rects.
-    final MethodCall methodCall = log.firstWhere((MethodCall m) => m.method == 'TextInput.setSelectionRects');
-    expect(methodCall.method, 'TextInput.setSelectionRects');
-    expect((methodCall.arguments as List<dynamic>).length, 5);
-
-    // On web, we should rely on the browser's implementation of Scribble, so we will not send selection rects.
-  }, skip: kIsWeb, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); // [intended]
-
-  testWidgets('selection rects are not sent if scribbleEnabled is false', (WidgetTester tester) async {
-    final List<MethodCall> log = <MethodCall>[];
-    SystemChannels.textInput.setMockMethodCallHandler((MethodCall methodCall) async {
-      log.add(methodCall);
-    });
-
-    final TextEditingController controller = TextEditingController();
-    controller.text = 'Text1';
-
-    await tester.pumpWidget(
-      MediaQuery(
-        data: const MediaQueryData(),
-        child: Directionality(
-          textDirection: TextDirection.ltr,
-          child: Column(
-            crossAxisAlignment: CrossAxisAlignment.start,
-            children:  <Widget>[
-              EditableText(
-                key: ValueKey<String>(controller.text),
-                controller: controller,
-                focusNode: FocusNode(),
-                style: Typography.material2018().black.subtitle1!,
-                cursorColor: Colors.blue,
-                backgroundCursorColor: Colors.grey,
-                scribbleEnabled: false,
-              ),
-            ],
-          ),
-        ),
-      ),
-    );
-    await tester.showKeyboard(find.byKey(ValueKey<String>(controller.text)));
-
-    // There should be a new platform message updating the selection rects.
-    expect(log.where((MethodCall m) => m.method == 'TextInput.setSelectionRects').length, 0);
-
-    // On web, we should rely on the browser's implementation of Scribble, so we will not send selection rects.
-  }, skip: kIsWeb, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); // [intended]
-
   testWidgets('text styling info is sent on show keyboard', (WidgetTester tester) async {
     final List<MethodCall> log = <MethodCall>[];
     tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.textInput, (MethodCall methodCall) async {
diff --git a/packages/flutter_test/lib/src/test_text_input.dart b/packages/flutter_test/lib/src/test_text_input.dart
index 1a4f5d9..a9787c0 100644
--- a/packages/flutter_test/lib/src/test_text_input.dart
+++ b/packages/flutter_test/lib/src/test_text_input.dart
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:typed_data';
-import 'dart:ui' show Rect, Offset;
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/services.dart';
@@ -273,84 +271,4 @@
       (ByteData? data) { /* response from framework is discarded */ },
     );
   }
-
-  /// Simulates a scribble interaction starting.
-  Future<void> startScribbleInteraction() async {
-    assert(isRegistered);
-    await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-      SystemChannels.textInput.name,
-      SystemChannels.textInput.codec.encodeMethodCall(
-        MethodCall(
-          'TextInputClient.scribbleInteractionBegan',
-           <dynamic>[_client ?? -1,]
-        ),
-      ),
-      (ByteData? data) { /* response from framework is discarded */ },
-    );
-  }
-
-  /// Simulates a Scribble focus.
-  Future<void> scribbleFocusElement(String elementIdentifier, Offset offset) async {
-    assert(isRegistered);
-    await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-      SystemChannels.textInput.name,
-      SystemChannels.textInput.codec.encodeMethodCall(
-        MethodCall(
-          'TextInputClient.focusElement',
-           <dynamic>[elementIdentifier, offset.dx, offset.dy]
-        ),
-      ),
-      (ByteData? data) { /* response from framework is discarded */ },
-    );
-  }
-
-  /// Simulates iOS asking for the list of Scribble elements during UIIndirectScribbleInteraction.
-  Future<List<List<dynamic>>> scribbleRequestElementsInRect(Rect rect) async {
-    assert(isRegistered);
-    List<List<dynamic>> response = <List<dynamic>>[];
-    await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-      SystemChannels.textInput.name,
-      SystemChannels.textInput.codec.encodeMethodCall(
-        MethodCall(
-          'TextInputClient.requestElementsInRect',
-           <dynamic>[rect.left, rect.top, rect.width, rect.height]
-        ),
-      ),
-      (ByteData? data) {
-        response = (SystemChannels.textInput.codec.decodeEnvelope(data!) as List<dynamic>).map((dynamic element) => element as List<dynamic>).toList();
-      },
-    );
-
-    return response;
-  }
-
-  /// Simulates iOS inserting a UITextPlaceholder during a long press with the pencil.
-  Future<void> scribbleInsertPlaceholder() async {
-    assert(isRegistered);
-    await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-      SystemChannels.textInput.name,
-      SystemChannels.textInput.codec.encodeMethodCall(
-        MethodCall(
-          'TextInputClient.insertTextPlaceholder',
-           <dynamic>[_client ?? -1, 0.0, 0.0]
-        ),
-      ),
-      (ByteData? data) { /* response from framework is discarded */ },
-    );
-  }
-
-  /// Simulates iOS removing a UITextPlaceholder after a long press with the pencil is released.
-  Future<void> scribbleRemovePlaceholder() async {
-    assert(isRegistered);
-    await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
-      SystemChannels.textInput.name,
-      SystemChannels.textInput.codec.encodeMethodCall(
-        MethodCall(
-          'TextInputClient.removeTextPlaceholder',
-           <dynamic>[_client ?? -1]
-        ),
-      ),
-      (ByteData? data) { /* response from framework is discarded */ },
-    );
-  }
 }