| // 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. |
| |
| /// @docImport 'package:flutter/material.dart'; |
| library; |
| |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/services.dart'; |
| |
| import 'basic.dart'; |
| import 'editable_text.dart'; |
| import 'framework.dart'; |
| import 'media_query.dart'; |
| import 'text_selection_toolbar_anchors.dart'; |
| |
| /// Displays the system context menu on top of the Flutter view. |
| /// |
| /// Currently, only supports iOS 16.0 and above and displays nothing on other |
| /// platforms. |
| /// |
| /// 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 instead. |
| /// |
| /// There can only be one system context menu visible at a time. Building this |
| /// widget when the system context menu is already visible will hide the old one |
| /// and display this one. A system context menu that is hidden is informed via |
| /// [onSystemHide]. |
| /// |
| /// To check if the current device supports showing the system context menu, |
| /// call [isSupported]. |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to create a [TextField] that uses the system context |
| /// menu where supported and does not show a system notification when the user |
| /// presses the "Paste" button. |
| /// |
| /// ** See code in examples/api/lib/widgets/system_context_menu/system_context_menu.0.dart ** |
| /// {@end-tool} |
| /// |
| /// See also: |
| /// |
| /// * [SystemContextMenuController], which directly controls the hiding and |
| /// showing of the system context menu. |
| class SystemContextMenu extends StatefulWidget { |
| /// Creates an instance of [SystemContextMenu] that points to the given |
| /// [anchor]. |
| const SystemContextMenu._({super.key, required this.anchor, this.onSystemHide}); |
| |
| /// Creates an instance of [SystemContextMenu] for the field indicated by the |
| /// given [EditableTextState]. |
| factory SystemContextMenu.editableText({Key? key, required EditableTextState editableTextState}) { |
| final (startGlyphHeight: double startGlyphHeight, endGlyphHeight: double endGlyphHeight) = |
| editableTextState.getGlyphHeights(); |
| return SystemContextMenu._( |
| key: key, |
| anchor: TextSelectionToolbarAnchors.getSelectionRect( |
| editableTextState.renderEditable, |
| startGlyphHeight, |
| endGlyphHeight, |
| editableTextState.renderEditable.getEndpointsForSelection( |
| editableTextState.textEditingValue.selection, |
| ), |
| ), |
| onSystemHide: () { |
| editableTextState.hideToolbar(); |
| }, |
| ); |
| } |
| |
| /// The [Rect] that the context menu should point to. |
| final Rect anchor; |
| |
| /// Called when the system hides this context menu. |
| /// |
| /// For example, tapping outside of the context menu typically causes the |
| /// system to hide the menu. |
| /// |
| /// This is not called when showing a new system context menu causes another |
| /// to be hidden. |
| final VoidCallback? onSystemHide; |
| |
| /// Whether the current device supports showing the system context menu. |
| /// |
| /// Currently, this is only supported on newer versions of iOS. |
| static bool isSupported(BuildContext context) { |
| return MediaQuery.maybeSupportsShowingSystemContextMenu(context) ?? false; |
| } |
| |
| @override |
| State<SystemContextMenu> createState() => _SystemContextMenuState(); |
| } |
| |
| class _SystemContextMenuState extends State<SystemContextMenu> { |
| late final SystemContextMenuController _systemContextMenuController; |
| |
| @override |
| void initState() { |
| super.initState(); |
| _systemContextMenuController = SystemContextMenuController(onSystemHide: widget.onSystemHide); |
| _systemContextMenuController.show(widget.anchor); |
| } |
| |
| @override |
| void didUpdateWidget(SystemContextMenu oldWidget) { |
| super.didUpdateWidget(oldWidget); |
| if (widget.anchor != oldWidget.anchor) { |
| _systemContextMenuController.show(widget.anchor); |
| } |
| } |
| |
| @override |
| void dispose() { |
| _systemContextMenuController.dispose(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| assert(SystemContextMenu.isSupported(context)); |
| return const SizedBox.shrink(); |
| } |
| } |