Update clipboard status on cut (#92167)

Cut now explicitly updates the clipboard status.
diff --git a/packages/flutter/lib/src/cupertino/desktop_text_selection.dart b/packages/flutter/lib/src/cupertino/desktop_text_selection.dart
index c0c76c1..6b1f89b 100644
--- a/packages/flutter/lib/src/cupertino/desktop_text_selection.dart
+++ b/packages/flutter/lib/src/cupertino/desktop_text_selection.dart
@@ -55,7 +55,7 @@
       clipboardStatus: clipboardStatus,
       endpoints: endpoints,
       globalEditableRegion: globalEditableRegion,
-      handleCut: canCut(delegate) ? () => handleCut(delegate) : null,
+      handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null,
       handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null,
       handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
       handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
diff --git a/packages/flutter/lib/src/cupertino/text_selection.dart b/packages/flutter/lib/src/cupertino/text_selection.dart
index 45e88a8..6b3ddde 100644
--- a/packages/flutter/lib/src/cupertino/text_selection.dart
+++ b/packages/flutter/lib/src/cupertino/text_selection.dart
@@ -236,7 +236,7 @@
       clipboardStatus: clipboardStatus,
       endpoints: endpoints,
       globalEditableRegion: globalEditableRegion,
-      handleCut: canCut(delegate) ? () => handleCut(delegate) : null,
+      handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null,
       handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null,
       handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
       handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
diff --git a/packages/flutter/lib/src/material/desktop_text_selection.dart b/packages/flutter/lib/src/material/desktop_text_selection.dart
index dde0640..e24f4db 100644
--- a/packages/flutter/lib/src/material/desktop_text_selection.dart
+++ b/packages/flutter/lib/src/material/desktop_text_selection.dart
@@ -41,7 +41,7 @@
       clipboardStatus: clipboardStatus,
       endpoints: endpoints,
       globalEditableRegion: globalEditableRegion,
-      handleCut: canCut(delegate) ? () => handleCut(delegate) : null,
+      handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null,
       handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null,
       handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
       handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
diff --git a/packages/flutter/lib/src/material/text_selection.dart b/packages/flutter/lib/src/material/text_selection.dart
index f1def2a..ca1d892 100644
--- a/packages/flutter/lib/src/material/text_selection.dart
+++ b/packages/flutter/lib/src/material/text_selection.dart
@@ -45,7 +45,7 @@
       endpoints: endpoints,
       delegate: delegate,
       clipboardStatus: clipboardStatus,
-      handleCut: canCut(delegate) ? () => handleCut(delegate) : null,
+      handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null,
       handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null,
       handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
       handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index 2995b5b..a0cb911 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -2808,7 +2808,7 @@
 
   VoidCallback? _semanticsOnCut(TextSelectionControls? controls) {
     return widget.selectionEnabled && cutEnabled && _hasFocus && controls?.canCut(this) == true
-      ? () => controls!.handleCut(this)
+      ? () => controls!.handleCut(this, _clipboardStatus)
       : null;
   }
 
diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart
index 971d9c9..9feb7fe 100644
--- a/packages/flutter/lib/src/widgets/text_selection.dart
+++ b/packages/flutter/lib/src/widgets/text_selection.dart
@@ -204,8 +204,9 @@
   ///
   /// This is called by subclasses when their cut affordance is activated by
   /// the user.
-  void handleCut(TextSelectionDelegate delegate) {
+  void handleCut(TextSelectionDelegate delegate, ClipboardStatusNotifier? clipboardStatus) {
     delegate.cutSelection(SelectionChangedCause.toolbar);
+    clipboardStatus?.update();
   }
 
   /// Call [TextSelectionDelegate.copySelection] to copy current selection.
diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart
index 7f28c9d..3ccb3c2 100644
--- a/packages/flutter/test/widgets/editable_text_test.dart
+++ b/packages/flutter/test/widgets/editable_text_test.dart
@@ -8724,7 +8724,7 @@
   }
 
   @override
-  void handleCut(TextSelectionDelegate delegate) {
+  void handleCut(TextSelectionDelegate delegate, ClipboardStatusNotifier? clipboardStatus) {
     cutCount += 1;
   }
 
diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart
index ba3b28f..04f7327 100644
--- a/packages/flutter/test/widgets/text_selection_test.dart
+++ b/packages/flutter/test/widgets/text_selection_test.dart
@@ -759,6 +759,28 @@
       });
     });
   });
+
+  group('TextSelectionControls', () {
+    test('ClipboardStatusNotifier is updated on handleCut', () async {
+      final FakeClipboardStatusNotifier clipboardStatus = FakeClipboardStatusNotifier();
+      final FakeTextSelectionDelegate delegate = FakeTextSelectionDelegate();
+      final CustomTextSelectionControls textSelectionControls = CustomTextSelectionControls();
+
+      expect(clipboardStatus.updateCalled, false);
+      textSelectionControls.handleCut(delegate, clipboardStatus);
+      expect(clipboardStatus.updateCalled, true);
+    });
+
+    test('ClipboardStatusNotifier is updated on handleCopy', () async {
+      final FakeClipboardStatusNotifier clipboardStatus = FakeClipboardStatusNotifier();
+      final FakeTextSelectionDelegate delegate = FakeTextSelectionDelegate();
+      final CustomTextSelectionControls textSelectionControls = CustomTextSelectionControls();
+
+      expect(clipboardStatus.updateCalled, false);
+      textSelectionControls.handleCopy(delegate, clipboardStatus);
+      expect(clipboardStatus.updateCalled, true);
+    });
+  });
 }
 
 class FakeTextSelectionGestureDetectorBuilderDelegate implements TextSelectionGestureDetectorBuilderDelegate {
@@ -872,3 +894,55 @@
     selectWordCalled = true;
   }
 }
+
+class CustomTextSelectionControls extends TextSelectionControls {
+  @override
+  Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight, [VoidCallback? onTap, double? startGlyphHeight, double? endGlyphHeight]) {
+    throw UnimplementedError();
+  }
+
+  @override
+  Widget buildToolbar(
+    BuildContext context,
+    Rect globalEditableRegion,
+    double textLineHeight,
+    Offset position,
+    List<TextSelectionPoint> endpoints,
+    TextSelectionDelegate delegate,
+    ClipboardStatusNotifier clipboardStatus,
+    Offset? lastSecondaryTapDownPosition,
+  ) {
+    throw UnimplementedError();
+  }
+
+  @override
+  Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight, [double? startGlyphHeight, double? endGlyphHeight]) {
+    throw UnimplementedError();
+  }
+
+  @override
+  Size getHandleSize(double textLineHeight) {
+    throw UnimplementedError();
+  }
+}
+
+class FakeClipboardStatusNotifier extends ClipboardStatusNotifier {
+  FakeClipboardStatusNotifier() : super(value: ClipboardStatus.unknown);
+
+  @override
+  bool get disposed => false;
+
+  bool updateCalled = false;
+  @override
+  Future<void> update() async {
+    updateCalled = true;
+  }
+}
+
+class FakeTextSelectionDelegate extends Fake implements TextSelectionDelegate {
+  @override
+  void cutSelection(SelectionChangedCause cause) { }
+
+  @override
+  void copySelection(SelectionChangedCause cause) { }
+}