Overwrite Paste Event for TextFormField - flutter

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!

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

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.

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.

Related

How do I disable KeyboardListener when the focus is in TextField?

I have a requirement of using arrow keys to move between PageView pages, which I achieved with KeyboardListener. However some pages may include a TextInput and if that TextInput is focused, I want to prevent the KeyboardListener to handle the arrow keys.
The only solution I can think of is to set some flag when TextInput gains focus and condition the arrow logic, but this does not look elegant at all (since the KeyboardListener is way up the Widget tree and I would need to wire that logic to every TextInput).
Is there a better way?
I ended up doing this:
Extended FocusNode to have class indicating that for given Widget global keyboard events should be ignored:
class IgnoreShortcutKeysFocusNode extends FocusNode {}
In Widgets that contain a TextInput, I first create the extended IgnoreShortcutKeysFocusNode and assign it to the TextInput:
final _focusNode = IgnoreShortcutKeysFocusNode();
#override
Widget build(BuildContext context) {
return TextField(
focusNode: _focusNode,
// ...
);
}
In KeyboardListener handler, I check what is the currently focused node and act accordingly:
void _handleKeyEvent(KeyEvent value) {
if (FocusManager.instance.primaryFocus is IgnoreShortcutKeysFocusNode) return;
// arrow key handling
}
This is not ideal since the IgnoreShortcutKeysFocusNode must be added to each TextInput, but it works.

how to use flutter speech to text provider class

folks.....how could I use Speech To Text Provider so I can use it multiple times in multiple widgets? I read speech to text could only be started once per app session....
I know I need to use it as a provider class but I don´t know how..
I tried speech to text but It just doesn´t work for me since I need to use it in multiple widgets and I want to be able to use the callbacks "onstatus" and "onerror"
I don´t know what else to say since it is asking me to type a certain number of letters
initialize speech to text in the root/first class.
final SpeechToText speech = SpeechToText();
late SpeechToTextProvider speechProvider;
in the initState, initialize the speechProvider and pass the speech instance to it.
speechProvider = SpeechToTextProvider(speech);
await speechProvider.initialize(); // do it in any other method and call it from initState.
in the root widget, add the ChangeNotifierProvider and pass the SpeechToTextProvider class.
ChangeNotifierProvider<SpeechToTextProvider>.value(
value: speechProvider,
child: ///
now you can use speechToTextProvider anyWhere in your project.
now in order to use it anywhere in your project you have to initilaize the speechToTextProvider in the class where you want to use it.
late SpeechToTextProvider speechToTextProvider;
speechToTextProvider = Provider.of<SpeechToTextProvider>(context); // do it in a didChangeDependency or any other method and call it from initState.
now in order to start listening
speechToTextProvider.listen(
pauseFor: Duration(seconds: 5),
localeId: 'en-us',
listenFor: Duration(seconds: 5));
StreamSubscription<SpeechRecognitionEvent> subscription =
speechToTextProvider.stream.listen(
(event) {
// on every change it update
if (event.eventType ==
SpeechRecognitionEventType.partialRecognitionEvent) {
if (mounted) {
_setState!(
() {
toDisplayText =
speechToTextProvider.lastResult!.recognizedWords; // the textField or Text Widget Will be updated
},
);
}
}
//on error
else if (event.eventType == SpeechRecognitionEventType.errorEvent) {
///on error if some error occurs then close the dilog box here . or stop the listner
}
}
//on done
else if (event.eventType ==
SpeechRecognitionEventType.finalRecognitionEvent) {
//when the user stop speaking.
}
},
);
}
and in that way you can use it in any class or widget. You have to repeat the process of start listening in other class.

Flutter. How to correctly update state of nested widgets?

I'm new in flutter and I'm trying to implement something like radio group with custom buttons in Android. For this I created StatefulWidget, which hold list of selectable buttons. For every button I was set press listener where I do something like this:
setState(() {
buttons.forEach((button) => button.isSelected = false);
buttons[selectedButtonIndex].isSelected = true;
});
And then my CustomButtonWidget changes color depending on the parameter isSelected .
All this works well. However, I have an additional requirement. I need my RadioGroupWidget to return the selected button type. For this I created a callback :
final ValueChanged<ButtonType> onChanged;
And now my button press listener looking like this:
onTap: () {
setState(() {
buttons.forEach((button) => button.isSelected = false);
buttons[selectedButtonIndex].isSelected = true;
});
onChanged(buttons[selectedButtonIndex].type);
}
Next I get this type of button in my other widget which is using RadioGroupWidget:
CustomRadioGroup(
onChanged: (value) {
setState(() {
buttonType= value;
});
}),
)
As you can see I call again setState. This is what leads to the problem. But I need to do this, because I need to update the state of another widget (for example let's call it InfoWidget) depending on the selected button.
After all these manipulations, the state of the InfoWidget is updated correctly, but the state of the selected button in the RadioGroupWidget does not change. I tried to debug this and I see that at first the parameter isSelected is set to true for the desired button, but after the state of the button I selected is not updated, because its parameter isSelected becomes false. And I don't understand why this is happening.
Please help, I am completely confused.

Flutter: How to add bullets points in a TextField?

How to add bullets points in a TextField widget similar to the Notes app in IOS?
You can use Unicode code to achieve this. Add the following Unicode ( \u25EF ) to your TextField using TextEditingController. You may also need to change the position of the cursor when adding text to your TextEditingController. This depends on your requirement and you will understand when you test this.
TextEditingController _textController = new TextEditingController();
_textController.text = " \u25EF ";
Yes. of course you need a custom button or something to trigger this event like in the note of IOS.
You can create a custom keyboard with an extra button to add bullets or a floating deck with a button like the Note app. Or simply give a button on the screen.
You need to create a checkbox (or similar button) manually when user select it and add to left of textfield. You can define variable on a Row and initialize to Container. Something like that:
Widget widget = Container();
[...]
Row(
children: [
widget,
Expanded(child: TextField(...))
],
);
[...]
//this activate bullet point
void functionCheckBox(){
setState({
widget = Checkbox(value: value, onChanged: onChanged);
});
}
I dont know if it's the best solution or if exists a plugin that does something like that.

changing variables inside listview builder

i want to change variable values when the widget is loading the data from the web,
just want to do something like this:
_playid = _notes[1].id;
title = _notes[1].title;
but wherever I put it, i get an error,
I tried to put it in a set state inside listview builder, but no luck since I don't want it with onPress or on tap methods
could someone help, please?
i just found out,
simply we have to use setstate in fetch inside initstate
void initState() {
title = " ";
fetchNotes().then((value) {
setState(() {
_notes.addAll(value);
_playid = _notes[0].numb;
});
});
}