【问题标题】:Overwrite Paste Event for TextFormField覆盖 TextFormField 的粘贴事件
【发布时间】:2022-04-18 22:06:42
【问题描述】:

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

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

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

提前致谢!

【问题讨论】:

  • @GameLoading 谢谢,我已经找到了。但尚不清楚如何将其与 TextFormField 一起使用
  • 请详细说明您想做什么?为什么要覆盖 textField 上的粘贴?当用户点击 textField 工具栏中的粘贴按钮时,你想打开一个对话框吗?
  • @PayamZahedi 是的!正常的方法是:长标签 -> 选择工具栏 -> 粘贴 -> 剪贴板内容将被粘贴到文本字段中。我不想直接插入内容,而是想打开一个带有两个选项的对话框或弹出菜单(可能是两种不同的格式)。因此用户可以选择粘贴的格式。
  • 现在可以创建一个完整的自定义文本选择工具栏:ktuusj.medium.com/flutter-custom-selection-toolbar-3acbe7937dd3

标签: flutter paste


【解决方案1】:

AFAIK,您不能完全“拦截”标准工具栏。但是,您可以做的是阻止标准工具栏并制作自己的工具栏。

您可以使用在 IgnorePointer 下包装 textfield/textformfield。它将隐藏文本字段上的任何点击手势。下面是代码sn-p。

IgnorePointer(
    child: TextField(
        focusNode: _textfieldFocusNode,
        controller: _controller,
    ),
)

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

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

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

【讨论】:

  • 感谢您的工作。但是,在这种情况下,我需要自己实现整个工具栏,包括复制、全选……嗯,我相信这可能是一种解决方法,但这并不是我真正想要的。
  • 我完全理解。在分享这个解决方法之前,我研究了粘贴事件是否可以被黑客入侵,但没有得到任何建设性的东西。实施标准工具栏(cur-copy-paste & co)是一个很小的代价,你需要支付:-(此外实施并不大。
【解决方案2】:

我搜索这个问题。我认为没有适当的方法来解决这个问题。我阅读了有关 Textfield 类的信息,并找到了两种解决方案。
如果您检查TextField 小部件,您会发现它将使用EditableText 来显示其简单的文本输入。 EditableText 有一个selectionControls 属性。此属性用于呈现选择工具栏。另外,我发现 material 和 Cupertino 有不同的实现。

第一个解决方案:您可以创建自己的自定义 TextField,它将使用 EditableText 并将您的自定义 selectionControl 传递给您的小部件。我认为这将是一项非常困难的工作。创建您自己的小部件实现、处理动画和...

第二个解决方案:您可以简单地将TextField的所有相关文件复制到一个新文件中并根据需要进行更新。对于这个解决方案,我创建了一个repo in GitHub。您可以签出源代码以了解如何在粘贴选项中显示对话框。这就是代码的工作方式。

注意:我只是简单地更新了 selectionControls 的 Material 实现的粘贴功能。如果你愿意,你也可以更新 Cupertino selectionControls。

注意:我还在更改代码的所有地方添加了文档。

【讨论】:

  • 谢谢,我去看看。目前我使用的是 TextFormField 而不是 TextField。您认为这可能会带来更多问题吗?
  • TextFormField 也使用 TextField。在这种情况下,您应该将 CustomFormField 发送到 TextFormField,而不是 TextField。我只是更新我的存储库并将 CustomTextFormField 添加到它。看看
  • IMO,第二个/第二个解决方案不会是未来的证明。 A. 需要以任何方式实现整个工具栏。更重要的是,B. 当 OP 从标准 TextField 复制文件时,他将无法使用 Flutter 更新 w.r.t。文本字段。所以,以我的拙见,如果没有其他可能的解决方案,这应该是最后的手段。相反,使用 IgnorePointer 和 GestureDetector 的解决方案是 100% 标准的。只是我的两分钱。
  • 非常感谢您的努力。但是,尽管如此,我还是分享了@Sukhi 关于这不会再有任何内部更新的问题的想法。这可能会引发比它解决的问题更多的问题。
【解决方案3】:

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

在每个 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);
      }
    }
  • 然后,在您的状态下初始化它(例如在 StatefulWidget 中,它可能看起来像这样,见下文)。要研究它如何在 TextField 中使用,请参阅源代码here
    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();
      }

onPaste 使用类型为ValueChanged&lt;TextSelectionDelegate&gt; 的回调,您可以使用Flutter 团队用来获取剪贴板数据的the same code

      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;
        }
      }

然后在您的 TextField 中使用选择控件。

    TextFormField(
      selectionControls: _selectionControls,
    )

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 2011-12-12
    • 2019-09-15
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    相关资源
    最近更新 更多