I'm creating a "barcoder" text field on flutter. After the page opened which is the barcoder belong,
I need to autofocus on TextField. But on the first opening, I want to hide the keyboard. And then if the user focuses on TextField The keyboard can appear.
How can I achieve this? I already try
class FirstDisabledFocusNode extends FocusNode {
#override
bool consumeKeyboardToken() {
return false;
}
}
and it didn't work.
Here is my code
Card(
elevation: 1,
child: TextField(
autofocus: true,
focusNode: textFieldFocusNode,
onEditingComplete: () {
print('Barcode: ${textController.text}');
buttonFocusNode.requestFocus();
events!(context);
if (textController.text.isNotEmpty) {
_audioCache.play('success.mp3');
}
},
controller: textController,
decoration: const InputDecoration(
fillColor: Colors.white,
prefixIcon: Icon(Icons.qr_code),
hintText: 'Barcode',
focusColor: Palette.asBlue,
border: InputBorder.none,
focusedBorder: InputBorder.none),
),
);
When it goes to the next screen, you can use this in the initState of that widget.
void initState() {
super.initState();
Future.delayed(
Duration(),
() => SystemChannels.textInput.invokeMethod('TextInput.hide'),
);
}
if it didn't work click the link that i provided in the comments.
Related
iam using a device that scans barcode , I want after each read the focus to return the TextFormFiled - the code below do the work and I see the cursor is focused on the TextFormFiled but when I read next time its show nothing , I need to manually just click by my finger on the textfiled to activate the focus ,can somebody help me ( the device returned LF after each read)
TextFormField(
decoration: new InputDecoration(
border: new OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
),
filled: true,
hintStyle: new TextStyle(
color: Colors.grey[800]),
hintText: "Read BarCode",
fillColor: Colors.white70),
focusNode: myFocusNode,
controller: search,
autofocus: true,
maxLines: null,
validator: (value) {
// print(value.toString().runes);
if (value.toString().contains("\n")) {
fetchProducts(value!);
search.text = "";
} else {}
},
),
Use your myFocusNode to activate the focus on textField.
void function(){
/// after scanning is complete call this
focusNode.requestFocus()
}
I pass for this and did solve it like this:
_addItem() {
final isValid = _formKey.currentState?.validate() ?? false;
if (!isValid) {
return;
}
final ean = int.parse(_eanController.text);
listaEan.add(ean);
_eanController.text = '';
setState(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_eanFocus.requestFocus();
});
});
}
but in physical scan device, did works fine. without use the addPostFramaCallback.
I'm working on a Flutter Web application which includes chat.
I'd like to include an ordinary input function where users can enter text and send it into the chat stream. A standard feature of chat apps these days is to send on <ENTER> and to perform a line break on <SHIFT-ENTER>, or some variation of this.
Currently I've only been able to achieve one of these functions at a time. If you set the TextField's keyboardType to TextInputType.multiline then <ENTER> and <SHIFT-ENTER> always perform a line-break, there doesn't appear to be a way to override this behavior.
If instead your TextField is TextInputType.text you can capture <ENTER> and send, but trying to capture <SHIFT-ENTER> to add a line-break has not worked. I've tried manually grabbing the key press via an onKey handler and inserting \n to the controller.text, but it appears that TextInputType.text is not meant for multiline at all, so it doesn't play well.
Just wondering if any other devs have run into this or come up with any suitable solutions. Ideally a solution would also work across android/ios. For me, I've decided to go with TextInputType.text and forgo the multiline functionality for now.
Thanks
For what it's worth, I was able to concoct a reasonable solution that I'll post below in case anyone runs into this themselves.
I wrapped the Textfield in a keyboard listener which calls my onSend function when it see's an <Enter>. I tried this before, but I guess earlier I was missing the cast to RawKeyEventDataWeb which allowed me to capture isShiftPressed to allow for new lines on <SHFT-ENTER> without forcing a send. Unfortunately I had to add some hacky code to remove the \n that's added when pressing enter, but that's a small price to pay for functional + modern messaging.
RawKeyboardListener(
focusNode: focusNode,
onKey: handleKeyPress,
child: TextField(
controller: messageController,
minLines: 1,
maxLines: null,
textInputAction: TextInputAction.done,
style: normalTextStyle,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
isDense: true,
hintText: 'Type a message',
hintStyle: TextStyle(
fontSize: 16,
color: Color(0xFF474749),
),
border: InputBorder.none,
),
),
)
void handleKeyPress(event) {
if (event is RawKeyUpEvent && event.data is RawKeyEventDataWeb) {
var data = event.data as RawKeyEventDataWeb;
if (data.code == "Enter" && !event.isShiftPressed) {
final val = messageController.value;
final messageWithoutNewLine =
messageController.text.substring(0, val.selection.start - 1) +
messageController.text.substring(val.selection.start);
messageController.value = TextEditingValue(
text: messageWithoutNewLine,
selection: TextSelection.fromPosition(
TextPosition(offset: messageWithoutNewLine.length),
),
);
_onSend();
}
}
}
This can be achieved by adding a FocusNode to the TextField. Place the focus node in your widget's state.
late final _focusNode = FocusNode(
onKey: (FocusNode node, RawKeyEvent evt) {
if (!evt.isShiftPressed && evt.logicalKey.keyLabel == 'Enter') {
if (evt is RawKeyDownEvent) {
_sendMessage();
}
return KeyEventResult.handled;
}
else {
return KeyEventResult.ignored;
}
},
);
In your build function add the focus when creating the TextField.
TextField(
autofocus: true,
controller: _textController,
focusNode: _focusNode,
)
This is what I am using in my TextField to support newline on enter.
class TextInputsWidget extends StatelessWidget {
final TextEditingController chatTextFieldController = TextEditingController();
late final _focusNode = FocusNode(
onKey: _handleKeyPress,
);
KeyEventResult _handleKeyPress(FocusNode focusNode, RawKeyEvent event) {
// handles submit on enter
if (event.isKeyPressed(LogicalKeyboardKey.enter) && !event.isShiftPressed) {
_sendMessage();
// handled means that the event will not propagate
return KeyEventResult.handled;
}
// ignore every other keyboard event including SHIFT+ENTER
return KeyEventResult.ignored;
}
void _sendMessage() {
if (chatTextFieldController.text.trim().isNotEmpty) {
// Do something with your input text
print(chatTextFieldController.text.trim());
// bring focus back to the input field
Future.delayed(Duration.zero, () {
_focusNode.requestFocus();
chatTextFieldController.clear();
});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
textInputAction: TextInputAction.newline,
autofocus: true,
focusNode: _focusNode,
controller: chatTextFieldController,
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.fromLTRB(8, 0, 0, 0),
hintText: "Enter your message here",
hintStyle: TextStyle(color: Colors.black54),
),
),
);
}
}
There are mainly 3 key changes
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
FocusNode which can listen to keyboard events
The best way to have the Enter key be disabled for the input and instead send it when no ctrl key is pressed is through the focusNode directly on the input, this way you won't have to remove extra new lines.
class _InputTextState extends State<InputText> {
late final _focusNode = FocusNode(onKey: handleKeyPress);
#override
Widget build(BuildContext context) {
return TextField(
focusNode: _focusNode,
);
}
KeyEventResult handleKeyPress(FocusNode focusNode, RawKeyEvent event) {
// handles submit on enter
if (kIsWeb &&
event.isKeyPressed(LogicalKeyboardKey.enter) &&
!event.isControlPressed &&
!event.isShiftPressed) {
widget.onSubmit();
// handled means that the event will not propagate
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
}
}
I want to change default colour of icon of TextFormField, which will also get changes on selection. There are few way to change colour but non of them works for me.
1. Set icon color
When changing icon colour directly as below the icon colour will not change when selected, below is the code of the same and screenshot which shows red colour when field is selected or not selected.
TextFormField(
maxLength: 15,
decoration: InputDecoration(
labelText: "USER NAME",
prefixIcon: IconTheme(data: IconThemeData(
color: Colors.redAccent
), child: Icon(Icons.email,))
),
onSaved: (username) => _username = username,
),
2. Leave it default
This will show grey colour when field is not selected and primary colour when field is selected. Below is the screenshot of the same. Here i want to change the colour of icon from grey to red and want to show primary colour on selection.
NOTE:- It will be good if we can do this using theme
The code below will generate the FocusNodes you need to manage the focus of your TextFormFields and do a setState when the focus changes from one field to another. We are just creating a list of focus nodes, listening to changes on each of them and assigning them to a field:
List<FocusNode> _focusNodes = [
FocusNode(),
FocusNode(),
FocusNode(),
FocusNode(),
];
#override
void initState() {
_focusNodes.forEach((node){
node.addListener(() {
setState(() {});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextFormField(
focusNode: _focusNodes[0],
decoration: InputDecoration(
prefixIcon: Icon(Icons.email,
color: _focusNodes[0].hasFocus ? Theme.of(context).accentColor : Colors.grey,
),
),
),
TextFormField(
focusNode: _focusNodes[1],
decoration: InputDecoration(
prefixIcon: Icon(Icons.email,
color: _focusNodes[1].hasFocus ? Theme.of(context).accentColor : Colors.grey,
),
),
),
TextFormField(
focusNode: _focusNodes[2],
decoration: InputDecoration(
prefixIcon: Icon(Icons.email,
color: _focusNodes[2].hasFocus ? Theme.of(context).accentColor : Colors.grey,
),
),
),
],
);
}
If you want to change icon color at the time textFormField is selected,
change it according to below.
...
Theme(
child: Column(
children: <Widget>[
TextFormField(
maxLength: 15,
decoration: InputDecoration(
labelText: "USER NAME",
prefixIcon: Icon(Icons.email,)
),
onSaved: (username) => _username = username,
),
TextFormField(
maxLength: 15,
decoration: InputDecoration(
labelText: "EMAIL",
prefixIcon: Icon(Icons.email,)
),
onSaved: (usermail) => _useremail = usermail,
),
],
),
data: Theme.of(context)
.copyWith(primaryColor: Colors.redAccent,),
),
...
Conclusion:
Wrap all the textFormFields widgets with Theme widget and set data field as the color you want as a highlighter.
As you can see in the picture below, You can't change the default color of Icons in Textfields. The only solution for it is to create PR on flutter and change this method. 😄
I create a profile page and i have 4 textformfield. I want to on tap icon activate textformfield and focus at the same time. Now I need tap twice on icon and first activated field, secondly focused.
How to solve it?
My code:
class UserProfile extends StatefulWidget {
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
FocusNode myFocusNode;
bool isEnable = false;
#override
void initState() {
super.initState();
myFocusNode = FocusNode();
}
#override
void dispose() {
myFocusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.fromLTRB(40.0, 50.0, 20.0, 0.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: TextFormField(
enabled: isEnable,
focusNode: myFocusNode,
),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
isEnable = true;
FocusScope.of(context).requestFocus(myFocusNode);
});
})
],
),
You should use autofocus: isEnable instead.
just do like below in your ontap
setState(() {
if(isEnable)
Future.delayed(const Duration(milliseconds: 10), ()
{FocusScope.of(context).requestFocus(myFocusNode);
});
isEnable = true;
});
in first time isEnable is false so focusing not call and just enabling work and in other times get focus too.
you can't focus widget until disabled and when you enabling widget. when you do focusing and enabling at same time in ui tread it's try focusing before enabling because of their rendering time.if you post some delay to focusing the problem get solving.
Try using readOnly instead of enabled in TextFormField
I faced similar issue when I had multiple TextFields to enable kinda PIN input. And some of that had to be dynamically enabled and disabled plus prevent users from entering value in the next field while they haven't finished the previous one. I've tried a lot of approaches and focusing field after some delay was not a way to go because I wanted the keyboard to always be available while entering. So I've took a crazy path and solved this next way:
onTap: () => _focusNodes[_currentLetterIndex].requestFocus()
where _focusNodes are for each letter and _currentLetterIndex is calculated programmatically during input (when finished letter 0 -> current becomes 1 and so on). As the result when user tried to tap on next field - it was automatically refocused to the current one which behaves like the next field is disabled.
An example of the full text field looks like this (don't pay attention to decorations etc.)
TextField(
key: ValueKey(index),
controller: _editingControllers[index],
onTap: () => _focusNodes[_currentLetterIndex].requestFocus(),
focusNode: _focusNodes[index],
textAlign: TextAlign.center,
showCursor: false,
maxLength: 2,
enableInteractiveSelection: false,
autocorrect: false,
enableSuggestions: false,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
height: 1.2,
decoration: TextDecoration.none),
decoration: InputDecoration(
fillColor: !_wordCompleted &&
!correct &&
_editingControllers[index].text.isNotEmpty
? const Color(0xFFFFEEF0)
: Colors.white,
filled: !correct,
counterText: "",
border: defaultInputBorder,
focusedBorder: !correct &&
!_wordCompleted &&
_editingControllers[index].text.isNotEmpty
? incorrectInputBorder
: focusedInputBorder,
enabledBorder: _wordCompleted
? focusedInputBorder
: correct
? correctInputBorder
: defaultInputBorder,
errorBorder: defaultInputBorder,
disabledBorder: _wordCompleted
? focusedInputBorder
: correct
? correctInputBorder
: defaultInputBorder,
contentPadding: const EdgeInsets.only(left: 2)),
),
Text entered in my TextField widget disappears when I remove the keyboard from the view.
There are two TextField's, title and description. The above problem only occurs for the title but not with the description.
Here's the relevant excerpt from the build method:
#override
Widget build(BuildContext context) {
_note = widget._note; // This is coming from StatefulWidget Class above
TextStyle textStyle = Theme.of(context).textTheme.title;
_titleController.text = _note.title;
_descriptionController.text = _note.description;
return Scaffold(
body: ListView(
children: <Widget>[
Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
style: textStyle,
controller: _titleController,
decoration: InputDecoration(
labelText: "Title",
labelStyle: textStyle,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0))),
),
),
Padding(
padding: EdgeInsets.all(15.0),
child: TextField(
style: textStyle,
controller: _descriptionController,
decoration: InputDecoration(
labelText: "Description",
labelStyle: textStyle,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0))),
),
),
...
}
}
Screenshots of Keyboard shown & removed.
This occurs because you are setting the text in your build method. This build method can get invoked at any time, e.g. when the keyboard is contracted because the UI needs to react to that.
This means that you should move this code to initState:
#override
void initState() {
_note = widget._note;
_titleController.text = _note.title;
_descriptionController.text = _note.description;
super.initState();
}
initState is only called once when the your widget is inserted into the build tree.
I am not sure why this only happens with one of the TextFields's. I assume that you are using the TextController's somewhere else to set the Note's content, which could cause this behavior.
Furthermore, you should probably avoid using a leading underscore _ for _note in your StatefulWidget (widget._note) as you access it from your State.