'Overwrite Paste Event for TextFormField

I have a TextFormField. Usually you can use the selection toolbar to copy/paste/select all and so on using long tap/double tap.

I want to overwrite the Paste Event. It shouldn't simple insert the current clipboard data but open a popup with several options to insert.

Is it possible to catch and overwrite the Paste event in any way? I saw something like handlePaste() for SelectionControls, but I don't know how to add this to my TextFormField.

Thanks in advance!



Solution 1:[1]

AFAIK, you can't exactly 'intercept' the standard toolbar. However, what you can do is to prevent the standard toolbar and make your own.

You can use wrap the textfield/textformfield under IgnorePointer. It will hide any tap gestures on the text field. Below is the code snippet.

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

Now,you can wrap this IgnorePointer under GestureDetector and show your own menu. Like this :

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

This produces the result below and the sample implementation code is here

enter image description here

Solution 2:[2]

I search for this problem. I think there is no proper way to solve this problem. I read about the Textfield class and found two solutions for it.
if you check TextField widget you can find that it will use EditableText to show its simple Text input. EditableText has a selectionControls property. this property is used to render the selection toolbar. also, I found that material and Cupertino have different implementation of it.

1st Solution: you can create your own custom TextField that will use EditableText and pass your custom selectionControl to your widget. I think this gonna be a very hard job to do. create your own implementation of the widget, handling animations, and...

2nd Solution: You can simply copy all related files of TextField in a new file and update it as you want. for this solution, I create a repo in GitHub. you can checkout source code to understand how you can show a dialog in the paste option. and this is how the code should work.

note: I just simply update paste function of the Material implementation of selectionControls. if you want you can also update the Cupertino selectionControls too.

note: also I added documents in everywhere I change the code.

enter image description here

Solution 3:[3]

Found a way to override paste event. I'm not sure, that it is a right way, but it works.

In every TextField you have selectionControls, that provides a way to show and handle toolbar controls.

So, to catch paste event first:

  • create your own version of selection controls, for example
    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);
      }
    }
  • then, initialise it in your state (for example in StatefulWidget it can looks like that, see below). To study how it used in TextField please see source 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();
      }

Use callback for onPaste with a type ValueChanged<TextSelectionDelegate> and you can use the same code the Flutter team used to get Clipboard data:

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

Then use selection controls in your TextField.

    TextFormField(
      selectionControls: _selectionControls,
    )

Hope it helps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sukhi
Solution 2 Payam Zahedi
Solution 3 Arenukvern