redesign for settings page
This commit is contained in:
152
frontend/pweb/lib/widgets/password/ui_controller.dart
Normal file
152
frontend/pweb/lib/widgets/password/ui_controller.dart
Normal file
@@ -0,0 +1,152 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fancy_password_field/fancy_password_field.dart';
|
||||
|
||||
import 'package:pweb/config/constants.dart';
|
||||
import 'package:pweb/models/state/control_state.dart';
|
||||
import 'package:pweb/widgets/password/password.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
class PasswordUiController extends StatefulWidget {
|
||||
final TextEditingController controller;
|
||||
final String? labelText;
|
||||
final ControlState state;
|
||||
|
||||
const PasswordUiController({
|
||||
required this.controller,
|
||||
this.labelText,
|
||||
this.state = ControlState.enabled,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PasswordUiController> createState() => _PasswordUiControllerState();
|
||||
}
|
||||
|
||||
class _PasswordUiControllerState extends State<PasswordUiController> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.controller.addListener(_onPasswordChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.controller.removeListener(_onPasswordChanged);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onPasswordChanged() => setState(() {});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isEnabled = widget.state == ControlState.enabled;
|
||||
final specialRule = _SpecialCharacterValidationRule(
|
||||
customText: AppLocalizations.of(
|
||||
context,
|
||||
)!.passwordValidationRuleSpecialCharacter,
|
||||
);
|
||||
final value = widget.controller.text;
|
||||
final missing = _allRules(context, specialRule)
|
||||
.where((rule) => !rule.validate(value))
|
||||
.map((rule) => rule.name)
|
||||
.toList(growable: false);
|
||||
final hasMissingRules = value.isNotEmpty && missing.isNotEmpty;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IgnorePointer(
|
||||
ignoring: !isEnabled,
|
||||
child: Opacity(
|
||||
opacity: isEnabled ? 1 : 0.6,
|
||||
child: Theme(
|
||||
data: hasMissingRules
|
||||
? _invalidTheme(context)
|
||||
: Theme.of(context),
|
||||
child: defaulRulesPasswordField(
|
||||
context,
|
||||
controller: widget.controller,
|
||||
labelText: widget.labelText,
|
||||
additionalRules: {specialRule},
|
||||
validationRuleBuilder: (_, _) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasMissingRules) ...[
|
||||
const SizedBox(height: 8),
|
||||
...missing.map(
|
||||
(ruleText) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: Text(
|
||||
'• $ruleText',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<ValidationRule> _allRules(
|
||||
BuildContext context,
|
||||
ValidationRule specialRule,
|
||||
) {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
return [
|
||||
DigitValidationRule(customText: loc.passwordValidationRuleDigit),
|
||||
UppercaseValidationRule(customText: loc.passwordValidationRuleUpperCase),
|
||||
LowercaseValidationRule(customText: loc.passwordValidationRuleLowerCase),
|
||||
MinCharactersValidationRule(
|
||||
Constants.minPasswordCharacters,
|
||||
customText: loc.passwordValidationRuleMinCharacters(
|
||||
Constants.minPasswordCharacters,
|
||||
),
|
||||
),
|
||||
specialRule,
|
||||
];
|
||||
}
|
||||
|
||||
ThemeData _invalidTheme(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final errorColor = theme.colorScheme.error;
|
||||
final border = OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: BorderSide(color: errorColor, width: 1.2),
|
||||
);
|
||||
|
||||
return theme.copyWith(
|
||||
inputDecorationTheme: theme.inputDecorationTheme.copyWith(
|
||||
enabledBorder: border,
|
||||
focusedBorder: border,
|
||||
errorBorder: border,
|
||||
focusedErrorBorder: border,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SpecialCharacterValidationRule extends ValidationRule {
|
||||
final String customText;
|
||||
|
||||
_SpecialCharacterValidationRule({required this.customText});
|
||||
|
||||
@override
|
||||
String get name => customText;
|
||||
|
||||
@override
|
||||
bool get showName => true;
|
||||
|
||||
@override
|
||||
bool validate(String value) => value.runes.any(_isAsciiSpecialCharacter);
|
||||
|
||||
bool _isAsciiSpecialCharacter(int code) =>
|
||||
(code >= 33 && code <= 47) ||
|
||||
(code >= 58 && code <= 64) ||
|
||||
(code >= 91 && code <= 96) ||
|
||||
(code >= 123 && code <= 126);
|
||||
}
|
||||
Reference in New Issue
Block a user