I'm trying to create a PasswordField as a TextFormField, that has a suffix, which should allow to toggle the visibility of the input string. The issue I'm receiving is that, I'm able to toggle obscureText between true and false, but the rerendering doesn't happen. I hope someone can help me with this issue. I will add the code down below
class _WFPasswordInputState extends State<WFPasswordInput> {
TextEditingController? controller;
TextFormField? formField;
late bool obscureText;
late FocusNode _focusNode;
final AppTheme style = AppTheme();
@override
void initState() {
_focusNode = FocusNode();
obscureText = widget.obscureText;
super.initState();
}
@override
void dispose() {
_focusNode.dispose();
formField!.controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
String formatted = widget.value.value?.toString() ?? '';
if (formField == null) {
TextEditingController controller;
if (widget.controller == null) {
controller = TextEditingController(text: formatted);
} else {
controller = widget.controller!;
}
formField = createTextFormField();
} else {
Future.delayed(
Duration.zero,
() {
if (formField!.controller?.text !=
(widget.value.display == null
? null
: widget.value.value.toString())) {
String formatted = widget.value.value!;
if (formField!.controller!.text.isEmpty) {
formatted = "";
}
formField!.controller?.text = formatted;
formField!.controller?.selection = TextSelection(
baseOffset: formatted.length,
extentOffset: formatted.length,
);
}
},
);
}
return formField!;
}
TextFormField createTextFormField() {
return TextFormField(
enabled: widget.enabled,
controller: controller,
keyboardType: widget.inputType,
focusNode: widget.focusNode,
obscureText: obscureText,
textAlign: widget.textAlignment,
inputFormatters: widget.inputFormatters,
maxLines:
widget.inputType == TextInputType.multiline ? widget.maxLines : 1,
minLines: widget.minLines,
style: widget.textStyle ??
style.textStyles.bodyText1!
.copyWith(color: Colors.white.withOpacity(0.5)),
decoration: CustomInputDecoration(
hintStyle: AppTheme()
.textStyles
.bodyText1!
.copyWith(color: Colors.white.withOpacity(0.5)),
hintText: widget.hintText,
suffix: passwordIcons(),
contentPadding: style.px4y2),
onChanged: (value) {
widget.value.value = value;
},
validator: widget.validator,
);
}
Widget passwordIcons() {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
setState(() {
obscureText = !obscureText;
print(obscureText);
});
},
icon: Icon(
obscureText ? Icons.visibility : Icons.visibility_off,
color: style.white.withOpacity(0.5),
),
color: style.white,
),
IconButton(
onPressed: showPasswordAlert,
icon: const Icon(Icons.info_outline),
color: style.white,
),
],
);
}
}
so how this basically should work is that i call
obscureText = widget.obscureText;
in the initState method. and everytime i click on the IconButton in the passwordIcons
Widget setState should be called and rerender it based on the obscureText value. But I just don't see the error.
Thank you in advance.
Solution 1: Peter Koltai
The problem with your approach is that you create formField
only once with this code:
if (formField == null) {
...
formField = createTextFormField();
}
You are forcing the framework not to rebuild this widget, but the whole point of using setState
is the rebuild.
This way Flutter framework will not update this widget upon calling setState
. Instead, add this widget to your widget tree directly, and setState
will work. Instead of this:
return formField!;
try this:
return TextFormField(
enabled: widget.enabled,
controller: controller,
keyboardType: widget.inputType,
focusNode: widget.focusNode,
obscureText: obscureText,
...