Add text to TextField from an outside source - flutter

I added a speech recognition to a text field, it works but I cannot manage to add the text to the textfield, is there a way to do that.
the textfield looks like this:
Widget _buildDescriptionTextField(productBloc) {
return StreamBuilder<Object>(
stream: productBloc.messageStream,
builder: (context, snapshot) {
return TextField(
maxLines: 3,
controller: _controllerMessage,
onChanged: productBloc.messageSink,
decoration: InputDecoration(
labelText: allTranslations.text(StringConstant.description),
errorText: snapshot.error,
suffixIcon: IconButton(icon: Icon(Icons.mic), onPressed: () {
if (_isAvailable && !_isListening)
_speechRecognition
.listen(locale: "en_US")
.then((result) => print('$result'));
},
),
),
);
}
);
}
I have a steam-builder to manage the added text manually, and an controller if this page is used for editing, then as suffixsIcon the iconButton to start the speech recognition. when I add the result text outside a text Widget it works but I need it inside the texField.

Just doing that should work no ?
setState(() => _controllerMessage.text = result)

You need to use TextEditingController properties. I assume you declared one as _controllerMessage.
To set new value to your TextField and keep the cursor in the end - use something similar to the example from the Docs.
e.g.
_speechRecognition
.listen(locale: "en_US")
.then(_onResult);
// ...
void _onResult(String result) {
setState(() {
_controllerMessage.value = _controllerMessage.value.copyWith(
text: result,
selection: TextSelection(baseOffset: result.length, extentOffset: result.length),
composing: TextRange.empty,
);
});
}
Let me know if this helped.

So What I did is just used the _speechRecognition.setRecognitionResultHandler from the documentation, to set a new value to the controller of the textField, like so:
_speechRecognition.setRecognitionResultHandler(
(String speech) => setState(() {
_controllerMessage = new TextEditingController(text: resultText = speech);
})
);
the textField stays like it was before, see question.

Related

Flutter TextFormfield Controller get value

This is Textformfield. It receives the value and then displays the information through it. TextEditContoller
This is page 1
Widget lngTextFormField(String label, String keyword, String text) {
TextEditingController lngtextController = TextEditingController(text: text);
lngtextController.selection = TextSelection.collapsed(offset: lngtextController.text.length);
return TextFormField(
controller: lngtextController,
onChanged: (value) {
saveAd(value, keyword);
},
decoration: InputDecoration(
labelText: label,
labelStyle: TextStyle(
fontFamily: 'supermarket',
fontSize: 16,
),
isDense: true,
),
);
}
This is a page 2
This is a save button that will return lat,lng to page 1.
void saveAddress() {
var lat = centerMap.latitude;
var lng = centerMap.longitude;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditAddressPage(
index: widget.index,
lat: lat.toString(),
lng: lng.toString(),
),
),
);
print("Saving address: Latitude: $lat, Longitude: $lng");
}
what can i do I want to send the second page lat,lng values ​​back to page 1 instead of the existing values. I tried this, sometimes the value is sent but the original value is empty before the new value is sent. Maybe need to type something in TextFormfield before being able to save
There are a lot of option to do, but the one of the easiest is :
When navigates to 2nd page from 1st page, replace or pop it
//you can use this
Navigator.pushReplacement(context, route);
//or this
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
Navigator.pop(context);
after 2nd page open and button pressed, you need to pass string to 1st page using Navigator.push (you have done it well)
inside initstate of the 1st page make a condition, example
#override
void initState() {
super.initState();
if (widget.lat != null || widget.lat != "") {
lattextController.text = widget.lat!;
}
if (widget.lng != null || widget.lng != "") {
lngtextController.text = widget.lng!;
}
//sometime when initsate doesnt work you can call setstate, depends on logic that you writes
}
this logic means that when the 1st page created they will set the value first before build()

ScrollView scrolls automatically if i set text to textfield using its controller

I have created a form using scrollview widget & when initState() method _getDataUser() is called i set some prefill controllerEmail.text to textfield which makes my scrollview to scroll to the TextFormField Email source code :
https://pastebin(dot)com/sFR7s4wS
I found a solution here, you can wrap your TextField into a Focus, and only when the user has focused on the text field we set showCursor to true.
bool showCursor = false;
Focus(
onFocusChange: (focus) => setState(() => showCursor = focus),
child: TextField(
showCursor: showCursor,
controller: _textController,
textCapitalization: TextCapitalization.words,
),
)
This is flutter's native functionality and I have programatically solved this using ScrollController and Future.delayed. Just assign your scroll view or list view to a scroll controller and do the following:
runZoned(() {
Future.delayed(Duration(seconds: 0), () {
_scrollController.jumpTo(0);
});
}, onError: (error, stackTrace) {
print('Zone Error: $error');
});

Flutter- TextEditingController listener get called multiple time for textfield

TextField controlled controller.addListener(() gets called multiple time after pressing the clear button, this will only happen if we are clearing it.
Snippet:
TextEditingController controller = new TextEditingController();
TextField field = new TextField(
controller: controller,
autofocus: true,
);
controller.addListener(() {
print("Pressed cancel button");
});
Video Link
Note: While adding characters in TextField listener method gets called only ones.
I guess that would be a defect on flutter, a possible solution would be to use onChanged()
TextField field = new TextField(
autofocus: true,
onChanged: (String value) {
print("Pressed clear button");
},
);
I have the same problem with Nexus 6p when used with API level 23 and Pixel with API 25.
but this problem did not occurs with Pixel with API28 and it does not occurs with Nexus6P with API26.
exact code from https://flutter.dev/docs/cookbook/forms/text-field-changes was used.
1. We need to create our own .clear() method
void clearField() {
print("c: clearField");
var newValue = textController.value.copyWith(
text: '',
selection: TextSelection.collapsed(offset: 0),
);
textController.value = newValue;
callApi('');
}
// and call it by :
child: TextField(
controller: textController,
autofocus: true,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: clearField, // call
),
),
),
2. We need to carefully handle changes
void changesOnField() {
print("c: changesOnField");
String text = textController.text;
if (text.isNotEmpty) { // set this
callApi(text);
}
}
Full Code
You may look into this repo and build it locally Github
Result

Detect 'enter key' press in flutter

In my case i need to scan barcode and fetch product details. Normally barcode scanner devices emit enter key(keycode=13) event at end of scanning, But in flutter enter key is not same as Done so how can code to detect enter key pressed in my TextFormField widget?
if you are using TextField then you have to add onSubmitted in your text field to detect when user press Enter key. For my case, I changed Done in keyboard to TextInputAction.Search. It also works for TextInputAction.Done too. here is a sample code
TextField(
onSubmitted: (value){
//value is entered text after ENTER press
//you can also call any function here or make setState() to assign value to other variable
},
textInputAction: TextInputAction.search,
)
The solution above works, but I believe RawKeyboardListener is a more reliable and flexible solution. You just need to cover the text field with it and start to listen to keyboard events:
var focusNode = FocusNode();
RawKeyboardListener(
focusNode: focusNode,
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
// Do something
}
},
child: TextField(controller: TextEditingController())
)
As a second option you can use onKey method of the FocusNoded and pass the node to your text field:
var focusNode = FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
// Do something
// Next 2 line needed If you don't want to update the text field with new line.
// node.unfocus();
// return true;
}
return false;
});
TextField(focusNode: focusNode, controller: TextEditingController())
In case someone is looking for the same solution (as Al Walid Ashik) but for TextFormField, just use the following:
TextFormField(
/// ...
onFieldSubmitted: (value) {
/// do some stuff here
},
),
TextFormField(
maxLines: null,
autovalidate: true,
validator: (value){
if(value.contains('\n')){
doFun(value);
}
}
)
When user press enter key new line create in text box. We check with that.
maxLine:null - to hide multiline
autovalidate:true -to automatically run validator fun
'\n' - new line ('\s'-whitespace,'\t'-tab.. etc)
In addition to the Sergey Yamshchikov's answer:
In case if it is a multiline TextField (maxLines: null) and you want to catch up the entered key and prevent passing it into the text field, you can use this approach:
RawKeyboardListener(
focusNode: FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
return KeyEventResult.handled; // prevent passing the event into the TextField
}
return KeyEventResult.ignored; // pass the event to the TextField
}),
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
// Do something
}
},
child: TextField(controller: TextEditingController())
)
But, if you need to detect the keys combination, like ctrl+enter, then you can use CallbackShortcuts:
CallbackShortcuts(
bindings: {
const SingleActivator(LogicalKeyboardKey.enter, control: true): _doSomething(),
},
child: Focus(
autofocus: true,
child: TextField(controller: TextEditingController()),
),
);

Select all text inside TextField when it gets focus

On TextField foucus I want to select all text so that when the user starts typing the existing text gets deleted.
This will be alternative of : android:selectAllOnFocus="true" in Android.
How to achive this?
Pass a controller and focusNode explicitly, then you have full control:
final _controller = TextEditingController();
final _focusNode = FocusNode();
initState() {
super.initState();
_focusNode.addListener(() {
if(_focusNode.hasFocus) {
_controller.selection = TextSelection(baseOffset: 0, extentOffset: _controller.text.length);
}
});
}
build() => TextField(controller: _controller, focusNode: _focusNode);
Update
from https://github.com/flutter/flutter/issues/28307#issuecomment-467952074 to prevent endless loop:
_controller.addListener(() {
final newText = _controller.text.toLowerCase();
_controller.value = _controller.value.copyWith(
text: newText,
selection: TextSelection(baseOffset: newText.length, extentOffset: newText.length),
composing: TextRange.empty,
);
});
It's also possible to set the selection in onTap event. Like this:
TextField(
controller: _controller,
onTap: () => _controller.selection = TextSelection(baseOffset: 0, extentOffset: _controller.value.text.length),
)
It's actually much easier than the accepted answer.
First initialize a TextEditingController.
final _controller = TextEditingController();
Then, somewhere within your build method (probably within your edit text logic), set the text and the selection props like so.
_controller.text = textValue;
_controller.selection = TextSelection(
baseOffset: 0,
extentOffset: textValue.length,
);
In the TextFormField, make sure to assign the controller and set autofocus to true.
TextFormField(
controller: _controller,
autofocus: true,
//...more properties
That's it!
For a cleaner, reusable experience an Extension class method (see note below) is handy.
extension TextEditingControllerExt on TextEditingController {
void selectAll() {
if (text.isEmpty) return;
selection = TextSelection(baseOffset: 0, extentOffset: text.length);
}
}
On the field:
TextField(
controller: myController,
onTap: myController.selectAll
)
Example
On Dartpad:
https://dartpad.dev/?id=271737b109637f90a2fe5ea55ea2ac43
Note
In extension classes, this can be omitted.
text is assumed to be: this.text i.e. <this instance of controller>.text
Same with selection being actually this.selection.
As of Dart 2:
controller1.selection = TextSelection(baseOffset:0, extentOffset:controller1.text.length);