fix change password fields and validation
This commit is contained in:
@@ -16,6 +16,9 @@ class ChangePasswordScreen extends ConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
||||||
final theme = ref.watch(themePortProvider);
|
final theme = ref.watch(themePortProvider);
|
||||||
|
final password = ref.watch(
|
||||||
|
changePasswordViewModelProvider.select((s)=>s.newPassword)
|
||||||
|
);
|
||||||
|
|
||||||
return LegacyPageLayout(
|
return LegacyPageLayout(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
@@ -28,11 +31,11 @@ class ChangePasswordScreen extends ConsumerWidget {
|
|||||||
child: SingleChildScrollView(child: Column(
|
child: SingleChildScrollView(child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const _PasswordSection(),
|
|
||||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
|
||||||
const _NewPasswordSection(),
|
const _NewPasswordSection(),
|
||||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||||
const _RepeatPasswordSection(),
|
const _RepeatPasswordSection(),
|
||||||
|
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||||
|
_PasswordCriteriaList(password: password),
|
||||||
const _ErrorMessageSection()
|
const _ErrorMessageSection()
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
@@ -42,29 +45,6 @@ class ChangePasswordScreen extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PasswordSection extends ConsumerWidget {
|
|
||||||
|
|
||||||
const _PasswordSection();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
|
|
||||||
final vm = ref.read(changePasswordViewModelProvider.notifier);
|
|
||||||
final showPassword = ref.watch(
|
|
||||||
changePasswordViewModelProvider.select((s)=>s.showCurrentPassword)
|
|
||||||
);
|
|
||||||
|
|
||||||
return CustomTextField(
|
|
||||||
controller: vm.currentPasswordController,
|
|
||||||
hint: '********',
|
|
||||||
label: context.translate(I18n.password),
|
|
||||||
showPassword: showPassword,
|
|
||||||
onVisibilityChanged: vm.toggleCurrentPasswordVisibility,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NewPasswordSection extends ConsumerWidget {
|
class _NewPasswordSection extends ConsumerWidget {
|
||||||
|
|
||||||
const _NewPasswordSection();
|
const _NewPasswordSection();
|
||||||
@@ -111,6 +91,89 @@ class _RepeatPasswordSection extends ConsumerWidget {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PasswordCriteriaList extends StatelessWidget {
|
||||||
|
final String password;
|
||||||
|
|
||||||
|
const _PasswordCriteriaList({required this.password});
|
||||||
|
|
||||||
|
static final _upperRegex = RegExp(r'[A-Z]');
|
||||||
|
static final _digitRegex = RegExp(r'[0-9]');
|
||||||
|
static final _specialRegex =
|
||||||
|
RegExp(r'[!@#$%^&*(),.?":{}|<>\-_+=\[\]\\\/~`]');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final hasInput = password.isNotEmpty;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
_CriteriaRow(
|
||||||
|
label: context.translate(I18n.passwordLength),
|
||||||
|
met: password.length >= 8,
|
||||||
|
hasInput: hasInput,
|
||||||
|
),
|
||||||
|
_CriteriaRow(
|
||||||
|
label: context.translate(I18n.passwordCapital),
|
||||||
|
met: _upperRegex.hasMatch(password),
|
||||||
|
hasInput: hasInput,
|
||||||
|
),
|
||||||
|
_CriteriaRow(
|
||||||
|
label: context.translate(I18n.passwordNumber),
|
||||||
|
met: _digitRegex.hasMatch(password),
|
||||||
|
hasInput: hasInput,
|
||||||
|
),
|
||||||
|
_CriteriaRow(
|
||||||
|
label: context.translate(I18n.passwordSpecial),
|
||||||
|
met: _specialRegex.hasMatch(password),
|
||||||
|
hasInput: hasInput,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CriteriaRow extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final bool met;
|
||||||
|
final bool hasInput;
|
||||||
|
|
||||||
|
const _CriteriaRow({
|
||||||
|
required this.label,
|
||||||
|
required this.met,
|
||||||
|
required this.hasInput,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final Color color;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
if (!hasInput) {
|
||||||
|
color = Colors.grey;
|
||||||
|
icon = Icons.circle_outlined;
|
||||||
|
} else if (met) {
|
||||||
|
color = Colors.green;
|
||||||
|
icon = Icons.check_circle;
|
||||||
|
} else {
|
||||||
|
color = Colors.red.shade400;
|
||||||
|
icon = Icons.cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Icon(icon, size: 16, color: color),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(fontSize: 13, color: color),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _ErrorMessageSection extends ConsumerWidget {
|
class _ErrorMessageSection extends ConsumerWidget {
|
||||||
|
|
||||||
const _ErrorMessageSection();
|
const _ErrorMessageSection();
|
||||||
@@ -136,7 +199,9 @@ class _ErrorMessageSection extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else return SizedBox.shrink();
|
} else {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ NotifierProvider.autoDispose<ChangePasswordViewModel, ChangePasswordViewState>(
|
|||||||
class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
||||||
late final ChangePasswordUseCase _changePasswordUseCase;
|
late final ChangePasswordUseCase _changePasswordUseCase;
|
||||||
|
|
||||||
late final TextEditingController currentPasswordController;
|
|
||||||
late final TextEditingController newPasswordController;
|
late final TextEditingController newPasswordController;
|
||||||
late final TextEditingController repeatPasswordController;
|
late final TextEditingController repeatPasswordController;
|
||||||
late final TextEditingController passwordController;
|
late final TextEditingController passwordController;
|
||||||
@@ -29,10 +28,6 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initControllers() {
|
void _initControllers() {
|
||||||
|
|
||||||
currentPasswordController = TextEditingController();
|
|
||||||
currentPasswordController.addListener(_onCurrentPasswordChanged);
|
|
||||||
|
|
||||||
newPasswordController = TextEditingController();
|
newPasswordController = TextEditingController();
|
||||||
newPasswordController.addListener(_onNewPasswordChanged);
|
newPasswordController.addListener(_onNewPasswordChanged);
|
||||||
|
|
||||||
@@ -60,17 +55,6 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCurrentPasswordChanged() {
|
|
||||||
final value = currentPasswordController.text;
|
|
||||||
|
|
||||||
if (value == state.currentPassword) return;
|
|
||||||
|
|
||||||
state = state.copyWith(
|
|
||||||
currentPassword: value,
|
|
||||||
errorMessage: ''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onNewPasswordChanged() {
|
void _onNewPasswordChanged() {
|
||||||
final value = newPasswordController.text;
|
final value = newPasswordController.text;
|
||||||
|
|
||||||
@@ -94,11 +78,14 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _validateForm() {
|
bool _validateForm() {
|
||||||
if (state.currentPassword.trim().isEmpty){
|
final _upperRegex = RegExp(r'[A-Z]');
|
||||||
state = state.copyWith(errorMessage: 'errorMessageCurrentPasswordIsEmpty');
|
final _digitRegex = RegExp(r'[0-9]');
|
||||||
return false;
|
final _specialRegex =
|
||||||
}
|
RegExp(r'[!@#$%^&*(),.?":{}|<>\-_+=\[\]\\\/~`]');
|
||||||
if (state.newPassword.trim().isEmpty){
|
|
||||||
|
final password = state.newPassword.trim();
|
||||||
|
|
||||||
|
if (password.isEmpty){
|
||||||
state = state.copyWith(errorMessage: 'errorMessageNewPasswordIsEmpty');
|
state = state.copyWith(errorMessage: 'errorMessageNewPasswordIsEmpty');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -106,10 +93,26 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
|||||||
state = state.copyWith(errorMessage: 'errorMessageRepeatPasswordIsEmpty');
|
state = state.copyWith(errorMessage: 'errorMessageRepeatPasswordIsEmpty');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (state.newPassword.trim() != state.repeatPassword.trim()){
|
if (password != state.repeatPassword.trim()){
|
||||||
state = state.copyWith(errorMessage: 'errorMessagePasswordsDontMatch');
|
state = state.copyWith(errorMessage: 'errorMessagePasswordsDontMatch');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (password.length < 8){
|
||||||
|
state = state.copyWith(errorMessage: 'errorPasswordMinLength');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_upperRegex.hasMatch(password)) {
|
||||||
|
state = state.copyWith(errorMessage: 'errorPasswordUppercase');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_digitRegex.hasMatch(password)) {
|
||||||
|
state = state.copyWith(errorMessage: 'errorPasswordDigits');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_specialRegex.hasMatch(password)) {
|
||||||
|
state = state.copyWith(errorMessage: 'errorPasswordSpecial');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,9 +159,6 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void disposeControllers() {
|
void disposeControllers() {
|
||||||
currentPasswordController.removeListener(_onCurrentPasswordChanged);
|
|
||||||
currentPasswordController.dispose();
|
|
||||||
|
|
||||||
newPasswordController.removeListener(_onNewPasswordChanged);
|
newPasswordController.removeListener(_onNewPasswordChanged);
|
||||||
newPasswordController.dispose();
|
newPasswordController.dispose();
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ abstract class ChangePasswordViewState with _$ChangePasswordViewState {
|
|||||||
const factory ChangePasswordViewState({
|
const factory ChangePasswordViewState({
|
||||||
@Default(false) bool isLoading,
|
@Default(false) bool isLoading,
|
||||||
@Default(false) bool isComplete,
|
@Default(false) bool isComplete,
|
||||||
@Default(false) bool showCurrentPassword,
|
|
||||||
@Default(false) bool showNewPassword,
|
@Default(false) bool showNewPassword,
|
||||||
@Default(false) bool showRepeatedPassword,
|
@Default(false) bool showRepeatedPassword,
|
||||||
@Default('') String currentPassword,
|
|
||||||
@Default('') String newPassword,
|
@Default('') String newPassword,
|
||||||
@Default('') String repeatPassword,
|
@Default('') String repeatPassword,
|
||||||
@Default('') String errorMessage
|
@Default('') String errorMessage
|
||||||
|
|||||||
Reference in New Issue
Block a user