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; String? _newPinError;
bool _currentIsWrong = false; bool _currentIsWrong = false;
bool _newIsWrong = false; bool _newIsWrong = false;
bool _isObscureCurrent = true;
bool _isObscureNew = true;
bool _isObscureConfirm = true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -76,16 +79,38 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
AppTextFormField( AppTextFormField(
initialValue: _currentPin, initialValue: _currentPin,
autofocus: true, autofocus: true,
obscureText: true, obscureText: _isObscureCurrent,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
labelText: l10n.s_current_pin, labelText: l10n.s_current_pin,
errorText: _currentIsWrong ? _currentPinError : null, errorText: _currentIsWrong ? _currentPinError : null,
errorMaxLines: 3, errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined), prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: _currentIsWrong ? const Icon(Icons.error) : null, 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) { onChanged: (value) {
setState(() { setState(() {
_currentIsWrong = false; _currentIsWrong = false;
@ -99,7 +124,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
AppTextFormField( AppTextFormField(
initialValue: _newPin, initialValue: _newPin,
autofocus: !hasPin, autofocus: !hasPin,
obscureText: true, obscureText: _isObscureNew,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
@ -108,7 +133,28 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
errorText: _newIsWrong ? _newPinError : null, errorText: _newIsWrong ? _newPinError : null,
errorMaxLines: 3, errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined), 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) { onChanged: (value) {
setState(() { setState(() {
@ -119,12 +165,29 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
), ),
AppTextFormField( AppTextFormField(
initialValue: _confirmPin, initialValue: _confirmPin,
obscureText: true, obscureText: _isObscureConfirm,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
labelText: l10n.s_confirm_pin, labelText: l10n.s_confirm_pin,
prefixIcon: const Icon(Icons.pin_outlined), 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: enabled:
(!hasPin || _currentPin.isNotEmpty) && _newPin.isNotEmpty, (!hasPin || _currentPin.isNotEmpty) && _newPin.isNotEmpty,
), ),

View File

@ -198,6 +198,8 @@
"s_change_puk": "Change PUK", "s_change_puk": "Change PUK",
"s_show_pin": "Show PIN", "s_show_pin": "Show PIN",
"s_hide_pin": "Hide PIN", "s_hide_pin": "Hide PIN",
"s_show_puk": "Show PUK",
"s_hide_puk": "Hide PUK",
"s_current_pin": "Current PIN", "s_current_pin": "Current PIN",
"s_current_puk": "Current PUK", "s_current_puk": "Current PUK",
"s_new_pin": "New PIN", "s_new_pin": "New PIN",
@ -295,6 +297,8 @@
"s_management_key": "Management key", "s_management_key": "Management key",
"s_current_management_key": "Current management key", "s_current_management_key": "Current management key",
"s_new_management_key": "New 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", "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.", "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", "l_management_key_changed": "Management key changed",

View File

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

View File

@ -51,6 +51,7 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
String _password = ''; String _password = '';
bool _passwordIsWrong = false; bool _passwordIsWrong = false;
bool _importing = false; bool _importing = false;
bool _isObscure = true;
@override @override
void initState() { void initState() {
@ -125,7 +126,7 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
Text(l10n.p_password_protected_file), Text(l10n.p_password_protected_file),
AppTextField( AppTextField(
autofocus: true, autofocus: true,
obscureText: true, obscureText: _isObscure,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
key: keys.managementKeyField, key: keys.managementKeyField,
decoration: InputDecoration( decoration: InputDecoration(
@ -134,7 +135,28 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
errorText: _passwordIsWrong ? l10n.s_wrong_password : null, errorText: _passwordIsWrong ? l10n.s_wrong_password : null,
errorMaxLines: 3, errorMaxLines: 3,
prefixIcon: const Icon(Icons.password_outlined), 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, textInputAction: TextInputAction.next,
onChanged: (value) { onChanged: (value) {

View File

@ -55,6 +55,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
ManagementKeyType _keyType = ManagementKeyType.tdes; ManagementKeyType _keyType = ManagementKeyType.tdes;
final _currentController = TextEditingController(); final _currentController = TextEditingController();
final _keyController = TextEditingController(); final _keyController = TextEditingController();
bool _isObscure = true;
@override @override
void initState() { void initState() {
@ -167,7 +168,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
if (protected) if (protected)
AppTextField( AppTextField(
autofocus: true, autofocus: true,
obscureText: true, obscureText: _isObscure,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
key: keys.pinPukField, key: keys.pinPukField,
maxLength: 8, maxLength: 8,
@ -183,9 +184,27 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
: null, : null,
errorMaxLines: 3, errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined), prefixIcon: const Icon(Icons.pin_outlined),
suffixIcon: _currentIsWrong || _currentInvalidFormat suffixIcon: Wrap(
? const Icon(Icons.error) crossAxisAlignment: WrapCrossAlignment.center,
: null, 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, textInputAction: TextInputAction.next,
onChanged: (value) { onChanged: (value) {

View File

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