Jaydip Pawar
Jaydip Pawar

Reputation: 135

How to visible/hide password in flutter?

I have created a login screen for my app but in password field I want a functionality like when I type password its been in * format and in right side a icon on when user click on it, password will be visible, I created a code for it but when I click on password field that icon getting invisible and when password field loose focus that icon appearing again, then how to always show that icon even password field is in focus? I have provided a snapshot to easily understand the problem.

enter image description here

enter image description here

Here are my login screen code....

import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
import 'package:secret_keeper/screens/home_screen/Home.dart';
import 'package:secret_keeper/screens/home_screen/passwords/PasswordsNavigation.dart';
import 'package:secret_keeper/screens/signup_page/SignupPage.dart';

class LoginPage extends StatefulWidget{
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {

  String _emailID, _password = "",_email = "[email protected]", _pass = "Try.t.r.y@1";
  bool _obscureText = true;
  final _formKey = GlobalKey<FormState>();
  
  void _toggle(){
    setState(() {
      _obscureText = !_obscureText;
    });
  }

  void validateLogin(){
    if(_formKey.currentState.validate()){
      _formKey.currentState.save();
      if(_emailID == _email && _password == _pass){
        Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Home()));
      }
    }
  }

  Widget emailInput(){
    return TextFormField(
      keyboardType: TextInputType.emailAddress,
      decoration: InputDecoration(
        labelText: "Email ID",
        labelStyle: TextStyle(fontSize: 14,color: Colors.grey.shade400),
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(10),
          borderSide: BorderSide(
            color: Colors.grey.shade300,
          ),
        ),
        focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(10),
            borderSide: BorderSide(
              color: Colors.red,
            )
        ),
      ),
      validator: (email) {
        if (email.isEmpty)
          return 'Please Enter email ID';
        else if (!EmailValidator.validate(email))
          return 'Enter valid email address';
        else
          return null;
      },
      onSaved: (email)=> _emailID = email,
      textInputAction: TextInputAction.next,
    );
  }

  Widget passInput(){
    return TextFormField(
      keyboardType: TextInputType.text,
      decoration: InputDecoration(
        labelText: "Password",
        labelStyle: TextStyle(fontSize: 14,color: Colors.grey.shade400),
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(10),
          borderSide: BorderSide(
            color: Colors.grey.shade300,
          ),
        ),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(10),
          borderSide: BorderSide(
            color: Colors.red,
          )
        ),
        suffixIcon: IconButton(
          icon: Icon(
            _obscureText ? Icons.visibility : Icons.visibility_off,
          ),
          onPressed: _toggle,
        ),
      ),
      validator: (password){
        Pattern pattern =
            r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#\$&*~]).{8,}$';
        RegExp regex = new RegExp(pattern);
        if (password.isEmpty){
          return 'Please Enter Password';
        }else if (!regex.hasMatch(password))
          return 'Enter valid password';
        else
          return null;
      },
      onSaved: (password)=> _password = password,
      textInputAction: TextInputAction.done,
      obscureText: _obscureText,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: false,
      backgroundColor: Colors.white,
      body: SafeArea(
        child: Container(
          padding: EdgeInsets.only(left: 16,right: 16),
          child: Form(
            key: _formKey,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    SizedBox(height: 50,),
                    Text("Welcome,",style: TextStyle(fontSize: 26,fontWeight: FontWeight.bold),),
                    SizedBox(height: 6,),
                    Text("Sign in to continue!",style: TextStyle(fontSize: 20,color: Colors.grey.shade400),),
                  ],
                ),
                Column(
                  children: <Widget>[
                    emailInput(),
                    SizedBox(height: 16,),
                    passInput(),
                    SizedBox(height: 12,),
                    Align(
                      alignment: Alignment.topRight,
                      child: Text("Forgot Password ?",style: TextStyle(fontSize: 14,fontWeight: FontWeight.w600),),
                    ),
                    SizedBox(height: 30,),
                    Container(
                      height: 50,
                      width: double.infinity,
                      child: FlatButton(
                        onPressed: validateLogin,
                        padding: EdgeInsets.all(0),
                        child: Ink(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(6),
                            gradient: LinearGradient(
                              begin: Alignment.centerLeft,
                              end: Alignment.centerRight,
                              colors: [
                                Color(0xffff5f6d),
                                Color(0xffff5f6d),
                                Color(0xffffc371),
                              ],
                            ),
                          ),
                          child: Container(
                            alignment: Alignment.center,
                            constraints: BoxConstraints(maxWidth: double.infinity,minHeight: 50),
                            child: Text("Login",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),textAlign: TextAlign.center,),
                          ),
                        ),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(6),
                        ),
                      ),
                    ),
                    SizedBox(height: 16,),
                    Container(
                      height: 50,
                      width: double.infinity,
                      child: FlatButton(
                        onPressed: (){},
                        color: Colors.indigo.shade50,
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(6),
                        ),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Image.asset("assets/images/facebook.png",height: 18,width: 18,),
                            SizedBox(width: 10,),
                            Text("Connect with Facebook",style: TextStyle(color: Colors.indigo,fontWeight: FontWeight.bold),),
                          ],
                        ),
                      ),
                    ),
                    SizedBox(height: 16,),
                    Container(
                      height: 50,
                      width: double.infinity,
                      child: FlatButton(
                        onPressed: (){},
                        color: Colors.indigo.shade50,
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(6),
                        ),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Image.asset("assets/images/facebook.png",height: 18,width: 18,),
                            SizedBox(width: 10,),
                            Text("Connect with Facebook",style: TextStyle(color: Colors.indigo,fontWeight: FontWeight.bold),),
                          ],
                        ),
                      ),
                    ),
                  ],
                ),
                Padding(
                  padding: EdgeInsets.only(bottom: 10),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Text("Don't have an account?",style: TextStyle(fontWeight: FontWeight.bold),),
                      SizedBox(width: 5,),
                      GestureDetector(
                        onTap: (){
                          Navigator.push(context, MaterialPageRoute(builder: (context){
                            return SignupPage();
                          }));
                        },
                        child: Text("Sign up",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.red),),
                      )
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Upvotes: 1

Views: 11760

Answers (5)

Taqi Tahmid Tanzil
Taqi Tahmid Tanzil

Reputation: 330

Full Example.main login here is:

  1. take a boolean param for detecting if text is obscure or not
  2. change suffix icon based on that boolean value
  3. change the boolean value on suffix item click

below i gave a full example for the task.

bool _passwordInVisible = true; //a boolean value

TextFormField buildPasswordFormField() {
return TextFormField(
  obscureText: _passwordInVisible,
  onSaved: (newValue) => registerRequestModel.password = newValue,
  onChanged: (value) {
    if (value.isNotEmpty) {
      removeError(error: kPassNullError);
    } else if (value.length >= 8) {
      removeError(error: kShortPassError);
    }
    return null;
  },
  validator: (value) {
    if (value.isEmpty) {
      addError(error: kPassNullError);
      return "";
    } else if (value.length < 8) {
      addError(error: kShortPassError);
      return "";
    }
    return null;
  },
  textInputAction: TextInputAction.next,
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    labelText: "Password",
    hintText: "Enter your password",
    contentPadding: new EdgeInsets.symmetric(vertical: 5.0, horizontal: 15.0),
    floatingLabelBehavior: FloatingLabelBehavior.always,
    suffixIcon: IconButton(
      icon: Icon(
        _passwordInVisible ? Icons.visibility_off : Icons.visibility, //change icon based on boolean value
        color: Theme.of(context).primaryColorDark, 
      ),
      onPressed: () {
        setState(() {
          _passwordInVisible = !_passwordInVisible; //change boolean value
        });
      },
    ),
  ),
);

}

Upvotes: 3

Rashpinder Singh
Rashpinder Singh

Reputation: 119

Here two scenarios come

  1. If you want your suffix icon color constant like always grey, you can give color property of icon, like :

button for password show hide

var passShowButton = GestureDetector(
      onLongPressEnd: outContact,
      onTapDown: inContact, //call this method when incontact
      onTapUp:
          outContact, //call this method when contact with screen is removed
      child: Icon(
        getXHelper.isEmailPasswordUpdate.isTrue
            ? AppIcons.hidePassword
            : AppIcons.hidePassword,
        size: 18,
        color:Colors.grey
      ),
    );

TextField

TextField(
    obscureText: getXHelper.isPassInvisible  ,
    autocorrect: false,
    textAlignVertical: TextAlignVertical.bottom,
    decoration: InputDecoration(
      enabled: true,
      errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      border: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      fillColor: Colors.white,
      filled: true,
      isDense: true,
      prefixIconConstraints: BoxConstraints(maxHeight: 18, minHeight: 18),
      hintText: "Password",
      prefixIcon: Padding(
        padding: const EdgeInsets.only(top: 0, right: 12, bottom: 0),
        child: Icon(Icons.lock, size: 18, color: Colors.grey),
      ),
      suffixIcon: passShowButton,
    ),
    cursorColor: Colors.black,
    style: TextStyle(
        color: Colors.black, fontFamily: AppFontFamily.fontFamily),
  )
  1. If you want your suffix icon color of your app-Primary Color, will change color when textfield focus, like :

Password show hide button

var passShowButton = GestureDetector(
          onLongPressEnd: outContact,
          onTapDown: inContact, //call this method when incontact
          onTapUp:
              outContact, //call this method when contact with screen is removed
          child: Icon(
            getXHelper.isEmailPasswordUpdate.isTrue
                ? AppIcons.hidePassword
                : AppIcons.hidePassword,
            size: 18,
          ),
        );

TextField(
    obscureText: getXHelper.isPassInvisible  ,
    autocorrect: false,
    textAlignVertical: TextAlignVertical.bottom,
    decoration: InputDecoration(
      enabled: true,
      errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      border: UnderlineInputBorder(
          borderSide: BorderSide(color: AppColors.primary)),
      fillColor: Colors.white,
      filled: true,
      isDense: true,
      prefixIconConstraints: BoxConstraints(maxHeight: 18, minHeight: 18),
      hintText: "Password",
      prefixIcon: Padding(
        padding: const EdgeInsets.only(top: 0, right: 12, bottom: 0),
        child: Icon(Icons.lock, size: 18),
      ),
      suffixIcon: passShowButton,
    ),
    cursorColor: Colors.black,
    style: TextStyle(
        color: Colors.black, fontFamily: AppFontFamily.fontFamily),
  )

Upvotes: 1

Jaydip Pawar
Jaydip Pawar

Reputation: 135

Actually, the suffix icon is visible but when i click on TextFormField then the icon color is changing to white so simply in icon field i added a color property and give a black color to icon so even textfield is in focus its color remain black.

Upvotes: 0

lordvidex
lordvidex

Reputation: 4098

I just copied and run your code and it works just fine. The icon was visible when the password field had focus. You should maybe check your flutter version or it might probably be your emulator device.
flutter --version
Flutter 1.22.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 8874f21e79 (2 weeks ago) • 2020-10-29 14:14:35 -0700
Engine • revision a1440ca392
Tools • Dart 2.10.3
I have focus and it works

Upvotes: 0

Sajad Abdollahi
Sajad Abdollahi

Reputation: 1741

you can put text field and icon button in a stack replace this code with your password textfield. you can change icon button position to what you want.

Stack(
      children: [
        TextFormField(
          keyboardType: TextInputType.text,
          decoration: InputDecoration(
            labelText: "Password",
            labelStyle:
                TextStyle(fontSize: 14, color: Colors.grey.shade400),
            enabledBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(10),
              borderSide: BorderSide(
                color: Colors.grey.shade300,
              ),
            ),
            focusedBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(10),
                borderSide: BorderSide(
                  color: Colors.red,
                )),
          ),
          validator: (password) {
            Pattern pattern =
                r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#\$&*~]).{8,}$';
            RegExp regex = new RegExp(pattern);
            if (password.isEmpty) {
              return 'Please Enter Password';
            } else if (!regex.hasMatch(password))
              return 'Enter valid password';
            else
              return null;
          },
          onSaved: (password) => _password = password,
          textInputAction: TextInputAction.done,
          obscureText: _obscureText,
        ),
        Positioned(
          top: 2,
          right: 10,
          child: IconButton(
              icon: Icon(
                _obscureText ? Icons.visibility : Icons.visibility_off,
              ),
              onPressed: () {
                setState(() {
                  _obscureText = !_obscureText;
                });
              }),
        ),
      ],
    ),

Upvotes: 0

Related Questions