Unverified Commit f6cd2d4b authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

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

parent d55251cb
...@@ -1522,6 +1522,25 @@ class ClipboardStatusNotifier extends ValueNotifier<ClipboardStatus> with Widget ...@@ -1522,6 +1522,25 @@ class ClipboardStatusNotifier extends ValueNotifier<ClipboardStatus> with Widget
/// Check the [Clipboard] and update [value] if needed. /// Check the [Clipboard] and update [value] if needed.
void update() { 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) { Clipboard.getData(Clipboard.kTextPlain).then((ClipboardData data) {
final ClipboardStatus clipboardStatus = data != null && data.text != null && data.text.isNotEmpty final ClipboardStatus clipboardStatus = data != null && data.text != null && data.text.isNotEmpty
? ClipboardStatus.pasteable ? ClipboardStatus.pasteable
......
...@@ -176,7 +176,8 @@ void main() { ...@@ -176,7 +176,8 @@ void main() {
}); });
}); });
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( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
...@@ -202,8 +203,8 @@ void main() { ...@@ -202,8 +203,8 @@ void main() {
await tester.tapAt(textOffsetToPosition(tester, index)); await tester.tapAt(textOffsetToPosition(tester, index));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// No Paste yet, because nothing has been copied. // Paste is showing even though clipboard is empty.
expect(find.text('Paste'), findsNothing); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget); expect(find.text('Copy'), findsOneWidget);
expect(find.text('Cut'), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
...@@ -219,7 +220,7 @@ void main() { ...@@ -219,7 +220,7 @@ void main() {
await tester.tapAt(textOffsetToPosition(tester, index)); await tester.tapAt(textOffsetToPosition(tester, index));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Paste now shows. // Paste still shows.
expect(find.text('Paste'), findsOneWidget); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Copy'), findsOneWidget); expect(find.text('Copy'), findsOneWidget);
expect(find.text('Cut'), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
......
...@@ -636,5 +636,57 @@ void main() { ...@@ -636,5 +636,57 @@ void main() {
expect(find.text('Cut'), findsOneWidget); expect(find.text('Cut'), findsOneWidget);
expect(find.text('Paste'), findsOneWidget); expect(find.text('Paste'), findsOneWidget);
expect(find.text('Select all'), 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 }));
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment