Have you ever tried typing a long, complex password on your phone, only to get stuck because every single letter turned into a dot instantly? It can be super frustrating!
When you build mobile apps, the sign-up or login screen is often the very first impression users have of your app. A clunky, hard-to-use flutter password field can make people give up before they even start. On the flip side, creating a smooth and flutter secure input experience makes your app feel safe, modern, and high quality.
In this guide, we are going to build a production-ready flutter password textfield step by step. We will look at how to handle flutter obscureText, add a clean flutter password eye button so users can show or flutter hide password text, build a real-time strength meter, and follow best practices for accessibility and autofill.
Whether you are setting up your first login form or polishing an existing authentication flow, this post will give you the practical code and UX tips you need!
Mastering obscureText in Flutter
When you build an input for sensitive data, hiding the typed characters is your first line of defense. In Flutter, hiding user input inside a TextField or TextFormField is very simple, thanks to the built-in obscureText property.
By setting obscureText: true, Flutter replaces each typed character with a masking symbol (usually a dot or bullet). This keeps the password safe from nearby eyes.
How obscureText Works
Here is a quick look at how obscureText behaves under the hood:
- Masking Characters: Converts plaintext characters into visual bullets.
- Disabling Copy Actions: Flutter automatically disables standard copy/cut operations on obscured fields so users cannot copy plaintext passwords out of the field.
-
Character Masking Customization: You can customize the dot using
obscuringCharacter(e.g.,obscuringCharacter: '*').
Complete Code Example
Here is a full, working Flutter example using our boilerplate setup. Notice how we toggle obscureText using a simple state variable inside our HomeScreen:
class _HomeScreenState extends State<HomeScreen> {
// Flag to keep track of password visibility
final bool _isObscured = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Basic obscureText Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: TextField(
// Key property to mask the input
obscureText: _isObscured,
autocorrect: false,
enableSuggestions: false,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
hintText: 'Enter your password',
),
),
),
),
);
}
}
Pro Tip: Always pair
obscureText: truewithautocorrect: falseandenableSuggestions: false. This stops the phone keyboard from saving or predicting sensitive passwords!
Ready to Go Beyond the Basics?
Learn Flutter the right way with 100+ practical lessons, real projects, and lifetime updates.
Building a Show/Hide Password Visibility Toggle
Typing long passwords on a glass screen leads to typos. Giving users a quick way to reveal what they typed drastically reduces friction and login errors.
Adding a flutter password eye icon inside the input decoration is the standard way to handle this. When the user taps the icon, we toggle our state variable, changing obscureText between true and false. This allows users to easily show or flutter hide password text on demand.
Key Components for the Toggle
To build an intuitive flutter hide password feature, we combine three main parts:
-
Suffix Icon: Place an
IconButtoninsideInputDecoration.suffixIcon. -
Dynamic Eye Icon: Swap between
Icons.visibilityandIcons.visibility_offdepending on the current state. -
State Updates: Call
setState()inside the button'sonPressedcallback to update the boolean.
Complete Code Example
Here is the complete, runnable code demonstrating a polished flutter password field toggle:
class _HomeScreenState extends State<HomeScreen> {
// Keeps track of password visibility state
bool _obscureText = true;
void _togglePasswordVisibility() {
setState(() {
_obscureText = !_obscureText;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Password Visibility Toggle')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: TextField(
obscureText: _obscureText,
autocorrect: false,
enableSuggestions: false,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
border: const OutlineInputBorder(),
// Suffix icon for the toggle button
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
onPressed: _togglePasswordVisibility,
tooltip: _obscureText ? 'Show password' : 'Hide password',
),
),
),
),
),
);
}
}
UX Best Practice: Always include a
tooltipon theIconButton. It makes your flutter secure input field much more accessible for screen readers!
Polishing the Eye Icon UX & Accessibility
While adding a flutter password eye icon is straightforward, small details separate a average input field from an exceptional one. Choosing the right icon and making sure screen readers handle it properly ensures a seamless experience for every user.
Essential Eye Icon Considerations
-
Icon Consistency: Use
Icons.visibility_offwhen the password is hidden (slashed eye) andIcons.visibilitywhen the text is exposed (open eye). - Accessibility Labels: Assistive technologies (like TalkBack or VoiceOver) need to announce what tapping the icon will do. Standard tooltips handle this automatically.
- Focus Preservation: Clicking an icon button shouldn't unexpectedly steal focus or close the virtual keyboard while typing.
Complete Code Example
Here is a runnable example showing how to refine the eye icon button with clear tooltips and semantically descriptive actions for a accessible flutter password textfield:
class _HomeScreenState extends State<HomeScreen> {
bool _hidePassword = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Eye Icon UX Practice')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: TextField(
obscureText: _hidePassword,
autocorrect: false,
enableSuggestions: false,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
// Secure input eye icon with accessibility
suffixIcon: IconButton(
icon: Icon(
_hidePassword ? Icons.visibility_off : Icons.visibility,
),
tooltip: _hidePassword ? 'Show password' : 'Hide password',
onPressed: () {
setState(() {
_hidePassword = !_hidePassword;
});
},
),
),
),
),
),
);
}
}
Handling Confirm Password Fields with Form Validation
When users create an account, asking them to re-enter their chosen password prevents painful typos. However, a poor confirm password UX can quickly lead to frustration.
To build a smooth confirm password flow, we use a Form widget coupled with TextFormField. This lets us validate both fields simultaneously and ensure the passwords match before allowing the form to submit.
Best Practices for Confirm Password Fields
- Real-time or On-Submit Validation: Validate matching passwords cleanly without spamming users with error messages while they are actively typing.
-
Shared Controllers: Use distinct
TextEditingControllerinstances for both fields to easily compare their text values. - Synchronized Visibility Option: You can choose whether to keep obscure toggles separate or sync them together for simplicity.
Complete Code Example
Here is a full, working example showcasing a complete account sign-up form using a flutter password textfield paired with a confirm password field:
class _HomeScreenState extends State<HomeScreen> {
final _formKey = GlobalKey<FormState>();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
bool _obscurePassword = true;
bool _obscureConfirmPassword = true;
@override
void dispose() {
_passwordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
void _submitForm() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Passwords match! Form submitted.')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Confirm Password Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Primary Password Field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
autocorrect: false,
enableSuggestions: false,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility_off
: Icons.visibility,
),
tooltip: _obscurePassword
? 'Show password'
: 'Hide password',
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a password';
}
if (value.length < 6) {
return 'Password must be at least 6 characters long';
}
return null;
},
),
const SizedBox(height: 16.0),
// Confirm Password Field
TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureConfirmPassword,
autocorrect: false,
enableSuggestions: false,
decoration: InputDecoration(
labelText: 'Confirm Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscureConfirmPassword
? Icons.visibility_off
: Icons.visibility,
),
tooltip: _obscureConfirmPassword
? 'Show password'
: 'Hide password',
onPressed: () {
setState(() {
_obscureConfirmPassword = !_obscureConfirmPassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please confirm your password';
}
if (value != _passwordController.text) {
return 'Passwords do not match';
}
return null;
},
),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
],
),
),
),
);
}
}
Building a Real-Time Password Strength Meter
A password strength indicator gives users immediate visual feedback as they type. This encourages them to pick strong, secure credentials without frustrating surprise errors when they click submit.
By listening to changes in your flutter password textfield, you can dynamically evaluate factors like length, numbers, uppercase letters, and special symbols.
Key Rules for Strong Passwords
- Length: Aim for at least 8 to 12 characters.
- Variety: Mix uppercase letters, lowercase letters, numbers, and symbols.
- Visual Feedback: Use progress bars and color transitions (Red → Orange → Green) to clearly indicate security level.
Complete Code Example
Here is a full, working Flutter example featuring a real-time strength meter built right below a flutter secure input field:
class _HomeScreenState extends State<HomeScreen> {
final _passwordController = TextEditingController();
bool _obscureText = true;
double _strength = 0;
String _strengthLabel = 'Weak';
Color _strengthColor = Colors.red;
@override
void initState() {
super.initState();
_passwordController.addListener(_checkPasswordStrength);
}
@override
void dispose() {
_passwordController.dispose();
super.dispose();
}
void _checkPasswordStrength() {
final password = _passwordController.text;
if (password.isEmpty) {
setState(() {
_strength = 0;
_strengthLabel = 'Empty';
_strengthColor = Colors.grey;
});
return;
}
double score = 0;
// Evaluate basic strength criteria
if (password.length >= 8) score += 0.25;
if (RegExp(r'[A-Z]').hasMatch(password)) score += 0.25;
if (RegExp(r'[0-9]').hasMatch(password)) score += 0.25;
if (RegExp(r'[!@#\$&*~]').hasMatch(password)) score += 0.25;
setState(() {
_strength = score;
if (score <= 0.25) {
_strengthLabel = 'Very Weak';
_strengthColor = Colors.red;
} else if (score <= 0.5) {
_strengthLabel = 'Weak';
_strengthColor = Colors.orange;
} else if (score <= 0.75) {
_strengthLabel = 'Medium';
_strengthColor = Colors.yellow.shade700;
} else {
_strengthLabel = 'Strong';
_strengthColor = Colors.green;
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Password Strength Indicator')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _passwordController,
obscureText: _obscureText,
autocorrect: false,
enableSuggestions: false,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
tooltip: _obscureText ? 'Show password' : 'Hide password',
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
const SizedBox(height: 12.0),
// Password strength visual indicator
LinearProgressIndicator(
value: _strength,
backgroundColor: Colors.grey.shade300,
color: _strengthColor,
minHeight: 6,
),
const SizedBox(height: 8.0),
Align(
alignment: Alignment.centerLeft,
child: Text(
'Strength: $_strengthLabel',
style: TextStyle(
color: _strengthColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
);
}
}
Supporting Secure Autofill for Seamless Logins
Nobody likes typing long passwords manually every time they open an app. Integrating secure autofill allows native password managers (like Apple Keychain, Google Password Manager, 1Password, or Bitwarden) to save and fill credentials automatically.
In Flutter, enabling password manager support is straightforward using the AutofillGroup widget and AutofillHints.
Key Steps for Secure Password Autofill
-
Wrap with
AutofillGroup: Enclose your username/email and password fields inside a singleAutofillGroup. -
Provide
autofillHints: Pass explicit hints such asAutofillHints.passwordorAutofillHints.newPasswordto the input fields. -
Trigger Commit: Call
TextInput.finishAutofillContext()when the user submits the form to prompt password managers to save or update the credentials.
Complete Code Example
Here is a runnable example demonstrating how to configure a flutter secure input field with system autofill support:
class _HomeScreenState extends State<HomeScreen> {
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
bool _obscureText = true;
@override
void dispose() {
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
void _onLogin() {
// Save/update password manager credentials upon successful login
TextInput.finishAutofillContext();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Logging in... Credentials saved to password manager!'),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Secure Autofill Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
// Wrap both fields in an AutofillGroup
child: AutofillGroup(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _usernameController,
keyboardType: TextInputType.emailAddress,
autofillHints: const [
AutofillHints.username,
AutofillHints.email,
],
decoration: const InputDecoration(
labelText: 'Email or Username',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
obscureText: _obscureText,
autocorrect: false,
enableSuggestions: false,
// Tell system password managers this is a password field
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
tooltip: _obscureText ? 'Show password' : 'Hide password',
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: _onLogin,
child: const Text('Log In'),
),
],
),
),
),
),
);
}
}
Configuring the Password Keyboard in Flutter
When users interact with a flutter password textfield, the mobile keyboard layout heavily impacts the typing experience. Setting up proper keyboard flags prevents annoying platform behaviors like auto-capitalizing the first letter or showing autocorrect text suggestions above sensitive credentials.
Essential Keyboard Configuration Properties
To optimize keyboard behavior for a flutter secure input field, configure these three properties together:
-
keyboardType: TextInputType.visiblePassword: Informs the operating system that this input contains a password, optimizing action buttons and suggestion behaviors. -
autocorrect: false: Disables auto-correction to prevent the system keyboard from altering typed password strings or caching them. -
enableSuggestions: false: Prevents auto-completion suggestion bars from rendering sensitive credentials in plain sight.
Complete Code Example
Here is a full, working Flutter example demonstrating correct keyboard configuration for a flutter password field:
class _HomeScreenState extends State<HomeScreen> {
bool _obscureText = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Password Keyboard Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: TextField(
// Password keyboard configurations
keyboardType: TextInputType.visiblePassword,
obscureText: _obscureText,
autocorrect: false,
enableSuggestions: false,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
tooltip: _obscureText ? 'Show password' : 'Hide password',
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
),
),
);
}
}
Pro Tip: Set
textInputAction: TextInputAction.doneon single password forms, orTextInputAction.nextwhen moving from a username or email field down to the password input!
Accessibility Best Practices for Password Fields
Accessibility should never be an afterthought when building a flutter password textfield. Screen reader users relying on TalkBack (Android) or VoiceOver (iOS) must be able to navigate, inspect, and toggle secure inputs just as easily as any other user.
When you obscure text in a flutter secure input, Flutter automatically announces to screen readers that the input is a secure field. However, custom actions—like our flutter password eye toggle button—need explicit labels so assistive technologies know what they do.
Key Accessibility Checklist
-
Clear Tooltips & Semantics: Give
IconButtonwidgets clear tooltips (e.g.,'Show password'and'Hide password'). Screen readers will announce these labels aloud when the button receives focus. -
Semantic Labels: Wrap interactive visual indicators in
Semanticswidgets if you want custom feedback (like reading out "Password strength: Medium"). - High-Contrast Colors: Ensure text, hints, error borders, and icons pass minimum WCAG contrast ratios (4.5:1) in both light and dark modes.
- Sufficient Tap Targets: Ensure interactive buttons (like the toggle eye) have a minimum tap area of 48x48 logical pixels.
Complete Code Example
Here is a full, accessible Flutter example using Semantics and IconButton tooltips:
class _HomeScreenState extends State<HomeScreen> {
bool _obscureText = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Accessible Password Input')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Semantics(
label: 'Password input field',
hint: 'Double tap to enter your password',
child: TextField(
obscureText: _obscureText,
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
// 48x48 logical pixel touch target for accessibility
constraints: const BoxConstraints(
minWidth: 48,
minHeight: 48,
),
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
tooltip: _obscureText ? 'Show password' : 'Hide password',
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
),
),
),
);
}
}
Perfecting Validation UX for Password Inputs
Validating a flutter password field requires a delicate balance. Showing errors too early disrupts the user while typing, but waiting until submission can leave them guessing why their input failed.
A smooth validation user experience guides users without causing annoyance.
Essential Rules for Password Validation UX
- Validate on Interaction or Submit: Avoid triggering destructive red error messages on empty fields before the user even finishes typing.
- Clear, Actionable Error Messages: Instead of vague messages like "Invalid password," specify exactly what is missing (e.g., "Must contain at least 8 characters and a number").
-
Focus Management: When a validation error occurs on form submission, automatically shift focus to the invalid field using a
FocusNode.
Complete Code Example
Here is a full, working Flutter example demonstrating clean validation UX using a TextFormField and FormValidation:
class _HomeScreenState extends State<HomeScreen> {
final _formKey = GlobalKey<FormState>();
final _passwordFocusNode = FocusNode();
bool _obscureText = true;
AutovalidateMode _autovalidateMode = AutovalidateMode.disabled;
@override
void dispose() {
_passwordFocusNode.dispose();
super.dispose();
}
void _submitForm() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Validation successful!')));
} else {
// Enable real-time auto-validation only after the user attempts submission
setState(() {
_autovalidateMode = AutovalidateMode.onUserInteraction;
});
_passwordFocusNode.requestFocus();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Validation UX Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
autovalidateMode: _autovalidateMode,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
focusNode: _passwordFocusNode,
obscureText: _obscureText,
autocorrect: false,
enableSuggestions: false,
decoration: InputDecoration(
labelText: 'Password',
helperText: 'Must be 8+ characters with at least one number',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
tooltip: _obscureText ? 'Show password' : 'Hide password',
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a password';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
if (!RegExp(r'[0-9]').hasMatch(value)) {
return 'Password must contain at least one number';
}
return null;
},
),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
],
),
),
),
);
}
}
Common Mistakes to Avoid with Flutter Password Inputs
Building a flutter secure input looks simple on the surface, but a few subtle mistakes can negatively affect security and user experience:
-
Forgetting to Disable Autocorrect & Suggestions: Leaving
autocorrect: trueorenableSuggestions: trueallows system keyboards to cache plaintext passwords or suggest them in word prediction bars. Always turn them off on sensitive fields! - Hardcoding Icon Tooltips: Skipping accessible tooltips on the flutter password eye toggle makes your app frustrating or unusable for visually impaired users.
-
Disabling Copy/Paste Manually in Frustrating Ways: While
obscureTextdisables copying text out of the field, blocking paste entirely prevents users from using secure external password managers. - Validating Too Aggressively: Running destructive validation checks on every keypress before the user finishes typing creates constant visual distraction.
-
Not Disposing Controllers or FocusNodes: Forgetting to clean up
TextEditingControllerorFocusNodeinstances indispose()leads to memory leaks in complex forms.
Related Topics & Further Reading
To learn more about building complete, high-quality forms in Flutter, check out these related guides:
- Form Validation: Master complex form state, custom validators, and cross-field validation rules.
- FocusNode: Learn how to manage keyboard focus, switch between fields automatically, and handle form submission shortcuts.
- TextField: Explore advanced input styling, input formatters, and custom decoration options.
Ready to Build Professional Flutter Apps?
Turn today’s knowledge into real-world Flutter skills with Flutter Foundations.
Top comments (0)