From 14d88ee0c8f499aad52f62a6573d4c37e20b7885 Mon Sep 17 00:00:00 2001
From: "auto-submit[bot]" <98614782+auto-submit[bot]@users.noreply.github.com>
Date: Mon, 13 May 2024 17:43:36 +0000
Subject: [PATCH] Reverts "Native ios context menu (#143002)" (#148237)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reverts: flutter/flutter#143002
Initiated by: cbracken
Reason for reverting: unresolved docs links. See failure here: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20docs_test/16540/overview
```
dartdoc:stdout: Generating docs for package flutter...
dartdoc:stderr: error: unresolved doc reference [TextInput.showSystemContextMenu]
dartdoc:stderr: from widgets.MediaQueryData.supportsShowingSystemContextMenu: (file:///b/s/w/ir/x/w/flutter/packages/flutt
Original PR Author: justinmc
Reviewed By: {Renzo-Olivares, hellohuanlin}
This change reverts the following previous change:
In order to work around the fact that iOS 15 shows a notification on pressing Flutter's paste button (https://github.com/flutter/flutter/issues/103163), this PR allows showing the iOS system context menu in text fields.
It is currently opt-in, which a user would typically do like this (also in example system_context_menu.0.dart):
```dart
contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) {
// If supported, show the system context menu.
if (SystemContextMenu.isSupported(context)) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
}
// Otherwise, show the flutter-rendered context menu for the current
// platform.
return AdaptiveTextSelectionToolbar.editableText(
editableTextState: editableTextState,
);
},
```
Requires engine PR https://github.com/flutter/engine/pull/50095.
## API changes
### SystemContextMenu
A widget that shows the system context menu when built, and removes it when disposed. The main high-level way that most users would use this PR. Only works on later versions of iOS.
### SystemContextMenuController
Used under the hood to hide and show a system context menu. There can only be one visible at a time.
### MediaQuery.supportsShowingSystemContextMenu
Sent by the iOS embedder to tell the framework whether or not the platform supports showing the system context menu. That way the framework, or Flutter developers, can decide to show a different menu.
### `flutter/platform ContextMenu.showSystemContextMenu`
Sent by the framework to show the menu at a given `targetRect`, which is the current selection rect.
### `flutter/platform ContextMenu.hideSystemContextMenu`
Sent by the framework to hide the menu. Typically not needed, because the platform will hide the menu when the user taps outside of it and after the user presses a button, but it handles edge cases where the user programmatically rebuilds the context menu, for example.
### `flutter/platform System.onDismissSystemContextMenu`
Sent by the iOS embedder to indicate that the system context menu has been hidden by the system, such as when the user taps outside of the menu. This is useful when there are multiple instances of SystemContextMenu, such as with multiple text fields.
---
.../system_context_menu.0.dart | 41 --
.../system_context_menu.0_test.dart | 67 ---
.../flutter/lib/src/services/binding.dart | 43 +-
.../flutter/lib/src/services/text_input.dart | 178 +-------
.../lib/src/widgets/editable_text.dart | 40 +-
.../flutter/lib/src/widgets/media_query.dart | 46 +-
.../lib/src/widgets/system_context_menu.dart | 132 ------
.../text_selection_toolbar_anchors.dart | 65 +--
packages/flutter/lib/widgets.dart | 1 -
.../system_context_menu_controller_test.dart | 257 -----------
.../test/services/text_input_utils.dart | 78 ----
.../widgets/system_context_menu_test.dart | 415 ------------------
packages/flutter_test/lib/src/window.dart | 13 -
13 files changed, 49 insertions(+), 1327 deletions(-)
delete mode 100644 examples/api/lib/widgets/system_context_menu/system_context_menu.0.dart
delete mode 100644 examples/api/test/widgets/system_context_menu/system_context_menu.0_test.dart
delete mode 100644 packages/flutter/lib/src/widgets/system_context_menu.dart
delete mode 100644 packages/flutter/test/services/system_context_menu_controller_test.dart
delete mode 100644 packages/flutter/test/widgets/system_context_menu_test.dart
diff --git a/examples/api/lib/widgets/system_context_menu/system_context_menu.0.dart b/examples/api/lib/widgets/system_context_menu/system_context_menu.0.dart
deleted file mode 100644
index b648c116b00..00000000000
--- a/examples/api/lib/widgets/system_context_menu/system_context_menu.0.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/material.dart';
-
-/// Flutter code sample for [SystemContextMenu].
-
-void main() => runApp(const SystemContextMenuExampleApp());
-
-class SystemContextMenuExampleApp extends StatelessWidget {
- const SystemContextMenuExampleApp({super.key});
-
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- home: Scaffold(
- appBar: AppBar(
- title: const Text('SystemContextMenu Basic Example'),
- ),
- body: Center(
- child: TextField(
- contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) {
- // If supported, show the system context menu.
- if (SystemContextMenu.isSupported(context)) {
- return SystemContextMenu.editableText(
- editableTextState: editableTextState,
- );
- }
- // Otherwise, show the flutter-rendered context menu for the current
- // platform.
- return AdaptiveTextSelectionToolbar.editableText(
- editableTextState: editableTextState,
- );
- },
- ),
- ),
- ),
- );
- }
-}
diff --git a/examples/api/test/widgets/system_context_menu/system_context_menu.0_test.dart b/examples/api/test/widgets/system_context_menu/system_context_menu.0_test.dart
deleted file mode 100644
index f8052e500b9..00000000000
--- a/examples/api/test/widgets/system_context_menu/system_context_menu.0_test.dart
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_api_samples/widgets/system_context_menu/system_context_menu.0.dart' as example;
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
- testWidgets('only shows the system context menu on iOS when MediaQuery says it is supported', (WidgetTester tester) async {
- await tester.pumpWidget(
- Builder(
- builder: (BuildContext context) {
- final MediaQueryData mediaQueryData = MediaQuery.of(context);
- return MediaQuery(
- data: mediaQueryData.copyWith(
- // Faking this value, which is usually set to true only on
- // devices running iOS 16+.
- supportsShowingSystemContextMenu: defaultTargetPlatform == TargetPlatform.iOS,
- ),
- child: const example.SystemContextMenuExampleApp(),
- );
- },
- ),
- );
-
- expect(find.byType(SystemContextMenu), findsNothing);
-
- // Show the context menu.
- final Finder textFinder = find.byType(EditableText);
- await tester.longPress(textFinder);
- tester.state(textFinder).showToolbar();
- await tester.pumpAndSettle();
-
- switch (defaultTargetPlatform) {
- case TargetPlatform.iOS:
- expect(find.byType(SystemContextMenu), findsOneWidget);
- expect(find.byType(AdaptiveTextSelectionToolbar), findsNothing);
- case TargetPlatform.android:
- case TargetPlatform.fuchsia:
- case TargetPlatform.linux:
- case TargetPlatform.macOS:
- case TargetPlatform.windows:
- expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
- expect(find.byType(SystemContextMenu), findsNothing);
- }
- }, variant: TargetPlatformVariant.all(), skip: kIsWeb); // [intended]
-
- testWidgets('does not show the system context menu when not supported', (WidgetTester tester) async {
- await tester.pumpWidget(
- // By default, MediaQueryData.supportsShowingSystemContextMenu is false.
- const example.SystemContextMenuExampleApp(),
- );
-
- expect(find.byType(SystemContextMenu), findsNothing);
-
- // Show the context menu.
- final Finder textFinder = find.byType(EditableText);
- await tester.longPress(textFinder);
- tester.state(textFinder).showToolbar();
- await tester.pumpAndSettle();
-
- expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
- expect(find.byType(SystemContextMenu), findsNothing);
- }, variant: TargetPlatformVariant.all(), skip: kIsWeb); // [intended]
-}
diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart
index b513f182380..14825107a94 100644
--- a/packages/flutter/lib/src/services/binding.dart
+++ b/packages/flutter/lib/src/services/binding.dart
@@ -357,14 +357,8 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
Future _handlePlatformMessage(MethodCall methodCall) async {
final String method = methodCall.method;
+ assert(method == 'SystemChrome.systemUIChange' || method == 'System.requestAppExit');
switch (method) {
- // Called when the system dismisses the system context menu, such as when
- // the user taps outside the menu. Not called when Flutter shows a new
- // system context menu while an old one is still visible.
- case 'ContextMenu.onDismissSystemContextMenu':
- for (final SystemContextMenuClient client in _systemContextMenuClients) {
- client.handleSystemHide();
- }
case 'SystemChrome.systemUIChange':
final List args = methodCall.arguments as List;
if (_systemUiChangeCallback != null) {
@@ -372,8 +366,6 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
}
case 'System.requestAppExit':
return {'response': (await handleRequestAppExit()).name};
- default:
- throw AssertionError('Method "$method" not handled.');
}
}
@@ -518,19 +510,6 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
Future initializationComplete() async {
await SystemChannels.platform.invokeMethod('System.initializationComplete');
}
-
- final Set _systemContextMenuClients = {};
-
- /// Registers a [SystemContextMenuClient] that will receive system context
- /// menu calls from the engine.
- static void registerSystemContextMenuClient(SystemContextMenuClient client) {
- instance._systemContextMenuClients.add(client);
- }
-
- /// Unregisters a [SystemContextMenuClient] so that it is no longer called.
- static void unregisterSystemContextMenuClient(SystemContextMenuClient client) {
- instance._systemContextMenuClients.remove(client);
- }
}
/// Signature for listening to changes in the [SystemUiMode].
@@ -609,23 +588,3 @@ class _DefaultBinaryMessenger extends BinaryMessenger {
}
}
}
-
-/// An interface to receive calls related to the system context menu from the
-/// engine.
-///
-/// Currently this is only supported on iOS 16+.
-///
-/// See also:
-/// * [SystemContextMenuController], which uses this to provide a fully
-/// featured way to control the system context menu.
-/// * [MediaQuery.maybeSupportsShowingSystemContextMenu], which indicates
-/// whether the system context menu is supported.
-/// * [SystemContextMenu], which provides a widget interface for displaying the
-/// system context menu.
-mixin SystemContextMenuClient {
- /// Handles the system hiding a context menu.
- ///
- /// This is called for all instances of [SystemContextMenuController], so it's
- /// not guaranteed that this instance was the one that was hidden.
- void handleSystemHide();
-}
diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart
index ecea3a576aa..f46a753e30c 100644
--- a/packages/flutter/lib/src/services/text_input.dart
+++ b/packages/flutter/lib/src/services/text_input.dart
@@ -17,7 +17,6 @@ import 'package:flutter/foundation.dart';
import 'package:vector_math/vector_math_64.dart' show Matrix4;
import 'autofill.dart';
-import 'binding.dart';
import 'clipboard.dart' show Clipboard;
import 'keyboard_inserted_content.dart';
import 'message_codec.dart';
@@ -1809,7 +1808,7 @@ class TextInput {
Future _handleTextInputInvocation(MethodCall methodCall) async {
final String method = methodCall.method;
- switch (method) {
+ switch (methodCall.method) {
case 'TextInputClient.focusElement':
final List args = methodCall.arguments as List;
_scribbleClients[args[0]]?.onScribbleFocus(Offset((args[1] as num).toDouble(), (args[2] as num).toDouble()));
@@ -2404,178 +2403,3 @@ class _PlatformTextInputControl with TextInputControl {
);
}
}
-
-/// Allows access to the system context menu.
-///
-/// The context menu is the menu that appears, for example, when doing text
-/// selection. Flutter typically draws this menu itself, but this class deals
-/// with the platform-rendered context menu.
-///
-/// Only one instance can be visible at a time. Calling [show] while the system
-/// context menu is already visible will hide it and show it again at the new
-/// [Rect]. An instance that is hidden is informed via [onSystemHide].
-///
-/// Currently this system context menu is bound to text input. The buttons that
-/// are shown and the actions they perform are dependent on the currently
-/// active [TextInputConnection]. Using this without an active
-/// [TextInputConnection] is a noop.
-///
-/// Call [dispose] when no longer needed.
-///
-/// See also:
-///
-/// * [ContextMenuController], which controls Flutter-drawn context menus.
-/// * [SystemContextMenu], which wraps this functionality in a widget.
-/// * [MediaQuery.maybeSupportsShowingSystemContextMenu], which indicates
-/// whether the system context menu is supported.
-class SystemContextMenuController with SystemContextMenuClient {
- /// Creates an instance of [SystemContextMenuController].
- ///
- /// Not shown until [show] is called.
- SystemContextMenuController({
- this.onSystemHide,
- }) {
- ServicesBinding.registerSystemContextMenuClient(this);
- }
-
- /// Called when the system has hidden the context menu.
- ///
- /// For example, tapping outside of the context menu typically causes the
- /// system to hide it directly. Flutter is made aware that the context menu is
- /// no longer visible through this callback.
- ///
- /// This is not called when [show]ing a new system context menu causes another
- /// to be hidden.
- final VoidCallback? onSystemHide;
-
- static const MethodChannel _channel = SystemChannels.platform;
-
- static SystemContextMenuController? _lastShown;
-
- /// The target [Rect] that was last given to [show].
- ///
- /// Null if [show] has not been called.
- Rect? _lastTargetRect;
-
- /// True when the instance most recently [show]n has been hidden by the
- /// system.
- bool _hiddenBySystem = false;
-
- bool get _isVisible => this == _lastShown && !_hiddenBySystem;
-
- /// After calling [dispose], this instance can no longer be used.
- bool _isDisposed = false;
-
- // Begin SystemContextMenuClient.
-
- @override
- void handleSystemHide() {
- assert(!_isDisposed);
- // If this instance wasn't being shown, then it wasn't the instance that was
- // hidden.
- if (!_isVisible) {
- return;
- }
- if (_lastShown == this) {
- _lastShown = null;
- }
- _hiddenBySystem = true;
- onSystemHide?.call();
- }
-
- // End SystemContextMenuClient.
-
- /// Shows the system context menu anchored on the given [Rect].
- ///
- /// The [Rect] represents what the context menu is pointing to. For example,
- /// for some text selection, this would be the selection [Rect].
- ///
- /// There can only be one system context menu visible at a time. Calling this
- /// while another system context menu is already visible will remove the old
- /// menu before showing the new menu.
- ///
- /// Currently this system context menu is bound to text input. The buttons
- /// that are shown and the actions they perform are dependent on the
- /// currently active [TextInputConnection]. Using this without an active
- /// [TextInputConnection] will be a noop.
- ///
- /// This is only supported on iOS 16.0 and later.
- ///
- /// See also:
- ///
- /// * [hideSystemContextMenu], which hides the menu shown by this method.
- /// * [MediaQuery.supportsShowingSystemContextMenu], which indicates whether
- /// this method is supported on the current platform.
- Future show(Rect targetRect) {
- assert(!_isDisposed);
- assert(
- TextInput._instance._currentConnection != null,
- 'Currently, the system context menu can only be shown for an active text input connection',
- );
-
- // Don't show the same thing that's already being shown.
- if (_lastShown != null && _lastShown!._isVisible && _lastShown!._lastTargetRect == targetRect) {
- return Future.value();
- }
-
- assert(
- _lastShown == null || _lastShown == this || !_lastShown!._isVisible,
- 'Attempted to show while another instance was still visible.',
- );
-
- _lastTargetRect = targetRect;
- _lastShown = this;
- _hiddenBySystem = false;
- return _channel.invokeMethod