Reputation: 7024
Currently I have my password TextFormField
like this:
TextFormField(
decoration: const InputDecoration(
labelText: 'Password',
icon: const Padding(
padding: const EdgeInsets.only(top: 15.0),
child: const Icon(Icons.lock),
)),
validator: (val) => val.length < 6 ? 'Password too short.' : null,
onSaved: (val) => _password = val,
obscureText: true,
);
I want a button like interaction which will make password visible and invisible. Can I do it inside TextFormField
? Or I will have to make a Stack
widget to get my required UI. And how will the condition be made regarding obscureText
true/false?
Upvotes: 175
Views: 285321
Reputation: 17
This the code I used. It is much simpler and more effective. This uses a valueNotifier
value, _obscureTextNotifier
, and a listenerBuilder
, ValueListenableBuilder
. This way we can show hide the password in the textform
field.
class PasswordInput extends StatelessWidget {
final TextEditingController controller;
final ValueNotifier<bool> _obscureTextNotifier = ValueNotifier(true);
PasswordInput({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
spreadRadius: 2,
blurRadius: 10,
offset: const Offset(0.1, 2),
),
],
borderRadius: BorderRadius.circular(10),
),
child: ValueListenableBuilder<bool>(
valueListenable: _obscureTextNotifier,
builder: (context, obscureText, child) {
return TextFormField(
controller: controller,
obscureText: obscureText,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
labelText: 'Password',
labelStyle: TextStyle(
color: Colors.black
),
suffixIcon: IconButton(
onPressed: () {
_obscureTextNotifier.value = !obscureText;
},
icon: Icon(
obscureText ? Icons.visibility_off : Icons.visibility
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.transparent)
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.transparent)
)
),
);
}
),
);
}
}
Upvotes: 0
Reputation: 41
Once you have a state variable _passwordObscured in the widget, you can configure the TextFormField like this:
TextFormField(
obscureText: _passwordObscured,
decoration: InputDecoration(
hintText: 'Enter your password',
suffixIcon: IconButton(
onPressed: () {
setState(() {
_passwordObscured = !_passwordObscured;
});
},
icon: _passwordObscured
? Icon(Icons.visibility)
: Icon(Icons.visibility_off))),
)
Upvotes: 1
Reputation: 771
○ Just a Simple 3 Steps you can follow and password Show/Hide done.
Step 1:create variable
bool _isHidden = true;
Step 2: Magical Step, make the icon clickable and see/hide the password.
Now I will wrap the icon with InkWell which will make it clickable. So, when we will click on that it will toggle the obscureText the argument between true and false.
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).secondaryHeaderColor,
body: Center(
child: Container(
height: 55,
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextField(
obscureText: _isHidden,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
suffix: InkWell(
onTap: _togglePasswordView, /// This is Magical Function
child: Icon(
_isHidden ? /// CHeck Show & Hide.
Icons.visibility :
Icons.visibility_off,
),
),
/*icon: Icon(
Icons.password_sharp,
color: Colors.black,
),*/
),
),
),
),
);
}
Step 3: Create This the Magical Function.
void _togglePasswordView() {
setState(() {
_isHidden = !_isHidden;
});
}
Upvotes: 12
Reputation: 2397
Here's a customizable textformfield widget I made:
// curstom_text_form_field.dart
import 'package:flutter/material.dart';
class CustomTextFormField extends StatefulWidget {
final String? label;
final String? hint;
final String? errorMessage;
bool obscureText;
final bool hasObscuredText;
final TextInputType? keyboardType;
final Function(String)? onChanged;
final Function(String)? onFieldSubmitted;
final String? Function(String?)? validator;
final TextEditingController? controller;
CustomTextFormField({
super.key,
this.label,
this.hint,
this.errorMessage,
this.obscureText = false,
this.hasObscuredText = false,
this.keyboardType = TextInputType.text,
this.onChanged,
this.onFieldSubmitted,
this.validator,
this.controller,
});
@override
State<CustomTextFormField> createState() => _CustomTextFormFieldState();
}
class _CustomTextFormFieldState extends State<CustomTextFormField> {
void _togglePasswordView() {
setState(() {
widget.obscureText = !widget.obscureText;
});
}
@override
Widget build(BuildContext context) {
final border = OutlineInputBorder(borderRadius: BorderRadius.circular(40));
return TextFormField(
controller: widget.controller,
readOnly: false,
onChanged: widget.onChanged,
validator: widget.validator,
decoration: InputDecoration(
border: border,
isDense: true,
label: widget.label != null ? Text(widget.label!) : null,
hintText: widget.hint,
errorText: widget.errorMessage,
suffixIcon: widget.hasObscuredText
? Padding(
padding: const EdgeInsets.only(right: 8),
child: IconButton(
onPressed: _togglePasswordView,
icon: Icon(
widget.obscureText
? Icons.visibility
: Icons.visibility_off,
)),
)
: null,
),
obscureText: widget.obscureText,
);
}
}
Here's the result:
Upvotes: 1
Reputation: 818
This is my Widget with Getx
Widget build(BuildContext context) {
RxBool passwordVisible = false.obs;
return
Obx(() => TextFormField(
obscureText: !passwordVisible.value,
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.never,
suffixIcon: IconButton(
icon: Icon(
passwordVisible.value
? Icons.visibility
: Icons.visibility_off,
color: Colors.black,
size: 20,
),
onPressed: (){
passwordVisible.value = !passwordVisible.value;
},
),
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
), ),
));}
Upvotes: 1
Reputation: 34769
First make you widget StatefulWidget
if it is a StatelessWidget
.
Then have a variable bool _obscureText
and pass it to your TextFormField
. The toggle it with setState
as required.
Example:
class _FormFieldSampleState extends State<FormFieldSample> {
// Initially password is obscure
bool _obscureText = true;
String _password;
// Toggles the password show status
void _toggle() {
setState(() {
_obscureText = !_obscureText;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Sample"),
),
body: new Container(
child: new Column(
children: <Widget>[
new TextFormField(
decoration: const InputDecoration(
labelText: 'Password',
icon: const Padding(
padding: const EdgeInsets.only(top: 15.0),
child: const Icon(Icons.lock))),
validator: (val) => val.length < 6 ? 'Password too short.' : null,
onSaved: (val) => _password = val,
obscureText: _obscureText,
),
new FlatButton(
onPressed: _toggle,
child: new Text(_obscureText ? "Show" : "Hide"))
],
),
),
);
}
}
Upvotes: 151
Reputation: 1291
//Password
const TextField(
obscureText: true, //for hide Password
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outline),
hintText: 'Password'),
),
Upvotes: 1
Reputation: 2506
This solution prevents taps in the suffixIcon from giving focus to the TextField, but if the TextField was focused and user wants to reveal/hide the password, then focus is not lost.
import 'package:flutter/material.dart';
class PasswordField extends StatefulWidget {
const PasswordField({Key? key}) : super(key: key);
@override
_PasswordFieldState createState() => _PasswordFieldState();
}
class _PasswordFieldState extends State<PasswordField> {
final textFieldFocusNode = FocusNode();
bool _obscured = false;
void _toggleObscured() {
setState(() {
_obscured = !_obscured;
if (textFieldFocusNode.hasPrimaryFocus) return; // If focus is on text field, dont unfocus
textFieldFocusNode.canRequestFocus = false; // Prevents focus if tap on eye
});
}
@override
Widget build(BuildContext context) {
return TextField(
keyboardType: TextInputType.visiblePassword,
obscureText: _obscured,
focusNode: textFieldFocusNode,
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.never, //Hides label on focus or if filled
labelText: "Password",
filled: true, // Needed for adding a fill color
fillColor: Colors.grey.shade800,
isDense: true, // Reduces height a bit
border: OutlineInputBorder(
borderSide: BorderSide.none, // No border
borderRadius: BorderRadius.circular(12), // Apply corner radius
),
prefixIcon: Icon(Icons.lock_rounded, size: 24),
suffixIcon: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 4, 0),
child: GestureDetector(
onTap: _toggleObscured,
child: Icon(
_obscured
? Icons.visibility_rounded
: Icons.visibility_off_rounded,
size: 24,
),
),
),
),
);
}
}
Upvotes: 27
Reputation: 20231
Well I personally like to keep the passwords hidden all the time and seen when you want to see them, so this is the approach I use to hide/unhide passwords,Incase you want the password to be visible when the touch is In contact with the hide icon, and hidden as soon as you remove the contact then this is for you
//make it invisible globally
bool invisible = true;
//wrap your toggle icon in Gesture Detector
GestureDetector(
onTapDown: inContact,//call this method when incontact
onTapUp: outContact,//call this method when contact with screen is removed
child: Icon(
Icons.remove_red_eye,
color: colorButton,
),
),
void inContact(TapDownDetails details) {
setState(() {
invisible = false;
});
}
void outContact(TapUpDetails details) {
setState(() {
invisible=true;
});
}
This approach is being used in of my packages https://pub.dev/packages/passwordfield
The output of the above code
Upvotes: 14
Reputation: 164
Here's a simpler example with built-in material design icons:
child: TextFormField(
decoration: InputDecoration(
fillColor: Color(0xFFFFFFFF), filled: true,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFF808080)),
),
suffixIcon: GestureDetector(
onTap: () {
setState(() {
_showPassword = !_showPassword;
});
},
child: Icon(
_showPassword ? Icons.visibility : Icons.visibility_off,
),
),
labelText: 'Password'),
obscureText: !_showPassword,
),
Upvotes: 4
Reputation: 171
I have more useful solution. You can use Provider and listen TextFormField with Consumer Widget
obscure_text_state.dart
import 'package:flutter/material.dart';
class ObscureTextState with ChangeNotifier {
bool _isTrue = true;
bool get isTrue => _isTrue;
get switchObsIcon {
return _isTrue ? Icon(Icons.visibility_off) : Icon(Icons.visibility);
}
void toggleObs() {
_isTrue = !_isTrue;
notifyListeners();
}
}
Then you should listen that state with Consumer where TextFromField is.
Consumer<ObscureTextState>(
builder: (context, obs, child) {
return TextFormField(
controller: _passwordController,
validator: (value) {
if (value.isEmpty) {
return "Alan boş bırakılamaz!";
} else if (value.length < 6) {
return "Şifre en az 6 haneden oluşmalıdır.";
} else {
return null;
}
},
obscureText:
Provider.of<ObscureTextState>(context, listen: false)
.isTrue,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
suffixIcon: IconButton(
onPressed: () {
Provider.of<ObscureTextState>(context, listen: false)
.toggleObs();
},
icon: Provider.of<ObscureTextState>(context,
listen: false)
.switchObsIcon,
),
hintText: "Şifre",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0))),
);
},
),
Upvotes: 0
Reputation: 1
TextFormFeild(
decoration:InputDecoration(
icon: _isSecurityIcon == true
? IconButton(
icon: Icon(Icons.visibility_off_outlined),
onPressed: () {
setState(() {
_isSecurityIcon = false;
});
},
)
: IconButton(
icon: Icon(Icons.visibility_outlined),
onPressed: () {
setState(
() {
_isSecurityIcon = true;
},
);
},
),
),
);```
Upvotes: -1
Reputation: 7945
Thank @Parikshit Chalke for answer. However,
setState
is quite expensive call if your only want to update your TextFormField
and IconButton
. Instead, wrap it inside StatefulBuilder and have only child items updated.
Example solution:
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
// initially password is invisible
bool _passwordVisible = false;
String _password;
@override
Widget build(BuildContext context) {
return Column(
children: [
// other widget that does not need update when password visibility is toggled
Text("I do not require update"),
StatefulBuilder(builder: (_context, _setState) {
// only following widget gets update when _setState is used
return TextFormField(
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
_passwordVisible ? Icons.visibility : Icons.visibility_off,
),
onPressed: () {
// use _setState that belong to StatefulBuilder
_setState(() {
_passwordVisible = !_passwordVisible;
});
},
),
labelText: 'Password',
icon: const Padding(
padding: const EdgeInsets.only(top: 15.0),
child: const Icon(Icons.lock),
),
),
validator: (val) => val.length < 6 ? 'Password too short.' : null,
onSaved: (val) => _password = val,
obscureText: true,
);
}),
],
);
}
}
Upvotes: 0
Reputation: 4005
I have created a solution as per @Hemanth Raj but in a more robust way.
First declare a bool
variable _passwordVisible
.
Initiate _passwordVisible
to false
in initState()
@override
void initState() {
_passwordVisible = false;
}
Following is the TextFormField
widget :
TextFormField(
keyboardType: TextInputType.text,
controller: _userPasswordController,
obscureText: !_passwordVisible,//This will obscure text dynamically
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
// Here is key idea
suffixIcon: IconButton(
icon: Icon(
// Based on passwordVisible state choose the icon
_passwordVisible
? Icons.visibility
: Icons.visibility_off,
color: Theme.of(context).primaryColorDark,
),
onPressed: () {
// Update the state i.e. toogle the state of passwordVisible variable
setState(() {
_passwordVisible = !_passwordVisible;
});
},
),
),
);
Upvotes: 314
Reputation: 41
class SignIn extends StatefulWidget {
@override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
// Initially password is obscure
bool _obscureText = true;
// Toggles the password show status
void _togglePasswordStatus() {
setState(() {
_obscureText = !_obscureText;
});
}
@override
Widget build(BuildContext context) {
return
Scaffold(
backgroundColor: Colors.brown[100],
appBar: AppBar(
backgroundColor: Colors.brown[400],
elevation: 0.0,
title: Text('Sign In'),
),
body: Container(
padding: EdgeInsets.symmetric(vertical:20.0,horizontal:50.0),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
TextFormField(
decoration: InputDecoration(
hintText: 'Password',
suffixIcon: IconButton(
icon:Icon(_obscureText ? Icons.visibility:Icons.visibility_off,),
onPressed: _togglePasswordStatus,
color: Colors.pink[400],
),
),
validator: (val){
return
val.length < 6 ? 'Enter A Password Longer Than 6 Charchters' :null;
},
obscureText: _obscureText,
onChanged: (val){
setState(() {
password = val.trim();
});
},
),
],),),
),
);
}
}
Upvotes: 4
Reputation: 39
bool _obscuredText = true;
_toggle(){
setState(() {
_obscuredText = !_obscuredText;
});
}
Widget _createPassword(){
return TextField(
obscureText: _obscuredText,
cursorColor: Colors.black54,
style: TextStyle( color: Colors.black54),
decoration: InputDecoration(
labelStyle: TextStyle(
color: Colors.black54
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black54
)
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0)
),
labelText: 'Contraseña',
hintText: 'Contraseña',
suffixIcon: FlatButton(onPressed: _toggle, child:Icon(Icons.remove_red_eye, color: _obscuredText ? Colors.black12 : Colors.black54))
),
onChanged: (value) {
setState(() {
_password = value;
});
},
);
}
Hope this helps!
Upvotes: 3
Reputation: 111
I did it with holding and releasing the longTap:
bool _passwordVisible;
@override
void initState() {
_passwordVisible = false;
super.initState();
}
// ...
TextFormField(
obscureText: !_passwordVisible,
decoration: InputDecoration(
hasFloatingPlaceholder: true,
filled: true,
fillColor: Colors.white.withOpacity(0.5),
labelText: "Password",
suffixIcon: GestureDetector(
onLongPress: () {
setState(() {
_passwordVisible = true;
});
},
onLongPressUp: () {
setState(() {
_passwordVisible = false;
});
},
child: Icon(
_passwordVisible ? Icons.visibility : Icons.visibility_off),
),
),
validator: (String value) {
if (value.isEmpty) {
return "*Password needed";
}
},
onSaved: (String value) {
_setPassword(value);
},
);
Upvotes: 11
Reputation: 24948
With a credit goes to X-Wei, you can create the widget as a separate password.dart
:
import 'package:flutter/material.dart';
class PasswordField extends StatefulWidget {
const PasswordField({
this.fieldKey,
this.hintText,
this.labelText,
this.helperText,
this.onSaved,
this.validator,
this.onFieldSubmitted,
});
final Key fieldKey;
final String hintText;
final String labelText;
final String helperText;
final FormFieldSetter<String> onSaved;
final FormFieldValidator<String> validator;
final ValueChanged<String> onFieldSubmitted;
@override
_PasswordFieldState createState() => new _PasswordFieldState();
}
class _PasswordFieldState extends State<PasswordField> {
bool _obscureText = true;
@override
Widget build(BuildContext context) {
return new TextFormField(
key: widget.fieldKey,
obscureText: _obscureText,
maxLength: 8,
onSaved: widget.onSaved,
validator: widget.validator,
onFieldSubmitted: widget.onFieldSubmitted,
decoration: new InputDecoration(
border: const UnderlineInputBorder(),
filled: true,
hintText: widget.hintText,
labelText: widget.labelText,
helperText: widget.helperText,
suffixIcon: new GestureDetector(
onTap: () {
setState(() {
_obscureText = !_obscureText;
});
},
child:
new Icon(_obscureText ? Icons.visibility : Icons.visibility_off),
),
),
);
}
}
Call it as:
import 'package:my_app/password.dart';
String _password;
final _passwordFieldKey = GlobalKey<FormFieldState<String>>();
PasswordField(
fieldKey: _passwordFieldKey,
helperText: 'No more than 8 characters.',
labelText: 'Password *',
onFieldSubmitted: (String value) {
setState(() {
this._password = value;
});
},
),
Upvotes: 18