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