Add visibility icon to fields with obscureText

This commit is contained in:
Dennis Fokin 2023-11-27 15:12:57 +01:00 committed by Elias Bonnici
parent fa92927f13
commit 613d05fbb2
No known key found for this signature in database
GPG Key ID: 5EAC28EA3F980CCF
6 changed files with 281 additions and 45 deletions

View File

@ -48,6 +48,9 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
String? _newPinError;
bool _currentIsWrong = false;
bool _newIsWrong = false;
bool _isObscureCurrent = true;
bool _isObscureNew = true;
bool _isObscureConfirm = true;
@override
Widget build(BuildContext context) {
@ -76,16 +79,38 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
AppTextFormField(
initialValue: _currentPin,
autofocus: true,
obscureText: true,
obscureText: _isObscureCurrent,
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.s_current_pin,
errorText: _currentIsWrong ? _currentPinError : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: _currentIsWrong ? const Icon(Icons.error) : null,
),
border: const OutlineInputBorder(),
labelText: l10n.s_current_pin,
errorText: _currentIsWrong ? _currentPinError : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureCurrent
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureCurrent = !_isObscureCurrent;
});
},
tooltip: _isObscureCurrent
? l10n.s_show_pin
: l10n.s_hide_pin,
),
if (_currentIsWrong) ...[
const Icon(Icons.error_outlined),
const SizedBox(
width: 8.0,
)
]
],
)),
onChanged: (value) {
setState(() {
_currentIsWrong = false;
@ -99,7 +124,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
AppTextFormField(
initialValue: _newPin,
autofocus: !hasPin,
obscureText: true,
obscureText: _isObscureNew,
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
border: const OutlineInputBorder(),
@ -108,7 +133,28 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
errorText: _newIsWrong ? _newPinError : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: _newIsWrong ? const Icon(Icons.error) : null,
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureNew
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureNew = !_isObscureNew;
});
},
tooltip:
_isObscureNew ? l10n.s_show_pin : l10n.s_hide_pin,
),
if (_newIsWrong) ...[
const Icon(Icons.error_outlined),
const SizedBox(
width: 8.0,
)
]
]),
),
onChanged: (value) {
setState(() {
@ -119,12 +165,29 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
),
AppTextFormField(
initialValue: _confirmPin,
obscureText: true,
obscureText: _isObscureConfirm,
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.s_confirm_pin,
prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureConfirm
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureConfirm = !_isObscureConfirm;
});
},
tooltip:
_isObscureConfirm ? l10n.s_show_pin : l10n.s_hide_pin,
)
],
),
enabled:
(!hasPin || _currentPin.isNotEmpty) && _newPin.isNotEmpty,
),

View File

@ -198,6 +198,8 @@
"s_change_puk": "Change PUK",
"s_show_pin": "Show PIN",
"s_hide_pin": "Hide PIN",
"s_show_puk": "Show PUK",
"s_hide_puk": "Hide PUK",
"s_current_pin": "Current PIN",
"s_current_puk": "Current PUK",
"s_new_pin": "New PIN",
@ -295,6 +297,8 @@
"s_management_key": "Management key",
"s_current_management_key": "Current management key",
"s_new_management_key": "New management key",
"s_show_management_key": "Show management key",
"s_hide_management_key": "Hide management key",
"l_change_management_key": "Change management key",
"p_change_management_key_desc": "Change your management key. You can optionally choose to allow the PIN to be used instead of the management key.",
"l_management_key_changed": "Management key changed",

View File

@ -42,6 +42,9 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
String _newPassword = '';
String _confirmPassword = '';
bool _currentIsWrong = false;
bool _isObscureCurrent = true;
bool _isObscureNew = true;
bool _isObscureConfirm = true;
_submit() async {
FocusUtils.unfocus(context);
@ -85,17 +88,38 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
Text(l10n.p_enter_current_password_or_reset),
AppTextField(
autofocus: true,
obscureText: true,
obscureText: _isObscureCurrent,
autofillHints: const [AutofillHints.password],
key: keys.currentPasswordField,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.s_current_password,
errorText: _currentIsWrong ? l10n.s_wrong_password : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: _currentIsWrong ? const Icon(Icons.error) : null,
),
border: const OutlineInputBorder(),
labelText: l10n.s_current_password,
errorText: _currentIsWrong ? l10n.s_wrong_password : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureCurrent
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureCurrent = !_isObscureCurrent;
});
},
tooltip: _isObscureCurrent
? l10n.s_show_password
: l10n.s_hide_password),
if (_currentIsWrong) ...[
const Icon(Icons.error_outlined),
const SizedBox(
width: 8.0,
)
]
],
)),
textInputAction: TextInputAction.next,
onChanged: (value) {
setState(() {
@ -147,12 +171,28 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
AppTextField(
key: keys.newPasswordField,
autofocus: !widget.state.hasKey,
obscureText: true,
obscureText: _isObscureNew,
autofillHints: const [AutofillHints.newPassword],
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.s_new_password,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureNew
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureNew = !_isObscureNew;
});
},
tooltip: _isObscureNew
? l10n.s_show_password
: l10n.s_hide_password),
]),
enabled: !widget.state.hasKey || _currentPassword.isNotEmpty,
),
textInputAction: TextInputAction.next,
@ -169,12 +209,29 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
),
AppTextField(
key: keys.confirmPasswordField,
obscureText: true,
obscureText: _isObscureConfirm,
autofillHints: const [AutofillHints.newPassword],
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.s_confirm_password,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureConfirm
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureConfirm = !_isObscureConfirm;
});
},
tooltip: _isObscureConfirm
? l10n.s_show_password
: l10n.s_hide_password)
],
),
enabled:
(!widget.state.hasKey || _currentPassword.isNotEmpty) &&
_newPassword.isNotEmpty,

View File

@ -51,6 +51,7 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
String _password = '';
bool _passwordIsWrong = false;
bool _importing = false;
bool _isObscure = true;
@override
void initState() {
@ -125,7 +126,7 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
Text(l10n.p_password_protected_file),
AppTextField(
autofocus: true,
obscureText: true,
obscureText: _isObscure,
autofillHints: const [AutofillHints.password],
key: keys.managementKeyField,
decoration: InputDecoration(
@ -134,7 +135,28 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
errorText: _passwordIsWrong ? l10n.s_wrong_password : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: _passwordIsWrong ? const Icon(Icons.error) : null,
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscure
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
},
tooltip: _isObscure
? l10n.s_show_password
: l10n.s_hide_password),
if (_passwordIsWrong) ...[
const Icon(Icons.error_outlined),
const SizedBox(
width: 8.0,
)
]
]),
),
textInputAction: TextInputAction.next,
onChanged: (value) {

View File

@ -55,6 +55,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
ManagementKeyType _keyType = ManagementKeyType.tdes;
final _currentController = TextEditingController();
final _keyController = TextEditingController();
bool _isObscure = true;
@override
void initState() {
@ -167,7 +168,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
if (protected)
AppTextField(
autofocus: true,
obscureText: true,
obscureText: _isObscure,
autofillHints: const [AutofillHints.password],
key: keys.pinPukField,
maxLength: 8,
@ -183,9 +184,27 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
: null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: _currentIsWrong || _currentInvalidFormat
? const Icon(Icons.error)
: null,
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscure
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
},
tooltip:
_isObscure ? l10n.s_show_pin : l10n.s_hide_pin),
if (_currentIsWrong || _currentInvalidFormat) ...[
const Icon(Icons.error_outlined),
const SizedBox(
width: 8.0,
)
]
]),
),
textInputAction: TextInputAction.next,
onChanged: (value) {

View File

@ -44,6 +44,9 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
String _confirmPin = '';
bool _currentIsWrong = false;
int _attemptsRemaining = -1;
bool _isObscureCurrent = true;
bool _isObscureNew = true;
bool _isObscureConfirm = true;
_submit() async {
final notifier = ref.read(pivStateProvider(widget.path).notifier);
@ -104,26 +107,52 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
: l10n.p_enter_current_puk_or_reset),
AppTextField(
autofocus: true,
obscureText: true,
obscureText: _isObscureCurrent,
maxLength: 8,
autofillHints: const [AutofillHints.password],
key: keys.pinPukField,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: widget.target == ManageTarget.pin
? l10n.s_current_pin
: l10n.s_current_puk,
errorText: _currentIsWrong
? (widget.target == ManageTarget.pin
? l10n
.l_wrong_pin_attempts_remaining(_attemptsRemaining)
: l10n
.l_wrong_puk_attempts_remaining(_attemptsRemaining))
: null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: _currentIsWrong ? const Icon(Icons.error) : null,
),
border: const OutlineInputBorder(),
labelText: widget.target == ManageTarget.pin
? l10n.s_current_pin
: l10n.s_current_puk,
errorText: _currentIsWrong
? (widget.target == ManageTarget.pin
? l10n.l_wrong_pin_attempts_remaining(
_attemptsRemaining)
: l10n.l_wrong_puk_attempts_remaining(
_attemptsRemaining))
: null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureCurrent
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureCurrent = !_isObscureCurrent;
});
},
tooltip: widget.target == ManageTarget.pin
? (_isObscureCurrent
? l10n.s_show_pin
: l10n.s_hide_pin)
: (_isObscureCurrent
? l10n.s_show_puk
: l10n.s_hide_puk),
),
if (_currentIsWrong) ...[
const Icon(Icons.error_outlined),
const SizedBox(
width: 8.0,
)
]
],
)),
textInputAction: TextInputAction.next,
onChanged: (value) {
setState(() {
@ -136,7 +165,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
widget.target == ManageTarget.puk ? l10n.s_puk : l10n.s_pin)),
AppTextField(
key: keys.newPinPukField,
obscureText: true,
obscureText: _isObscureNew,
maxLength: 8,
autofillHints: const [AutofillHints.newPassword],
decoration: InputDecoration(
@ -145,6 +174,27 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
? l10n.s_new_puk
: l10n.s_new_pin,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureNew
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureNew = !_isObscureNew;
});
},
tooltip: widget.target == ManageTarget.pin
? (_isObscureNew
? l10n.s_show_pin
: l10n.s_hide_pin)
: (_isObscureNew
? l10n.s_show_puk
: l10n.s_hide_puk),
),
]),
// Old YubiKeys allowed a 4 digit PIN
enabled: _currentPin.length >= 4,
),
@ -162,7 +212,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
),
AppTextField(
key: keys.confirmPinPukField,
obscureText: true,
obscureText: _isObscureConfirm,
maxLength: 8,
autofillHints: const [AutofillHints.newPassword],
decoration: InputDecoration(
@ -171,6 +221,27 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
? l10n.s_confirm_puk
: l10n.s_confirm_pin,
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
icon: Icon(_isObscureConfirm
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_isObscureConfirm = !_isObscureConfirm;
});
},
tooltip: widget.target == ManageTarget.pin
? (_isObscureConfirm
? l10n.s_show_pin
: l10n.s_hide_pin)
: (_isObscureConfirm
? l10n.s_show_puk
: l10n.s_hide_puk),
)
]),
enabled: _currentPin.length >= 4 && _newPin.length >= 6,
),
textInputAction: TextInputAction.done,