Don't access clipboard passively on iOS (#60316)

diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart
index 4000b61..046aa8f 100644
--- a/packages/flutter/lib/src/widgets/text_selection.dart
+++ b/packages/flutter/lib/src/widgets/text_selection.dart
@@ -1522,6 +1522,25 @@
 
   /// Check the [Clipboard] and update [value] if needed.
   void update() {
+    // iOS 14 added a notification that appears when an app accesses the
+    // clipboard. To avoid the notification, don't access the clipboard on iOS,
+    // and instead always shown the paste button, even when the clipboard is
+    // empty.
+    // TODO(justinmc): Use the new iOS 14 clipboard API method hasStrings that
+    // won't trigger the notification.
+    // https://github.com/flutter/flutter/issues/60145
+    switch (defaultTargetPlatform) {
+      case TargetPlatform.iOS:
+        value = ClipboardStatus.pasteable;
+        return;
+      case TargetPlatform.android:
+      case TargetPlatform.fuchsia:
+      case TargetPlatform.linux:
+      case TargetPlatform.macOS:
+      case TargetPlatform.windows:
+        break;
+    }
+
     Clipboard.getData(Clipboard.kTextPlain).then((ClipboardData data) {
       final ClipboardStatus clipboardStatus = data != null && data.text != null && data.text.isNotEmpty
           ? ClipboardStatus.pasteable
diff --git a/packages/flutter/test/cupertino/text_selection_test.dart b/packages/flutter/test/cupertino/text_selection_test.dart
index 35bed86..f77d926 100644
--- a/packages/flutter/test/cupertino/text_selection_test.dart
+++ b/packages/flutter/test/cupertino/text_selection_test.dart
@@ -176,7 +176,8 @@
     });
   });
 
-  testWidgets('Paste only appears when clipboard has contents', (WidgetTester tester) async {
+  // TODO(justinmc): https://github.com/flutter/flutter/issues/60145
+  testWidgets('Paste always appears regardless of clipboard content on iOS', (WidgetTester tester) async {
     final TextEditingController controller = TextEditingController(
       text: 'Atwater Peel Sherbrooke Bonaventure',
     );
@@ -202,8 +203,8 @@
     await tester.tapAt(textOffsetToPosition(tester, index));
     await tester.pumpAndSettle();
 
-    // No Paste yet, because nothing has been copied.
-    expect(find.text('Paste'), findsNothing);
+    // Paste is showing even though clipboard is empty.
+    expect(find.text('Paste'), findsOneWidget);
     expect(find.text('Copy'), findsOneWidget);
     expect(find.text('Cut'), findsOneWidget);
 
@@ -219,7 +220,7 @@
     await tester.tapAt(textOffsetToPosition(tester, index));
     await tester.pumpAndSettle();
 
-    // Paste now shows.
+    // Paste still shows.
     expect(find.text('Paste'), findsOneWidget);
     expect(find.text('Copy'), findsOneWidget);
     expect(find.text('Cut'), findsOneWidget);
diff --git a/packages/flutter/test/material/text_selection_test.dart b/packages/flutter/test/material/text_selection_test.dart
index 518923b..ec36761 100644
--- a/packages/flutter/test/material/text_selection_test.dart
+++ b/packages/flutter/test/material/text_selection_test.dart
@@ -636,5 +636,57 @@
     expect(find.text('Cut'), findsOneWidget);
     expect(find.text('Paste'), findsOneWidget);
     expect(find.text('Select all'), findsOneWidget);
-  }, skip: isBrowser);
+  }, skip: isBrowser, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android }));
+
+  // TODO(justinmc): https://github.com/flutter/flutter/issues/60145
+  testWidgets('Paste always appears regardless of clipboard content on iOS', (WidgetTester tester) async {
+    final TextEditingController controller = TextEditingController(
+      text: 'Atwater Peel Sherbrooke Bonaventure',
+    );
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Material(
+          child: Column(
+            children: <Widget>[
+              TextField(
+                controller: controller,
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+
+    // Make sure the clipboard is empty.
+    await Clipboard.setData(const ClipboardData(text: ''));
+
+    // Double tap to select the first word.
+    const int index = 4;
+    await tester.tapAt(textOffsetToPosition(tester, index));
+    await tester.pump(const Duration(milliseconds: 50));
+    await tester.tapAt(textOffsetToPosition(tester, index));
+    await tester.pumpAndSettle();
+
+    // Paste is showing even though clipboard is empty.
+    expect(find.text('Paste'), findsOneWidget);
+    expect(find.text('Copy'), findsOneWidget);
+    expect(find.text('Cut'), findsOneWidget);
+
+    // Tap copy to add something to the clipboard and close the menu.
+    await tester.tapAt(tester.getCenter(find.text('Copy')));
+    await tester.pumpAndSettle();
+    expect(find.text('Copy'), findsNothing);
+    expect(find.text('Cut'), findsNothing);
+
+    // Double tap to show the menu again.
+    await tester.tapAt(textOffsetToPosition(tester, index));
+    await tester.pump(const Duration(milliseconds: 50));
+    await tester.tapAt(textOffsetToPosition(tester, index));
+    await tester.pumpAndSettle();
+
+    // Paste still shows.
+    expect(find.text('Copy'), findsOneWidget);
+    expect(find.text('Cut'), findsOneWidget);
+    expect(find.text('Paste'), findsOneWidget);
+  }, skip: isBrowser, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
 }