Hey maybe someone can help me to figure out how implement proper form validation in Flutter.
So basically in InputDecoration we have
decoration: InputDecoration(
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.white, width: 3.0),
// ),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 3.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.greenAccent, width: 3.0),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 3.0),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white, width: 3.0),
),
labelText: 'Email',
// errorText: !_isValid ? "Invalid Email" : null,
labelStyle: TextStyle(color: Colors.white),
prefixIcon: Padding(
padding: const EdgeInsets.only(top: 10, left: 13),
child: FaIcon(
FontAwesomeIcons.at,
color: Colors.white,
size: 25,
),
),
),
So problem is that I can't understand how to set regular border on form when it is not touched , than when it's touched , on each input it has to validate input and set error if not valid. I know that on TextFormField i can pass onchange function and do the validation
onChanged: (value) {
if (value.isEmpty || !value.contains('#')) {
setState(() {
_isValid = false;
});
print('invalid');
} else {
setState(() {
_isValid = true;
});
}
with this approach I have issue, first I have to set initial value of _isValid and in this case when form is opened and not touched it's already are enabled or with error depending on initial value of _isValid.
I came from web and worked with Angular and there we have reactive forms, maybe someone explain how to achieve similar behavior in flutter?
validator: (value) {
if (enter code herevalue.isEmpty || !value.contains('#')) {
return "incorrect format";
}
1 create a global form key
2 warp your widget inside a Form widget
3 pass your global key to the key parameter of your Form widget
4 and use this line on button click
5 _yourFormKey.CurrentState.Validate();
Related
I have label in the top left corner of my TextField. I want the user to be able to see the hint always (and after that he filled the form), but it disappears when I tap.
#override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.never,
label: Align(
alignment: Alignment.topLeft,
child: Text(
_text,
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15),
)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(10))),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(10)),
)));
}
You can achieve this behavior by using the hintText property in the InputDecoration class. The hintText property provides a text hint that is displayed in the input field when it is empty. The hint text disappears as soon as the user starts typing and reappears if the text field is emptied. Here's the updated code:
#override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
hintText: _text,
floatingLabelBehavior: FloatingLabelBehavior.never,
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(10))
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(10)),
)
),
);
}
The material design suggest to use the labelText property on the InputDecoration. Maybe you have already tried and dismissed it.. but I'm checking just in case... have you tried that?
This will be the typical outcome:
If you do not want to go with hintText, you can make a stack where the first child is the ’TextField’ and the other is Positioned for your text.
I have a name form field in flutter app. There's a prefix icon in it. When I click the form field the color of icon changes to blue, rather I want to change it to green. How can I do that, can anyone please guide me? This is its code :
TextFormField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.green, width: 2.0),
borderRadius: BorderRadius.circular(10.0),
),
prefixIcon: const Icon(Icons.person),
hintText: "Name",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
),
);
Flutter's way to go is to use resolveWith. You can check the current state to check if the text field is focused, shows an error, etc. And depending on that you set the color.
From the docs (https://api.flutter.dev/flutter/material/InputDecoration-class.html):
final ThemeData themeData = Theme.of(context);
return Theme(
data: themeData.copyWith(
inputDecorationTheme: themeData.inputDecorationTheme.copyWith(
prefixIconColor:
MaterialStateColor.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.focused)) {
return Colors.green;
}
if (states.contains(MaterialState.error)) {
return Colors.red;
}
return Colors.grey;
}),
)),
child: TextFormField(
initialValue: 'abc',
decoration: const InputDecoration(
prefixIcon: Icon(Icons.person),
),
),
);
Use Theme color to change then define the focus node to determine when the field is on focus in order to apply these color changes
...
FocusNode _fieldNode = FocusNode(); //<-Define this then
...
TextFormField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.green, width: 2.0),
borderRadius: BorderRadius.circular(10.0),
),
prefixIcon: Icon(Icons.person,
color: _fieldNode.hasFocus
? Theme.of(context).primaryColor
: Colors.purple)),
hintText: "Name",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
),
);
I have a TextField with its textDirection set to rtl (Right-to-Left). When I select the TextField, I expect the cursor go to the end, as usual, but cursor goes to one position before the end.
TextField(
textDirection: TextDirection.rtl,
controller: widget.controller,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(8),
isDense: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2,
color: Theme.of(context).primaryColor,
),
borderRadius: BorderRadius.circular(8),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1.7,
color: Colors.grey.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(8),
),
),
),
);
How can I make the cursor appear at the end instead?
UPDATE: I realized that specifying controller in the TextField make the problem appear. but i need cotroller in this situation.
I think this because of each arabic character is encoded as 2 to 4 bytes.
Any way this code can do the trick
TextField(
onTap: (){
if(controller.selection == TextSelection.fromPosition(TextPosition(offset: controller.text.length -1))){
setState(() {
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
});
}
},
controller: controller,
...
);
You can control cursor position by using TextSelection.
I didn't test Arabic environment, but try this.
offset value means position of cursor, so test 0 or (widget.controller.text.length)
TextField(
textDirection: TextDirection.rtl,
controller: widget.controller
..selection = TextSelection.fromPosition(TextPosition(offset: 0)),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(8),
isDense: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2,
color: Theme.of(context).primaryColor,
),
borderRadius: BorderRadius.circular(8),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1.7,
color: Colors.grey.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(8),
),
),
),
);
You can try this action to move the cursor to the end of the text. Then place this controller in the controller in TextField.:
late TextEditingController _textEditingController;
#override
void initState() {
_textEditingController = TextEditingController(text: widget.text);
_textEditingController.selection = TextSelection.fromPosition(
TextPosition(offset: _textEditingController.text.length));
super.initState();
}
You can fix it by add this function on your textfield
onTap: () {
if (searchController.text[searchController.text.length - 1] != ' ') {
searchController.text = (searchController.text + ' ');
}
if (searchController.selection ==TextSelection.fromPosition(
TextPosition(offset:
searchController.text.length - 1))) {
setState(() {});
}
}
I want to change the background color of the TextFormField
when I selected the TextFormField to enter a value.
default : TextFormField background color is grey
on focus : TextFormField background color is blue.
What should I do?
const _lowColor = Colors.redAccent; // use your own colors
const _highColor = Colors.yellow;
Color _field1Color = _lowColor;
Focus(
onFocusChange: (hasFocus) {
setState(() =>
_field1Color = hasFocus ? _highColor : _lowColor)
);
},
child: TextFormField(
obscureText: (textType == "password") ? true : false,
controller: (controller != null) ? controller : null,
focusNode: _thisFocus,
validator: (value) => (value.isEmpty) ? "Enter the Text" : null,
style: _inputStyle,
onFieldSubmitted: (String v) => {
if (onFieldSubmitted != null) {
onFieldSubmitted(v)
}
},
decoration: InputDecoration(
labelText: placeHolder,
labelStyle: _labelStyle,
filled: true,
fillColor: _field1Color,
contentPadding: EdgeInsets.only(top: 4,bottom: 4,left: 6,right: 6),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xffE8E8E8), width: 1.0),
borderRadius: BorderRadius.circular(10)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xff3FC4FF), width: 1.0),
borderRadius: BorderRadius.circular(10)
)
),
),
),
I have tried to understand your questions and provide you with a better option to design. Do try it out and revert me how do you feel, you can customise it if you like.
Click here to see the design
Container inputBox(String tabName,bool hidePassword){
return Container(
height: 50,
margin: EdgeInsets.symmetric(vertical: 15.0, horizontal: 54.0,),
child: TextField(
obscureText: hidePassword,
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Color(0xFF579FAF), width: 2.0),
borderRadius: BorderRadius.circular(7.0),
),
labelText: tabName,
labelStyle: TextStyle(color: Colors.white),
contentPadding: EdgeInsets.symmetric(horizontal: 20),
focusedBorder:OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFF579FAF), width: 2.0),
borderRadius: BorderRadius.circular(7.0),
),
),
)
);
}
Resolved the problem.
I modified the background of TextField to change it using focus value.
fillColor: _focusNode.hasFocus ? _focusColor : _normalColor,
I cant get the textfield labeltext to show. anyone can help? and how do I get the border to always showing and not disappear when I click on the text field.
#override
Widget build(BuildContext context) {
return TextFormField(
controller: _controller,
decoration: InputDecoration(
border: new OutlineInputBorder(
borderSide: new BorderSide(width: 1)
),
// hintText: _hintText,
labelText: "Label Text",
contentPadding: EdgeInsets.only(left: 15),
// suffixText: _nameLength.toString() + "/ " + _maxLength.toString(),
// suffixStyle: TextStyle(fontSize: 12),
// suffixStyle: TextStyle(color: Theme.of(context).textTheme.bodyText2.color),
// counterText: "",
),
maxLines: _maxLines,
textCapitalization: TextCapitalization.sentences,
cursorRadius: Radius.circular(10),
keyboardType: TextInputType.text,
autofocus: true,
maxLength: _showTextCount ? _maxLength : null,
style: TextStyle(color: AppSetting.textColor1),
validator: (val) {
// if (val.isEmpty) {
// return 'Please enter text.';
// }
// return null;
},
onChanged: (val) {
_onChanged(val);
setState(() {
_inputFieldText = val;
_nameLength = _inputFieldText.length;
});
},
onTap: () => AppSetting.hapticFeeback(),
);
}
}
You should add
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.redAccent,
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: primaryColor,
width: 2.0,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black54,
width: 2.0,
),
),
),
Just make sure the default value of labelColor of the input decoration is different from background (the default label color was white in my case). In order to show border when clicked set focusedBorder property.