Flutter - Textfield reaction when focus changes - flutter

I've made a program where the user can create an account, but I've placed some restrictions on the textfields (like for the First Name, it can only accept a string made of A-Z or a-z).
I want an "error text" to show under the textfield if the input is wrong (like if the user types "John1"), but I don't know how to do this. The simple way would be to use the onEditingComplete but this only works if the user taps on the keyboard the "done" key.
Is there a way to make the "error text" appear when the focus changes from one textfield to another?

You can use the validator property of the textfield to do this. It is recommended to encapsulate your fields in a Form() widget and then use the FormTextField() widget to implement your validations, this will enable an error to be shown at the bottom of the text field, if focus changes, or submission is attempted.
Example:
TextFormField(
onSaved: (String value) {},
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
)
The error will appear below in red fonts.

If you want to show error text whenever text/value changed you can do this.
In TextField 'onChanged' validate the user input using some regex and check for error ,if there is an error using TextField decoration 'errorText' property you can show required error.
Widget phoneNumberTextField() {
return Padding(
padding: EdgeInsets.fromLTRB(25, 10, 25, 10),
child: TextField(
style: TextStyle(color: Colors.white),
controller: _phoneNumberController,
maxLength: Constants.PhoneNumberTextFieldMaxLength,
onChanged: (value) {
_phoneNumberErrorText =
validatePhoneNumberTextField(_phoneNumberController.text);
_hasPhoneNumberError = _phoneNumberErrorText.isNotEmpty;
setState(() {});
},
keyboardType: TextInputType.number,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green, width: 1.0),
),
errorText: _hasPhoneNumberError ? _phoneNumberErrorText : null),
),
);
}
String validatePhoneNumberTextField(String text) {
String errorText = '';
if (text.length < 1) {
errorText = 'Phone Number should not be empty';
} else if (text.length != 10) {
errorText = 'Please enter valid phone number.';
}
return errorText;
}

Related

Flutter TextFormField with non-const decoration

I want to write something like a function that generates TextFormFields with different names, but I don't want the labelText attribute to be const. That way I can easily rubber stamp out a bunch of similar fields that have different names.
For example
TextFormField myFormField(myMapType myMap, String fieldName) {
return TextFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: fieldName,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your $fieldName';
}
return null;
},
initialValue: myMap.attribute[fieldName],
onSaved: (val) {
setState(
() {
myMap.setAttribute(fieldName, val!);
},
);
});
}
But that gives an error "Invalid constant value" at the line "labelText: fieldName". What's the trick needed to accomplish what I'm trying to do? Or what dumb mistake did I make?
fieldName will get on runtime rather than compile time. Therefore you cant use const
You can do
return TextFormField(
decoration: InputDecoration(
border: const OutlineInputBorder(), //this can be const
labelText: fieldName,
),
validator: (value) {
You can check What is the difference between the "const" and "final" keywords in Dart?

How to make TextField which has 2 hintText?

I have ui of text field from Figma.
There are 2 hint texts where 1st hintText is located at the beginning of textfield and other one is at the end.
As can be seen, trailing is hint text which is disappearing when user inputs number.
If you have any idea please share how to make this kind of text field)
Try this:
TextField(
textAlign: TextAlign.end,
decoration: InputDecoration(
prefixIcon: Padding(
padding: EdgeInsets.all(15), child: Text('Prefix')),
border: OutlineInputBorder(),
hintText: "Cym",
),
),
You can just use the trailing for the hint and can you onChanged method to handle changes.. Like you will be managing your text on the behavior of input. For example if the input is empty then you text will be something like "A" if you input something text will be changed.
TextFormField(
decoration: InputDecoration(
suffix: Text(text)
),
onChanged: (value) {
// handle or change your text here
if(value.isEmpty){
setState(() { text = "A"});
}
else{
setState(() { myState = "any text"});
}
},
)

Flutter TextField - How to support submission on <ENTER> _and_ newline on <SHIFT-ENTER>

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

Showing an error under a TextField in Flutter

Material design allows a text field to indicate an error via a small red label under the input box: https://material.io/components/text-fields (see screenshot below).
Is there a way to achieve this for a TextField field in Flutter? I expected this to be a property of either TextField or TextEditingController, but haven't found anything like it.
It is present in the decoration property in TextField, also you can style it using it's style property.
TextField(
decoration: InputDecoration(
errorStyle: TextStyle(),
errorText: 'Please enter something'
),
),
You show errors based on the validation results which are returned by the validator function provided by TextFormField, You check for some conditions there and return an error message or null based on what you want to show and when, or if you don't want to show anything.
child: new TextFormField(
autocorrect: false,
validator: (value) {
if (value.isEmpty) {
return 'Error Message';
}
return null;
},
onSaved: (val) => //do something...,
decoration: new InputDecoration(labelText: "Label*"),
),

Show error text of widget on button click

I have below code which needs to be get called on button click to show error Text, as condition failed on button click.
Error Text:
final confirmPassword = TextFormField(
controller: widget.confirmPasswordController,
obscureText: true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_open, color: Colors.grey),
hintText: 'Confirm Password',
errorText: validatePassword(widget.confirmPasswordController.text)
? "Password should contains more then 5 character"
: null,
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
);
Button Click:
onPressed: () {
if (validateEmail(widget.emailController.text) &&
validatePassword(widget.passwordController.text) &&
validatePassword(widget.confirmPasswordController.text)) {
// launch new screen
} else {
// show the error text as above checks failed
}
}
How we can achive this? will setState() help us?
You can achieve it by use Form widget with key. Like
Declare
GlobalKey<FormState> _globalFormKey = GlobalKey();
and set it to
Form(
key: _globalFormKey,
.
.
.
child:
)
Here you can use child as TextFormField and write on button click failure.
_globalFormKey.currentState.validate()
For more info Form widget
~PS: Instead of check validation out of TextFormField, It has own property validator. Search and use it.
Using a Form widget.
Flutter has a good topic about it with complete examples: Build a form with validation