change password screen ui

This commit is contained in:
jayesh
2024-06-10 12:18:54 +05:30
parent aad0f3b87b
commit 1a77f65377
15 changed files with 201 additions and 174 deletions

View File

@@ -121,7 +121,6 @@ class AppText {
static const String declineText = "Decline";
//Forgot Password
static const String restorePasswordText = "Restore password";
static const String toRestorePasswordPleaseEnterPhoneNumber =
"To restore password please enter phone number";
@@ -158,6 +157,10 @@ class AppText {
static const String noText = "No";
static const String yesText = "Yes";
static const String pinUpdatedSucess = "Pin updated Sucessfully !";
static const String passwordUpdatedSucess = "Password updated Sucessfully !";
static const String changePasswordText = "Change Password";
static const String newPasswordText = "New Password";
static const String currentPsswordText = "Current Password";
//Contact Admin
static const String byPhoneText = "By phone";

View File

@@ -40,7 +40,11 @@ class PrivacySettingsSection extends StatelessWidget {
),
const Gap(12),
SettingsListItem(
onTapFunc: () {},
onTapFunc: () {
goRouter.pushNamed(
RouteName.changePasswordScreen,
);
},
icon: AppImages.resetPasswordIcon,
title: AppText.resetPasswordText,
trailing: "",

View File

@@ -1,12 +1,15 @@
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'restore_password_event.dart';
import 'restore_password_state.dart';
import 'change_password_event.dart';
import 'change_password_state.dart';
class RestorePasswordBloc
extends Bloc<RestorePasswordEvent, RestorePasswordState> {
class ChangePasswordBloc
extends Bloc<ChangePasswordEvent, ChangePasswordState> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController currentPasswordTextField =
TextEditingController();
final TextEditingController passwordTextField = TextEditingController();
final TextEditingController repeatPasswordTextField = TextEditingController();
@@ -14,47 +17,51 @@ class RestorePasswordBloc
return formKey;
}
RestorePasswordBloc() : super(RestorePasswordInitial()) {
ChangePasswordBloc() : super(ChangePasswordInitial()) {
currentPasswordTextField.addListener(_onFormFieldChanged);
passwordTextField.addListener(_onFormFieldChanged);
repeatPasswordTextField.addListener(_onFormFieldChanged);
on<RestorePasswordFormChanged>(_onLoginFormChanged);
on<RestorePasswordSubmitted>((event, emit) async {
on<ChangePasswordFormChanged>(_onLoginFormChanged);
on<ChangePasswordSubmitted>((event, emit) async {
if (!formKey.currentState!.validate()) {
return;
}
emit(RestorePasswordLoading());
emit(ChangePasswordLoading());
try {
// Simulate API call
await Future.delayed(const Duration(seconds: 2));
// Replace the next line with actual API call
final isSuccess = await _mockLoginApi(event.password);
if (isSuccess) {
emit(RestorePasswordSuccess());
emit(ChangePasswordSuccess());
} else {
emit(const RestorePasswordFailure(
emit(const ChangePasswordFailure(
"Failed. Please check your credentials."));
}
} catch (e) {
emit(RestorePasswordFailure(e.toString()));
emit(ChangePasswordFailure(e.toString()));
}
});
}
void _onFormFieldChanged() {
add(RestorePasswordFormChanged(
add(ChangePasswordFormChanged(
currentPasswordTextField.text,
passwordTextField.text,
repeatPasswordTextField.text,
));
}
void _onLoginFormChanged(
RestorePasswordFormChanged event, Emitter<RestorePasswordState> emit) {
final areFieldsFilled =
event.password.isNotEmpty && event.repeatPassword.isNotEmpty;
emit(RestorePasswordFieldsState(areFieldsFilled));
ChangePasswordFormChanged event, Emitter<ChangePasswordState> emit) {
final areFieldsFilled = event.currentPassword.isNotEmpty &&
event.password.isNotEmpty &&
event.repeatPassword.isNotEmpty;
emit(ChangePasswordFieldsState(areFieldsFilled));
}
// Method to reset text fields
void resetFields() {
currentPasswordTextField.clear();
passwordTextField.clear();
repeatPasswordTextField.clear();
}
@@ -69,6 +76,7 @@ class RestorePasswordBloc
@override
Future<void> close() {
passwordTextField.dispose();
currentPasswordTextField.dispose();
repeatPasswordTextField.dispose();
return super.close();

View File

@@ -0,0 +1,35 @@
import 'package:equatable/equatable.dart';
abstract class ChangePasswordEvent extends Equatable {
const ChangePasswordEvent();
@override
List<Object> get props => [];
}
class ChangePasswordSubmitted extends ChangePasswordEvent {
final String currentPassword;
final String password;
final String repeatPassword;
const ChangePasswordSubmitted(
this.password,
this.currentPassword,
this.repeatPassword,
);
@override
List<Object> get props => [currentPassword, password, repeatPassword];
}
class ChangePasswordFormChanged extends ChangePasswordEvent {
final String currentPassword;
final String password;
final String repeatPassword;
const ChangePasswordFormChanged(
this.currentPassword, this.password, this.repeatPassword);
@override
List<Object> get props => [currentPassword, password, repeatPassword];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
abstract class ChangePasswordState extends Equatable {
const ChangePasswordState();
@override
List<Object> get props => [];
}
class ChangePasswordInitial extends ChangePasswordState {}
class ChangePasswordLoading extends ChangePasswordState {}
class ChangePasswordSuccess extends ChangePasswordState {}
class ChangePasswordFailure extends ChangePasswordState {
final String error;
const ChangePasswordFailure(this.error);
@override
List<Object> get props => [error];
}
class ChangePasswordFieldsState extends ChangePasswordState {
final bool areFieldsFilled;
const ChangePasswordFieldsState(this.areFieldsFilled);
@override
List<Object> get props => [areFieldsFilled];
}

View File

@@ -1,31 +0,0 @@
import 'package:equatable/equatable.dart';
abstract class RestorePasswordEvent extends Equatable {
const RestorePasswordEvent();
@override
List<Object> get props => [];
}
class RestorePasswordSubmitted extends RestorePasswordEvent {
final String password;
final String repeatPassword;
const RestorePasswordSubmitted(
this.password,
this.repeatPassword,
);
@override
List<Object> get props => [password, repeatPassword];
}
class RestorePasswordFormChanged extends RestorePasswordEvent {
final String password;
final String repeatPassword;
const RestorePasswordFormChanged(this.password, this.repeatPassword);
@override
List<Object> get props => [password, repeatPassword];
}

View File

@@ -1,32 +0,0 @@
import 'package:equatable/equatable.dart';
abstract class RestorePasswordState extends Equatable {
const RestorePasswordState();
@override
List<Object> get props => [];
}
class RestorePasswordInitial extends RestorePasswordState {}
class RestorePasswordLoading extends RestorePasswordState {}
class RestorePasswordSuccess extends RestorePasswordState {}
class RestorePasswordFailure extends RestorePasswordState {
final String error;
const RestorePasswordFailure(this.error);
@override
List<Object> get props => [error];
}
class RestorePasswordFieldsState extends RestorePasswordState {
final bool areFieldsFilled;
const RestorePasswordFieldsState(this.areFieldsFilled);
@override
List<Object> get props => [areFieldsFilled];
}

View File

@@ -1,23 +1,15 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import '../widgets/restore_password_bottom_section.dart';
import '../widgets/restore_password_form.dart';
import '../widgets/restore_password_top_section.dart';
import '../widgets/change_password_bottom_section.dart';
import '../widgets/change_password_form.dart';
class ChangePasswordLayout extends StatelessWidget {
const ChangePasswordLayout({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: const [
RestorePasswordTopSection(),
RestorePasswordForm(),
Gap(150),
RestorePasswordBottomSection(),
],
));
return const Scaffold(
bottomNavigationBar: RestorePasswordBottomSection(),
body: RestorePasswordForm());
}
}

View File

@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/restore_password_bloc.dart';
import '../../../../core/styles/app_text.dart';
import '../../../../shared/components/appbar_widget.dart';
import '../bloc/change_password_bloc.dart';
import 'change_password_layout.dart';
class ChangePasswordScreen extends StatelessWidget {
@@ -10,13 +12,17 @@ class ChangePasswordScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: const AppBarWidget(
height: 75,
titleTxt: AppText.changePasswordText,
),
resizeToAvoidBottomInset: false,
body: MultiBlocProvider(
// Define the providers for the OnboardingBloc and other blocs
providers: [
BlocProvider(
// Create an instance of the OnboardingBloc
create: (context) => RestorePasswordBloc(),
create: (context) => ChangePasswordBloc(),
),
],
child: const ChangePasswordLayout(),

View File

@@ -5,16 +5,14 @@ import 'package:gap/gap.dart';
import 'package:tanami_app/shared/components/loader.dart';
import 'package:tanami_app/shared/components/toast_message.dart';
import '../../../../core/routes/route_name.dart';
import '../../../../core/routes/routes.dart';
import '../../../../core/styles/app_color.dart';
import '../../../../core/styles/app_text.dart';
import '../../../../shared/components/button_widget.dart';
import '../../../../shared/components/text_widget.dart';
import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart';
import '../bloc/restore_password_bloc.dart';
import '../bloc/restore_password_event.dart';
import '../bloc/restore_password_state.dart';
import '../bloc/change_password_bloc.dart';
import '../bloc/change_password_event.dart';
import '../bloc/change_password_state.dart';
class RestorePasswordBottomSection extends StatelessWidget {
const RestorePasswordBottomSection({
@@ -23,21 +21,18 @@ class RestorePasswordBottomSection extends StatelessWidget {
@override
Widget build(BuildContext context) {
final radioBloc = context.read<RadioBloc>();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
BlocConsumer<RestorePasswordBloc, RestorePasswordState>(
BlocConsumer<ChangePasswordBloc, ChangePasswordState>(
listener: (context, state) {
if (state is RestorePasswordLoading) {
if (state is ChangePasswordLoading) {
Loader.loader(context);
} else if (state is RestorePasswordSuccess) {
successToastMessage(context, "Password resetted successful !");
} else if (state is ChangePasswordSuccess) {
successToastMessage(context, "Password changed successful !");
goRouter.pop();
radioBloc.resetSelection();
goRouter.goNamed(RouteName.loginScreen, pathParameters: {
"fromScreen": "registerStep",
});
} else if (state is RestorePasswordFailure) {
goRouter.pop();
} else if (state is ChangePasswordFailure) {
goRouter.pop();
errorToastMessage(
context,
@@ -47,10 +42,10 @@ class RestorePasswordBottomSection extends StatelessWidget {
},
builder: (context, state) {
bool isButtonEnabled = false;
if (state is RestorePasswordFieldsState) {
if (state is ChangePasswordFieldsState) {
isButtonEnabled = state.areFieldsFilled;
} else if (state is RestorePasswordSuccess ||
state is RestorePasswordFailure) {
} else if (state is ChangePasswordSuccess ||
state is ChangePasswordFailure) {
isButtonEnabled = true;
}
return Container(
@@ -65,13 +60,21 @@ class RestorePasswordBottomSection extends StatelessWidget {
: AppColor.inactiveBtnTxtColor,
function: () {
isButtonEnabled
? context.read<RestorePasswordBloc>().add(
RestorePasswordSubmitted(
context
.read<RestorePasswordBloc>()
.passwordTextField
.text,
""),
? context.read<ChangePasswordBloc>().add(
ChangePasswordSubmitted(
context
.read<ChangePasswordBloc>()
.currentPasswordTextField
.text,
context
.read<ChangePasswordBloc>()
.passwordTextField
.text,
context
.read<ChangePasswordBloc>()
.repeatPasswordTextField
.text,
),
)
: null;
},

View File

@@ -2,17 +2,22 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:tanami_app/core/styles/app_text.dart';
import 'package:tanami_app/features/forgotPassword/presentation/bloc/restore_password_bloc.dart';
import '../../../../core/routes/route_name.dart';
import '../../../../core/routes/routes.dart';
import '../../../../core/styles/app_color.dart';
import '../../../../shared/components/bloc/password_field/password_visibility_bloc.dart';
import '../../../../shared/components/button_widget.dart';
import '../../../../shared/components/form_label_textfield.dart';
import '../../../../shared/components/text_widget.dart';
import '../bloc/change_password_bloc.dart';
class RestorePasswordForm extends StatelessWidget {
const RestorePasswordForm({super.key});
@override
Widget build(BuildContext context) {
final restorePasswordBloc = context.read<RestorePasswordBloc>();
final restorePasswordBloc = context.read<ChangePasswordBloc>();
// Reset fields when the screen is built
restorePasswordBloc.resetFields();
@@ -33,7 +38,18 @@ class RestorePasswordForm extends StatelessWidget {
create: (_) => PasswordVisibilityBloc(),
child: FormLabelTextField(
hintText: AppText.enterPassword,
title: AppText.password,
title: AppText.currentPsswordText,
type: AppText.password.toLowerCase(),
textEditingController:
restorePasswordBloc.currentPasswordTextField,
),
),
const Gap(12),
BlocProvider(
create: (_) => PasswordVisibilityBloc(),
child: FormLabelTextField(
hintText: AppText.enterPassword,
title: AppText.newPasswordText,
type: AppText.password.toLowerCase(),
textEditingController: restorePasswordBloc.passwordTextField,
),
@@ -42,13 +58,26 @@ class RestorePasswordForm extends StatelessWidget {
BlocProvider(
create: (_) => PasswordVisibilityBloc(),
child: FormLabelTextField(
hintText: AppText.repeatPasswordText,
hintText: AppText.enterPassword,
title: AppText.repeatPasswordText,
type: AppText.password.toLowerCase(),
textEditingController:
restorePasswordBloc.repeatPasswordTextField,
),
),
const Gap(12),
Align(
alignment: Alignment.topRight,
child: ButtonWidget().textBtn(
function: () {
goRouter.pushNamed(
RouteName.forgotPasswordPhoneVerificationScreen);
},
text: TextWidget().text15W400(AppText.forgorPassword,
clr: AppColor.forgotPassButtonColor,
textDecoration: TextDecoration.underline)),
),
const Gap(20),
],
),
),

View File

@@ -1,41 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gap/gap.dart';
import 'package:tanami_app/core/styles/app_color.dart';
import 'package:tanami_app/core/styles/app_images.dart';
import 'package:tanami_app/core/styles/app_text.dart';
import 'package:tanami_app/shared/components/text_widget.dart';
class RestorePasswordTopSection extends StatelessWidget {
const RestorePasswordTopSection({
super.key,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Gap(85),
Center(
child: SvgPicture.asset(
AppImages.weclomeLogo,
),
),
const Gap(60),
TextWidget().text20W700(
AppText.restorePasswordText,
clr: AppColor.charcoalColor,
),
const Gap(10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 75),
child: TextWidget().text14W500(
AppText.createNewPasswordText,
clr: AppColor.smokeGrayColor,
),
),
],
);
}
}

View File

@@ -1,7 +1,6 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:tanami_app/core/styles/app_text.dart';
import '../../../../core/utils/secure/secure_storage_service.dart';
part 'pin_event.dart';
@@ -12,13 +11,20 @@ class PinBloc extends Bloc<PinEvent, PinState> {
PinBloc({required this.secureStorageService})
: super(const PinState(
pin: '', pinComplete: false, isVerified: false, error: '')) {
pin: '',
pinComplete: false,
isVerified: false,
error: '',
verifiedOnce: false)) {
on<NumberPressed>((event, emit) {
final newPin = state.pin + event.number;
if (newPin.length <= 6) {
emit(state.copyWith(
pin: newPin, pinComplete: newPin.length == 6, error: ''));
pin: newPin,
pinComplete: newPin.length == 6,
error: '',
verifiedOnce: false));
if (newPin.length == 6) {
add(VerifyPinPressed(newPin));
@@ -30,7 +36,10 @@ class PinBloc extends Bloc<PinEvent, PinState> {
if (state.pin.isNotEmpty) {
final newPin = state.pin.substring(0, state.pin.length - 1);
emit(state.copyWith(
pin: newPin, pinComplete: newPin.length == 6, error: ''));
pin: newPin,
pinComplete: newPin.length == 6,
error: '',
verifiedOnce: false));
}
});
@@ -42,11 +51,12 @@ class PinBloc extends Bloc<PinEvent, PinState> {
final storedPin = await secureStorageService.read('pin_code');
if (storedPin == event.pin) {
emit(state.copyWith(isVerified: true, error: ''));
emit(state.copyWith(isVerified: true, error: '', verifiedOnce: true));
} else {
emit(state.copyWith(
isVerified: false,
error: AppText.incorrectPinCode,
verifiedOnce: true,
));
}
});

View File

@@ -5,12 +5,14 @@ class PinState extends Equatable {
final bool pinComplete;
final bool isVerified;
final String error;
final bool verifiedOnce; // New variable to prevent double listener calls
const PinState({
required this.pin,
required this.pinComplete,
required this.isVerified,
required this.error,
required this.verifiedOnce,
});
PinState copyWith({
@@ -18,15 +20,17 @@ class PinState extends Equatable {
bool? pinComplete,
bool? isVerified,
String? error,
bool? verifiedOnce,
}) {
return PinState(
pin: pin ?? this.pin,
pinComplete: pinComplete ?? this.pinComplete,
isVerified: isVerified ?? this.isVerified,
error: error ?? this.error,
verifiedOnce: verifiedOnce ?? this.verifiedOnce,
);
}
@override
List<Object> get props => [pin, pinComplete, isVerified, error];
List<Object> get props => [pin, pinComplete, isVerified, error, verifiedOnce];
}

View File

@@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
@@ -26,10 +28,13 @@ class PinKey extends StatelessWidget {
const Gap(20),
BlocConsumer<PinBloc, PinState>(
listener: (context, state) {
if (state.pinComplete) {
if (state.pinComplete &&
state.error.isEmpty &&
!state.verifiedOnce) {
if (fromScreen == "login") {
goRouter.pushNamed(RouteName.mainScreen);
} else if (fromScreen == "reset-pin") {
log("Running this");
successToastMessage(context, AppText.pinUpdatedSucess);
goRouter.pop();
} else {