覆盖 TextFormField 的粘贴事件

S-M*_*Man 6 paste flutter

我有一个TextFormField。通常,您可以使用选择工具栏通过长按/双击来复制/粘贴/全选等。

我想覆盖粘贴事件。它不应该简单地插入当前剪贴板数据,而是打开一个弹出窗口,其中包含多个要插入的选项。

是否可以以任何方式捕获并覆盖粘贴事件?我看到类似handlePaste()for 的东西SelectionControls,但我不知道如何将其添加到我的TextFormField.

提前致谢!

Suk*_*khi 7

AFAIK,您无法完全“拦截”标准工具栏。但是,您可以做的是阻止标准工具栏并创建您自己的工具栏。

您可以使用将文本字段/文本表单字段包装在 IgnorePointer 下。它将隐藏文本字段上的所有点击手势。下面是代码片段。

IgnorePointer(
    child: TextField(
        focusNode: _textfieldFocusNode,
        controller: _controller,
    ),
)
Run Code Online (Sandbox Code Playgroud)

现在,您可以将此 IgnorePointer 包装在 GestureDetector 下并显示您自己的菜单。像这样 :

GestureDetector(
    behavior: HitTestBehavior.opaque,
    onTap: () {
        FocusScope.of(context).requestFocus(_textfieldFocusNode);
    },
    onLongPress: () {
        showMenu(____
    }
)
Run Code Online (Sandbox Code Playgroud)

这会产生下面的结果,示例实现代码在这里

在此输入图像描述


leg*_*ael 7

由于 flutter 3.7 @Arenukvern 答案已被弃用。相反,可以使用contextMenuBuilder覆盖工具栏事件:

TextFormField(
    ...
    /// Override default input action toolbar to catch paste event
    contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) {
        return AdaptiveTextSelectionToolbar.editable(
            anchors: editableTextState.contextMenuAnchors,
            clipboardStatus: ClipboardStatus.pasteable,
            // to apply the normal behavior when click on copy (copy in clipboard close toolbar)
            // use an empty function `() {}` to hide this option from the toolbar
            onCopy: () => editableTextState.copySelection(SelectionChangedCause.toolbar),
            // to apply the normal behavior when click on cut
            onCut: () => editableTextState.cutSelection(SelectionChangedCause.toolbar),
            onPaste: () {
                // HERE will be called when the paste button is clicked in the toolbar
                // apply your own logic here

                // to apply the normal behavior when click on paste (add in input and close toolbar)
                editableTextState.pasteText(SelectionChangedCause.toolbar);
            },            
            // to apply the normal behavior when click on select all
            onSelectAll: () => editableTextState.selectAll(SelectionChangedCause.toolbar),
        );
    },
    ...
)
Run Code Online (Sandbox Code Playgroud)

这应该与除网络之外的所有平台兼容(


Are*_*ern 5

找到了一种覆盖粘贴事件的方法。我不确定这是一种正确的方法,但它确实有效。

在每个 TextField 中都有SelectionControls,它提供了一种显示和处理工具栏控件的方法。

因此,首先要捕获粘贴事件:

  • 例如,创建您自己的选择控件版本
    class AppCupertinoTextSelectionControls extends CupertinoTextSelectionControls {
      AppCupertinoTextSelectionControls({
        required this.onPaste,
      });
      ValueChanged<TextSelectionDelegate> onPaste;
      @override
      Future<void> handlePaste(final TextSelectionDelegate delegate) {
        onPaste(delegate);
        return super.handlePaste(delegate);
      }
    }
    
    class AppMaterialTextSelectionControls extends MaterialTextSelectionControls {
      AppMaterialTextSelectionControls({
        required this.onPaste,
      });
      ValueChanged<TextSelectionDelegate> onPaste;
      @override
      Future<void> handlePaste(final TextSelectionDelegate delegate) {
        onPaste(delegate);
        return super.handlePaste(delegate);
      }
    }
Run Code Online (Sandbox Code Playgroud)
  • 然后,在您的状态中初始化它(例如在 StatefulWidget 中,它看起来像这样,见下文)。要研究它如何在 TextField 中使用,请参阅此处的源代码
    TextSelectionControls? _selectionControls;
      @override
      void initState() {
        if (widget.onPaste != null) {
          if (Platform.isIOS) {
            _selectionControls = AppCupertinoTextSelectionControls(
              onPaste: widget.onPaste!,
            );
          } else {
            _selectionControls = AppMaterialTextSelectionControls(
              onPaste: widget.onPaste!,
            );
          }
        }
        super.initState();
      }
Run Code Online (Sandbox Code Playgroud)

onPaste使用带有类型的回调ValueChanged<TextSelectionDelegate>,您可以使用Flutter 团队用于获取剪贴板数据的相同代码:

      Future<void> onPastePhone(final TextSelectionDelegate? delegate) async {
        final TextSelection selection = phoneController.selection;
        if (!selection.isValid) {
          return;
        }
        // Snapshot the input before using `await`.
        // See https://github.com/flutter/flutter/issues/11427
        final ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
        final text = data?.text ?? '';
        if (text.isEmpty) {
          return;
        }
      }
Run Code Online (Sandbox Code Playgroud)

然后在文本字段中使用选择控件。

    TextFormField(
      selectionControls: _selectionControls,
    )
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你。