(Flutter) TextFormField Change labelColor on Focus - flutter

I am trying to change my labelText color when focused. I can change the text color but not when focused.
I have tried all the hint text colors and label text colors, but nothing helps.
Container(
padding: EdgeInsets.fromLTRB(15, 10, 15, 0),
child: TextFormField(
cursorColor: Colors.lightGreen,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'Phone Number',
hintText: 'Enter a Phone Number',
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightGreen
)
),
border: OutlineInputBorder(
borderSide: BorderSide()
),
)
),
),
Here are images of what is happening:

You'd need to have a way determine its focus state and then create a conditional for its color based off of that. This is where a focusNode would be helpful. Construct a new FocusNode off the widget creation, use that as the focusNode property in the TextFormField. Then in color property of the TextStyle property of the TextFormField you could add something like:
FocusNode myFocusNode = new FocusNode();
...
return TextFormField(
focusNode: myFocusNode,
decoration: InputDecoration(
labelText: 'test',
labelStyle: TextStyle(
color: myFocusNode.hasFocus ? Colors.blue : Colors.black
)
),
);
EDIT : Just a quick note, you'll probably need to make sure this is in a StatefulWidget and then add a listener to the focusNode you created and call setState on any events on that focusNode. Otherwise you wont see any changes.

The Summary
You might want to check out Flutter Cookbook's Focus and text fields recipe.
Essentially, we have to:
Create a FocusNode property.
Add initialization and disposal to it.
Add it to the TextFormField.
Add a focus request on every tap on the TextFormField.
1. Create a FocusNode property
class CustomTextFormFieldState extends State<CustomTextFormField> {
FocusNode _focusNode;
...
2. Add initialization and disposal to it
#override
void initState() {
super.initState();
_focusNode = FocusNode();
}
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
3. Add it to the TextFormField
#override
Widget build(BuildContext context) {
return TextFormField(
focusNode: _focusNode,
...
4. Add a focus request on every tap on the TextFormField
Don't forget to use setState:
void _requestFocus(){
setState(() {
FocusScope.of(context).requestFocus(_focusNode);
});
}
Add the method to the TextFormField's onTap property:
#override
Widget build(BuildContext context) {
return TextFormField(
focusNode: _focusNode,
onTap: _requestFocus,
...

Use themes, this would only have to be done once in a central place:
inputDecorationTheme: InputDecorationTheme(
floatingLabelStyle: TextStyle(color: Colors.blue),
),

A quick solution is changing the primarySwatch from the theme of the Widget MaterialApp. The only disadvantage is that a Material Color is required.

I solve this problem with a Focus widget. First I defined a Color-Variable for each Field:
final _lowColor = Colors.amber[50]; // use your own colors
final _highColor = Colors.amber[200];
Color _field1Color = _lowColor;
Color _field2Color = _lowColor;
...
Then I wrapped each TextFormField with a Focus Widget and change the fieldColor:
child: Focus(
onFocusChange: (hasFocus) {
setState(() => _field1Color = hasFocus ? _highColor : _lowColor);
},
child: TextFormField(
...
color: _field1Color,
...
),
),
),

Here is a quick method without using stateful widget
return Theme( // 1) wrap with theme widget
data: Theme.of(context).copyWith(primaryColor: //2) color you want here)
child: TextFormField(
focusNode: myFocusNode,
decoration: InputDecoration(
labelText: 'test',
),
),
);

https://api.flutter.dev/flutter/material/InputDecoration/labelStyle.html
labelStyle: MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
final Color color = states.contains(MaterialState.focused)
? Colors.pink
: Colors.orange;
return TextStyle(color: color);
}),

for active/click
floatingLabelStyle: TextStyle(color: Colors.yellow),
for inactive
labelStyle: TextStyle(color: Colors.black),
below is complete example
TextField(
decoration: InputDecoration(
hintText: 'Verify Password',
focusColor: appColor,
labelText: "Verify Password",
labelStyle: TextStyle(color: Colors.black),
floatingLabelStyle: TextStyle(color: appColor),
floatingLabelBehavior: FloatingLabelBehavior.always
),
cursorColor: appColor,
)

Update
As mentioned by Guilherme in the comment, the later version of Flutter uses different logic to get the color
Color _getActiveColor(ThemeData themeData) {
if (isFocused) {
return themeData.colorScheme.primary;
}
return themeData.hintColor;
}
Source
You will now need to set it from the colorScheme instead
ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
primary: activeColor,
),
)
Original answer
After digging the source code for the InputDecorator used to determine the label color, here's what I found.
TextStyle _getFloatingLabelStyle(ThemeData themeData) {
final Color color = decoration.errorText != null
? decoration.errorStyle?.color ?? themeData.errorColor
: _getActiveColor(themeData);
final TextStyle style = themeData.textTheme.subtitle1.merge(widget.baseStyle);
return style
.copyWith(color: decoration.enabled ? color : themeData.disabledColor)
.merge(decoration.labelStyle);
}
Color _getActiveColor(ThemeData themeData) {
if (isFocused) {
switch (themeData.brightness) {
case Brightness.dark:
return themeData.accentColor;
case Brightness.light:
return themeData.primaryColor;
}
}
return themeData.hintColor;
}
In short, to change the label color, set the primaryColor light theme or accentColor for dark theme.
Another tip: To change the label color while it's not focused, set hintColor.
ThemeData.dark().copyWith(
primaryColor: Colors.red,
accentColor: Colors.white,
hintColor: Colors.pink,
)

One more clean way to do it with styles (you can add styles to the main theme and use for dark \ light modes)
TextFormField(
decoration: InputDecoration(
labelText: "some label",
labelStyle: const TextStyle(color: Colors.grey),
floatingLabelStyle: const TextStyle(color: Colors.blueAccent),
),),

you can also use labelStyle when the text field is in focus and if the text field is erroneous (caused by not following the validation)
labelText: 'Password',
labelStyle: TextStyle(
color: Colors.black54,
),
//when error has occured
errorStyle: TextStyle(
color: Colors.black,
),

I solved the problem using a StatefulWidget and Focus widget.
I use StatefulWidget because you need to use setState to notify the color change when focus on input
class InputEmail extends StatefulWidget {
#override
_InputEmailState createState() => _InputEmailState();
}
class _InputEmailState extends State<InputEmail> {
Color _colorText = Colors.black54;
#override
Widget build(BuildContext context) {
const _defaultColor = Colors.black54;
const _focusColor = Colors.purple;
return Container(
padding: EdgeInsets.symmetric(vertical: 15),
child: Focus(
onFocusChange: (hasFocus) {
// When you focus on input email, you need to notify the color change into the widget.
setState(() => _colorText = hasFocus ? _focusColor : _defaultColor);
},
child: TextField(
// Validate input Email
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'example#domain.com',
labelText: 'Email',
labelStyle: TextStyle(color: _colorText),
// Default Color underline
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black26),
),
// Focus Color underline
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.purple),
),
icon: Icon(
Icons.mail,
color: Colors.deepPurpleAccent,
),
),
),
),
);
}
}

If You want to change the primary color. Just simply define add the primaryColor of the Widget MaterialApp
const appPrimaryColor = Color(0xFF746DF7);
ThemeData theme() {
return ThemeData(
scaffoldBackgroundColor: Colors.white,
fontFamily: "Inter",
appBarTheme: appBarTheme(),
textTheme: textTheme(),
inputDecorationTheme: inputDecorationTheme(),
visualDensity: VisualDensity.adaptivePlatformDensity,
primaryColor: appPrimaryColor // <------ HERE
);
}
MaterialApp(
title: 'Flutter Demo',
theme: theme(), // <------ HERE
home: SplashScreen(),
routes: routes,
)

You can wrap the textfield with Theme and set the primary color to whatever you want the label color to be

There is a 'lableStyle' in 'decoration', just like:
labelText: 'Description',
labelStyle: TextStyle(
color: Colors.lightBlueAccent,
)),

There is a floatingLabelStyle parameter in the InputDecoration, you can use it like this:
decoration: InputDecoration(
labelText: "label",
hintText: "hint",
labelStyle: GoogleFonts.roboto(
color: color.labelColor,
),
floatingLabelStyle: GoogleFonts.roboto(
color: color.defaultGreen,
),),

Related

Design of TextFormField (focusColor and cursorColor)

Hey in my App I've got a few TextFormFields. I wanted to define their theme with a theme in MaterialApp(). I've read that the FocusColor is the secondary color, but in my case the focusColor is cyan, although I never defined that. Here is my Theme:
runApp(MaterialApp(
theme: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: globals.primaryColor,
secondary: globals.highlightColor,
background: globals.backgroundColor,
),
),
));
And here is my TextFormField:
TextFormField(
style: TextStyle(color: globals.darkText, fontWeight: FontWeight.bold),
decoration: textInputDecoration.copyWith(
hintText: "Name",
hintStyle: TextStyle(color: globals.backgroundColor, fontWeight: FontWeight.bold),
focusColor: globals.highlightColor,
fillColor: globals.primaryColor,
),
onSaved: (input) => _name = input,
),
In addition the cursor is always dark blue, I dont get where to change that color too.
Thank you for your answers!
Try using this:
textSelectionTheme: const TextSelectionThemeData(cursorColor: MyColor.kGreen)

Flutter SvgPicture in TextField not adapting color

When I set the TextField.prefixIcon property to an Icon(Icons.example) widget, the color of the icon automatically adapts according to the state of the TextField (enabled, focussed, ...).
Now, I got an Icon Pack with svg files that I want to use within the TextField. I am using flutter_svg to show them like that:
TextFormField(
decoration: InputDecoration(
hintText: "Password",
prefixIcon: SvgPicture.asset(
AppIcons.lock,
),
),
),
Now, how can I achieve the automatic color adaption with SvgPicture?
you need to resolve it manually :
FocusNode _focusNode = FocusNode();
bool _enabled = true;
#override
Widget build(BuildContext context) {
return TextFormField(
focusNode: _focusNode,
enabled: _enabled,
decoration: InputDecoration(
hintText: "password"
).copyWith(
prefixIcon: SvgPicture.asset(AppIcons.lock, color: _resolveColor())
),
);
}
Color _resolveColor(){
if (_focusNode.hasFocus){
return Colors.yellow;
}
if(!_enabled){
return Colors.white;
}
if (!_form.valid){
return Colors.red;
}
return Colors.grey;
}

Resetting TextfieldController for all textfields, using provider

I'm having a problem trying to figuring out the proper way on how to do this. Basically in my app, I want to reset all the fields for "cleanup" by the user. I can reset everything, but the TextFields. The only way that I found to solve the problem is by using the if that you can see inside the Consumer. I don't think though it's the proper way on how to handle this type of thing.
I also thought to push inside my provider class all the controller and then reset them, but I think it's still too heavy. I'm trying to find the cleanest and lightest solution, even to learn what's the best practice in these situations.
Thanks in advance!
return Provider.of<Provider_Class>(context, listen: false).fields[_label] != null ? SizedBox(
height: 57.5,
child: Consumer<Provider_Class>(builder: (context, provider, child) {
if (provider.resetted == true) {
_controller.text = "";
}
return Material(
elevation: this.elev,
shadowColor: Colors.black,
borderRadius: new BorderRadius.circular(15),
animationDuration: new Duration(milliseconds: 500),
child: new TextField(
focusNode: _focusNode,
keyboardAppearance: Brightness.light,
style: Theme.of(context).textTheme.headline5,
controller: _controller,
keyboardType: TextInputType.number,
textAlign: TextAlign.end,
inputFormatters: <TextInputFormatter>[
LengthLimitingTextInputFormatter(8),
_whichLabel(widget.label),
],
decoration: new InputDecoration(
enabledBorder: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(15),
borderSide: new BorderSide(width: 1.2, color: CliniLiliac300),
),
focusedBorder: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(15),
borderSide: new BorderSide(width: 2.5, color: CliniLiliac300),
),
filled: true,
fillColor: Colors.white,
hintText: "0.0",
hintStyle: new TextStyle(fontSize: 15, color: Colors.black, fontFamily: "Montserrat"),
),
onChanged: (val) {
var cursorPos = _controller.selection;
val = val.replaceAll(",", ".");
if (val == "") {
provider.fields[_label] = 0.0;
} else if (double.parse(val) > provider.measure[_label] &&
provider.measure[_label] != 0) {
provider.fields[_label] % 1 == 0
? _controller.text = provider.fields[_label].toString().split(".")[0]
: _controller.text = provider.fields[_label].toString();
if (cursorPos.start > _controller.text.length) {
cursorPos = new TextSelection.fromPosition(
new TextPosition(offset: _controller.text.length),
);
}
_controller.selection = cursorPos;
} else {
provider.fields[_label] = double.parse(val);
}
provider.calculateResultRA();
},
),
);
}),
) : SizedBox();
}
Use TextFormField instead of TextField. TextFormField has several callbacks like validator, onSaved, onChanged, onEditingComplete, onSubmitted, ...
You can connect all your TextFormFields by wrapping it in a Form. This form should be given a GlobalKey so that you can identify the Form and call methods on FormState.
final _form = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
// ...
return Form(
key: _form,
child: // build Material with TextFormFields
);
}
Now to call onSaved on each TextFormField, you can call _form.currentState().save(). To reset every TextFormField you can call _form.currentState().reset().
You can get more information about how to build a Form and the functions you can call on FomState here:
https://flutter.dev/docs/cookbook/forms/validation
https://api.flutter.dev/flutter/widgets/FormState-class.html

Change default TextFormField's icon color from grey to anything but show primary color on selection

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. 😄

Text in the TextField disappears when keyboard is removed

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.