flutterdart

Flutter - Textfield lose focus when text is visible


In my project I made a textfield for the password, and I made a method to show and hide the password. What happens is that when I press the icon to show the text, the keyboard and the cursor disappear, I mean, the focus is lost.

How do I make that when I press the icon it doesn't lose the focus? Thanks

class ExampleLoginFocus extends StatefulWidget {
  @override
  _ExampleLoginFocusState createState() => _ExampleLoginFocusState();
}

class _ExampleLoginFocusState extends State<ExampleLoginFocus> {
  FocusNode focusPass;

  var pass = '';
  bool passwordVisible;

  @override
  void initState() {
    super.initState();
    passwordVisible = true;
    focusPass = FocusNode();
  }

  Widget password() {
    return TextField(
      obscureText: passwordVisible,
      focusNode: focusPass,
      decoration: InputDecoration(
        suffixIcon: Padding(
          padding: EdgeInsets.only(top: 20, bottom: 0),
            child: IconButton(
          icon: Icon(
            passwordVisible ? Icons.visibility_off : Icons.visibility,
            color: Theme.of(context).primaryColorDark,
          ),
          onPressed: () {
            setState(() {
              passwordVisible = !passwordVisible;
            });
          },
        )),
      ),
      onChanged: (String tex) {
        pass = tex;
        print(pass);
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: password(),
      ),
    );
  }
}

Solution

  • I can't test your code but I have a custom text field widget that allows using a property to define if is a common text field or a password text field, maybe this helps you

    class DnetTextField extends StatefulWidget {
      final String label;
      final bool isPasswordField;
      final TextEditingController controller;
      final TextInputType textInputType;
      final TextInputAction textInputAction;
      final FormFieldValidator validator;
      final IconData icon;
      final Color color;
      final Color errorColor;
      final BuildContext context;
      final FocusNode currentFocus;
      final FocusNode nextFocus;
      final bool autoValidate;
      final double width;
      final ValueChanged<String> onFieldSubmitted;
      final List<TextInputFormatter> inputFormatters;
      final TextCapitalization textCapitalization;
      final int maxLength;
      final bool focusable;
      final String hint;
    
      DnetTextField({
        @required this.context,
        @required this.label,
        @required this.controller,
        this.icon,
        this.color,
        this.errorColor,
        this.textInputType = TextInputType.text,
        this.textInputAction = TextInputAction.done,
        this.validator,
        this.isPasswordField = false,
        this.currentFocus,
        this.nextFocus,
        this.onFieldSubmitted,
        this.autoValidate = false,
        this.width = 266,
        this.inputFormatters,
        this.textCapitalization = TextCapitalization.none,
        this.maxLength = -1,
        this.focusable = true,
        this.hint,
      });
    
      @override
      DnetTextFieldState createState() {
        return DnetTextFieldState();
      }
    }
    
    class DnetTextFieldState extends State<DnetTextField> {
      bool hasErrors = false;
      bool passwordVisible = false;
    
      Color get currentColor => hasErrors ? widget.errorColor : widget.color;
    
      IconData get currentIcon {
        if (widget.isPasswordField) if (passwordVisible)
          return Icons.visibility_off;
        else
          return Icons.visibility;
        else
          return widget.icon;
      }
    
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        final border = OutlineInputBorder(
          borderRadius: BorderRadius.all(Radius.circular(10.0)),
          borderSide: BorderSide(color: currentColor),
        );
    
        final focusableTheme = widget.focusable
            ? Theme.of(context)
            : Theme.of(
                context,
              ).copyWith(
                splashFactory: const NoSplashFactory(),
              );
    
        return Container(
          width: widget.width,
          height: 80,
          child: Theme(
            data: focusableTheme,
            child: TextFormField(
              inputFormatters: widget.inputFormatters,
              style: TextStyle(color: currentColor),
              controller: widget.controller,
              keyboardType: widget.textInputType,
              textInputAction: widget.textInputAction,
              enableInteractiveSelection: widget.focusable,
              focusNode:
                  widget.focusable ? widget.currentFocus : DnetDisabledFocusNode(),
              decoration: InputDecoration(
                hintText: widget.hint,
                suffixIcon: getSuffixIcon(),
                fillColor: currentColor,
                labelText: widget.label,
                labelStyle:
                    TextStyle(color: currentColor, fontStyle: FontStyle.italic),
                focusedBorder: border,
                enabledBorder: border,
                border: border,
              ),
              autovalidate: widget.autoValidate,
              validator: (text) {
                final validatorMessage =
                    widget.validator == null ? null : widget.validator(text);
                setHasErrors(validatorMessage != null);
                if (validatorMessage != null) return validatorMessage;
              },
              onFieldSubmitted: widget.onFieldSubmitted ??
                  (term) {
                    if (widget.nextFocus != null) {
                      FocusScope.of(context).requestFocus(widget.nextFocus);
                    }
                  },
              obscureText: widget.isPasswordField && !passwordVisible,
              textCapitalization: widget.textCapitalization,
              maxLength: widget.maxLength != -1 ? widget.maxLength : null,
            ),
          ),
        );
      }
    
      setHasErrors(bool errors) {
        setState(() {
          hasErrors = errors;
        });
      }
    
      setPasswordVisible(bool visible) {
        setState(() {
          passwordVisible = visible;
        });
      }
    
      GestureDetector getSuffixIcon() {
        if (widget.icon != null || widget.isPasswordField) {
          return GestureDetector(
            child: Icon(
              currentIcon,
              color: currentColor,
            ),
            onTap: () {
              if (widget.isPasswordField) setPasswordVisible(!passwordVisible);
            },
          );
        } else {
          return null;
        }
      }
    }