add AppTextField/AppTextFormField

This commit is contained in:
Adam Velebil 2023-11-10 15:24:53 +01:00
parent 547fe01ab0
commit f60e5ad756
No known key found for this signature in database
GPG Key ID: C9B1E4A3CBBD2E10
17 changed files with 244 additions and 51 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,12 +25,13 @@ import 'package:logging/logging.dart';
import 'package:yubico_authenticator/app/logging.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../desktop/models.dart';
import '../../fido/models.dart';
import '../../widgets/app_text_form_field.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/utf8_utils.dart';
import '../state.dart';
import '../../fido/models.dart';
import '../../app/models.dart';
final _log = Logger('fido.views.add_fingerprint_dialog');
@ -199,7 +200,7 @@ class _AddFingerprintDialogState extends ConsumerState<AddFingerprintDialog>
],
),
Text(l10n.l_fp_step_2_name),
TextFormField(
AppTextFormField(
focusNode: _nameFocus,
maxLength: 15,
inputFormatters: [limitBytesLength(15)],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,9 +23,10 @@ import '../../app/views/app_page.dart';
import '../../app/views/graphics.dart';
import '../../app/views/message_page.dart';
import '../../core/state.dart';
import '../../widgets/app_text_field.dart';
import '../features.dart' as features;
import '../models.dart';
import '../state.dart';
import '../features.dart' as features;
import 'key_actions.dart';
class FidoLockedPage extends ConsumerWidget {
@ -160,7 +161,7 @@ class _PinEntryFormState extends ConsumerState<_PinEntryForm> {
Text(l10n.l_enter_fido2_pin),
Padding(
padding: const EdgeInsets.only(top: 16.0, bottom: 16.0),
child: TextField(
child: AppTextField(
autofocus: true,
obscureText: _isObscure,
autofillHints: const [AutofillHints.password],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,6 +25,7 @@ import '../../app/models.dart';
import '../../app/state.dart';
import '../../desktop/models.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_form_field.dart';
import '../models.dart';
import '../state.dart';
@ -72,7 +73,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
children: [
if (hasPin) ...[
Text(l10n.p_enter_current_pin_or_reset_no_puk),
TextFormField(
AppTextFormField(
initialValue: _currentPin,
autofocus: true,
obscureText: true,
@ -94,7 +95,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
],
Text(l10n.p_enter_new_fido2_pin(minPinLength)),
// TODO: Set max characters based on UTF-8 bytes
TextFormField(
AppTextFormField(
initialValue: _newPin,
autofocus: !hasPin,
obscureText: true,
@ -114,7 +115,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
});
},
),
TextFormField(
AppTextFormField(
initialValue: _confirmPin,
obscureText: true,
autofillHints: const [AutofillHints.password],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,12 +19,13 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../desktop/models.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_form_field.dart';
import '../../widgets/utf8_utils.dart';
import '../models.dart';
import '../state.dart';
import '../../app/models.dart';
class RenameFingerprintDialog extends ConsumerStatefulWidget {
final DevicePath devicePath;
@ -89,7 +90,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameFingerprintDialog> {
children: [
Text(l10n.q_rename_target(widget.fingerprint.label)),
Text(l10n.p_will_change_label_fp),
TextFormField(
AppTextFormField(
initialValue: _label,
maxLength: 15,
inputFormatters: [limitBytesLength(15)],

View File

@ -30,15 +30,16 @@ import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../app/views/user_interaction.dart';
import '../../exception/apdu_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../core/state.dart';
import '../../desktop/models.dart';
import '../../exception/apdu_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../management/models.dart';
import '../../widgets/choice_filter_chip.dart';
import '../../widgets/file_drop_target.dart';
import '../../widgets/focus_utils.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_field.dart';
import '../../widgets/utf8_utils.dart';
import '../keys.dart' as keys;
import '../models.dart';
@ -353,7 +354,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
AppTextField(
key: keys.issuerField,
controller: _issuerController,
autofocus: widget.credentialData == null,
@ -385,7 +386,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
if (isValid) submit();
},
),
TextField(
AppTextField(
key: keys.nameField,
controller: _accountController,
maxLength: nameMaxLength,
@ -413,7 +414,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
if (isValid) submit();
},
),
TextField(
AppTextField(
key: keys.secretField,
controller: _secretController,
obscureText: _isObscure,

View File

@ -17,14 +17,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:yubico_authenticator/widgets/app_text_field.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../widgets/focus_utils.dart';
import '../../widgets/responsive_dialog.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
import '../keys.dart' as keys;
class ManagePasswordDialog extends ConsumerStatefulWidget {
final DevicePath path;
@ -43,7 +44,6 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
bool _currentIsWrong = false;
_submit() async {
FocusUtils.unfocus(context);
final result = await ref
@ -83,7 +83,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
children: [
if (widget.state.hasKey) ...[
Text(l10n.p_enter_current_password_or_reset),
TextField(
AppTextField(
autofocus: true,
obscureText: true,
autofillHints: const [AutofillHints.password],
@ -142,7 +142,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
),
],
Text(l10n.p_enter_new_password),
TextField(
AppTextField(
key: keys.newPasswordField,
autofocus: !widget.state.hasKey,
obscureText: true,
@ -165,7 +165,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
}
},
),
TextField(
AppTextField(
key: keys.confirmPasswordField,
obscureText: true,
autofillHints: const [AutofillHints.newPassword],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,6 +26,7 @@ import '../../app/views/app_page.dart';
import '../../app/views/graphics.dart';
import '../../app/views/message_page.dart';
import '../../core/state.dart';
import '../../widgets/app_text_form_field.dart';
import '../features.dart' as features;
import '../keys.dart' as keys;
import '../models.dart';
@ -153,7 +154,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
},
child: Builder(builder: (context) {
final textTheme = Theme.of(context).textTheme;
return TextFormField(
return AppTextFormField(
key: keys.searchAccountsField,
controller: searchController,
focusNode: searchFocus,

View File

@ -23,14 +23,15 @@ import '../../app/logging.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../exception/cancellation_exception.dart';
import '../../desktop/models.dart';
import '../../exception/cancellation_exception.dart';
import '../../widgets/focus_utils.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_form_field.dart';
import '../../widgets/utf8_utils.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
import '../keys.dart' as keys;
import 'utils.dart';
final _log = Logger('oath.view.rename_account_dialog');
@ -171,7 +172,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
? '${widget.issuer} (${widget.name})'
: widget.name)),
Text(l10n.p_rename_will_change_account_displayed),
TextFormField(
AppTextFormField(
initialValue: _issuer,
enabled: issuerRemaining > 0,
maxLength: issuerRemaining > 0 ? issuerRemaining : null,
@ -191,7 +192,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
});
},
),
TextFormField(
AppTextFormField(
initialValue: _name,
maxLength: nameRemaining,
inputFormatters: [limitBytesLength(nameRemaining)],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,8 +20,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../models.dart';
import '../../widgets/app_text_field.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
class UnlockForm extends ConsumerStatefulWidget {
@ -72,7 +73,7 @@ class _UnlockFormState extends ConsumerState<UnlockForm> {
l10n.l_enter_oath_pw,
),
const SizedBox(height: 16.0),
TextField(
AppTextField(
key: keys.passwordField,
controller: _passwordController,
autofocus: true,

View File

@ -22,9 +22,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/models.dart';
import '../../exception/cancellation_exception.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_field.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
import '../keys.dart' as keys;
class AuthenticationDialog extends ConsumerStatefulWidget {
final DevicePath devicePath;
@ -93,7 +94,7 @@ class _AuthenticationDialogState extends ConsumerState<AuthenticationDialog> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.p_unlock_piv_management_desc),
TextField(
AppTextField(
key: keys.managementKeyField,
autofocus: true,
autofillHints: const [AutofillHints.password],

View File

@ -24,9 +24,10 @@ import '../../app/state.dart';
import '../../core/models.dart';
import '../../widgets/choice_filter_chip.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_field.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
import '../keys.dart' as keys;
import 'overwrite_confirm_dialog.dart';
class GenerateKeyDialog extends ConsumerStatefulWidget {
@ -157,7 +158,7 @@ class _GenerateKeyDialogState extends ConsumerState<GenerateKeyDialog> {
style: textTheme.bodyLarge,
),
Text(l10n.p_subject_desc),
TextField(
AppTextField(
autofocus: true,
key: keys.subjectField,
decoration: InputDecoration(

View File

@ -24,9 +24,10 @@ import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/app_text_field.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
import '../keys.dart' as keys;
import 'cert_info_view.dart';
import 'overwrite_confirm_dialog.dart';
@ -122,7 +123,7 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.p_password_protected_file),
TextField(
AppTextField(
autofocus: true,
obscureText: true,
autofillHints: const [AutofillHints.password],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,11 +24,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../widgets/app_text_field.dart';
import '../../widgets/app_text_form_field.dart';
import '../../widgets/choice_filter_chip.dart';
import '../../widgets/responsive_dialog.dart';
import '../keys.dart' as keys;
import '../models.dart';
import '../state.dart';
import '../keys.dart' as keys;
import 'pin_dialog.dart';
class ManageKeyDialog extends ConsumerStatefulWidget {
@ -151,7 +153,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
children: [
Text(l10n.p_change_management_key_desc),
if (protected)
TextField(
AppTextField(
autofocus: true,
obscureText: true,
autofillHints: const [AutofillHints.password],
@ -175,7 +177,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
},
),
if (!protected)
TextFormField(
AppTextFormField(
key: keys.managementKeyField,
autofocus: !_defaultKeyUsed,
autofillHints: const [AutofillHints.password],
@ -219,7 +221,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
});
},
),
TextField(
AppTextField(
key: keys.newPinPukField,
autofocus: _defaultKeyUsed,
autofillHints: const [AutofillHints.newPassword],

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022-2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../widgets/responsive_dialog.dart';
import '../state.dart';
import '../../widgets/app_text_field.dart';
import '../keys.dart' as keys;
import '../state.dart';
enum ManageTarget { pin, puk, unblock }
@ -101,7 +102,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
Text(widget.target == ManageTarget.pin
? l10n.p_enter_current_pin_or_reset
: l10n.p_enter_current_puk_or_reset),
TextField(
AppTextField(
autofocus: true,
obscureText: true,
maxLength: 8,
@ -131,7 +132,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
),
Text(l10n.p_enter_new_piv_pin_puk(
widget.target == ManageTarget.puk ? l10n.s_puk : l10n.s_pin)),
TextField(
AppTextField(
key: keys.newPinPukField,
obscureText: true,
maxLength: 8,
@ -157,7 +158,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
}
},
),
TextField(
AppTextField(
key: keys.confirmPinPukField,
obscureText: true,
maxLength: 8,

View File

@ -21,8 +21,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/models.dart';
import '../../exception/cancellation_exception.dart';
import '../../widgets/responsive_dialog.dart';
import '../state.dart';
import '../../widgets/app_text_field.dart';
import '../keys.dart' as keys;
import '../state.dart';
class PinDialog extends ConsumerStatefulWidget {
final DevicePath devicePath;
@ -85,7 +86,7 @@ class _PinDialogState extends ConsumerState<PinDialog> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.p_pin_required_desc),
TextField(
AppTextField(
autofocus: true,
obscureText: _isObscure,
maxLength: 8,

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:flutter/material.dart';
/// TextField without autocorrect and suggestions
class AppTextField extends TextField {
const AppTextField({
// default settings to turn off autocorrect
super.autocorrect = false,
super.enableSuggestions = false,
super.keyboardType = TextInputType.text,
// forward other TextField parameters
super.key,
super.controller,
super.focusNode,
super.undoController,
super.decoration,
super.textInputAction,
super.textCapitalization,
super.style,
super.strutStyle,
super.textAlign,
super.textAlignVertical,
super.textDirection,
super.readOnly,
super.toolbarOptions,
super.showCursor,
super.autofocus,
super.obscuringCharacter,
super.obscureText,
super.smartDashesType,
super.smartQuotesType,
super.maxLines,
super.minLines,
super.expands,
super.maxLength,
super.maxLengthEnforcement,
super.onChanged,
super.onEditingComplete,
super.onSubmitted,
super.onAppPrivateCommand,
super.inputFormatters,
super.enabled,
super.cursorWidth,
super.cursorHeight,
super.cursorRadius,
super.cursorOpacityAnimates,
super.cursorColor,
super.selectionHeightStyle,
super.selectionWidthStyle,
super.keyboardAppearance,
super.scrollPadding,
super.dragStartBehavior,
super.enableInteractiveSelection,
super.selectionControls,
super.onTap,
super.onTapOutside,
super.mouseCursor,
super.buildCounter,
super.scrollController,
super.scrollPhysics,
super.autofillHints,
super.contentInsertionConfiguration,
super.clipBehavior,
super.restorationId,
super.scribbleEnabled,
super.enableIMEPersonalizedLearning,
super.contextMenuBuilder,
super.canRequestFocus,
super.spellCheckConfiguration,
super.magnifierConfiguration,
});
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:flutter/material.dart';
/// TextFormField without autocorrect and suggestions
class AppTextFormField extends TextFormField {
AppTextFormField({
// default settings to turn off autocorrect
super.autocorrect = false,
super.enableSuggestions = false,
super.keyboardType = TextInputType.text,
// forward other TextField parameters
super.key,
super.controller,
super.initialValue,
super.focusNode,
super.decoration,
super.textCapitalization,
super.textInputAction,
super.style,
super.strutStyle,
super.textDirection,
super.textAlign,
super.textAlignVertical,
super.autofocus,
super.readOnly,
super.toolbarOptions,
super.showCursor,
super.obscuringCharacter,
super.obscureText,
super.smartDashesType,
super.smartQuotesType,
super.maxLengthEnforcement,
super.maxLines,
super.minLines,
super.expands,
super.maxLength,
super.onChanged,
super.onTap,
super.onTapOutside,
super.onEditingComplete,
super.onFieldSubmitted,
super.onSaved,
super.validator,
super.inputFormatters,
super.enabled,
super.cursorWidth,
super.cursorHeight,
super.cursorRadius,
super.cursorColor,
super.keyboardAppearance,
super.scrollPadding,
super.enableInteractiveSelection,
super.selectionControls,
super.buildCounter,
super.scrollPhysics,
super.autofillHints,
super.autovalidateMode,
super.scrollController,
super.restorationId,
super.enableIMEPersonalizedLearning,
super.mouseCursor,
super.contextMenuBuilder,
super.spellCheckConfiguration,
super.magnifierConfiguration,
super.undoController,
super.onAppPrivateCommand,
super.cursorOpacityAnimates,
super.selectionHeightStyle,
super.selectionWidthStyle,
super.dragStartBehavior,
super.contentInsertionConfiguration,
super.clipBehavior,
super.scribbleEnabled,
super.canRequestFocus,
});
}