Files
CityCards_Partner_Flutter/lib/login/views/reset_password_page.dart

289 lines
12 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../constants/app_assets.dart';
import '../../constants/app_colors.dart';
import '../../core/app_router.dart';
import '../../custome_widgets/custom_button.dart';
import '../../custome_widgets/custom_textfield.dart';
import '../blocs/reset_password/reset_password_bloc.dart';
class ResetPasswordPage extends StatefulWidget {
final String email;
const ResetPasswordPage({super.key, required this.email});
@override
State<ResetPasswordPage> createState() => _ResetPasswordPageState();
}
class _ResetPasswordPageState extends State<ResetPasswordPage> {
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
final _passwordFocusNode = FocusNode();
final _confirmFocusNode = FocusNode();
@override
void dispose() {
_passwordController.dispose();
_confirmPasswordController.dispose();
_passwordFocusNode.dispose();
_confirmFocusNode.dispose();
super.dispose();
}
void _onResetPressed(BuildContext context, ResetPasswordState state) {
FocusManager.instance.primaryFocus?.unfocus();
final password = _passwordController.text;
final confirmPassword = _confirmPasswordController.text;
if (!state.isPasswordValid) {
context.read<ResetPasswordBloc>().add(
const ResetPasswordErrorToggled(true,
error: "Please fulfill all password requirements"),
);
return;
}
if (password != confirmPassword) {
context.read<ResetPasswordBloc>().add(
const ResetConfirmPasswordErrorToggled(true,
error: "Passwords do not match"),
);
return;
}
context.read<ResetPasswordBloc>().add(
ResetPasswordSubmitted(
emailAddress: widget.email,
newPassword: password,
),
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
behavior: HitTestBehavior.translucent,
child: Scaffold(
backgroundColor: AppColors.backgroundWhite,
body: BlocConsumer<ResetPasswordBloc, ResetPasswordState>(
listener: (context, state) {
if (state.status == ResetPasswordStatus.success) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Password reset successfully!"),
backgroundColor: AppColors.successGreen,
),
);
Navigator.pushNamedAndRemoveUntil(
context,
AppRouter.login,
(route) => false,
);
} else if (state.status == ResetPasswordStatus.failure) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage ?? "Reset failed"),
backgroundColor: Colors.redAccent,
),
);
}
},
builder: (context, state) {
final isLoading = state.status == ResetPasswordStatus.loading;
return SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 40.h),
// ===== LOGO SECTION =====
Center(
child: Column(
children: [
Image.asset(
AppAssets.appIcon,
height: 60.h,
),
SizedBox(height: 8.h),
Text(
"Partners App",
style: GoogleFonts.poppins(
color: AppColors.primaryRed,
fontSize: 20.sp,
fontWeight: FontWeight.w500,
),
),
],
),
),
SizedBox(height: 60.h),
// ===== HEADER TEXT =====
Text(
"Reset your password",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
color: AppColors.black,
fontSize: 24.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 8.h),
Text(
"Almost there — just set your new password",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
color: AppColors.textGrey,
fontSize: 16.sp,
),
),
SizedBox(height: 48.h),
// ===== NEW PASSWORD FIELD =====
CustomTextField(
label: 'New Password',
hintText: 'Enter new password',
controller: _passwordController,
focusNode: _passwordFocusNode,
prefixIcon: Icons.lock_outline,
isPassword: true,
isPasswordVisible: state.isPasswordVisible,
onTogglePasswordVisibility: () {
context.read<ResetPasswordBloc>().add(
const ResetPasswordVisibilityToggled(),
);
},
hasError: state.showPasswordError,
errorText: state.passwordErrorText,
textInputAction: TextInputAction.next,
readOnly: isLoading,
onChanged: (val) {
context
.read<ResetPasswordBloc>()
.add(PasswordChanged(val));
if (state.showPasswordError) {
context.read<ResetPasswordBloc>().add(
const ResetPasswordErrorToggled(false));
}
},
),
SizedBox(height: 20.h),
// ===== VALIDATION BOX =====
Container(
padding: EdgeInsets.all(16.r),
decoration: BoxDecoration(
color: AppColors.bgLightGrey,
borderRadius: BorderRadius.circular(12.r),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Password must contain:",
style: GoogleFonts.poppins(
fontSize: 13.sp,
color: AppColors.textGrey,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 12.h),
_buildValidationRow(
"At least 8 characters", state.hasMinLength),
_buildValidationRow(
"One uppercase letter", state.hasUppercase),
_buildValidationRow(
"One lowercase letter", state.hasLowercase),
_buildValidationRow(
"One number", state.hasNumber),
_buildValidationRow(
"One special character", state.hasSpecial),
],
),
),
SizedBox(height: 20.h),
// ===== CONFIRM PASSWORD FIELD =====
CustomTextField(
label: 'Confirm New Password',
hintText: 'Enter your password',
controller: _confirmPasswordController,
focusNode: _confirmFocusNode,
prefixIcon: Icons.lock_outline,
isPassword: true,
isPasswordVisible: state.isConfirmPasswordVisible,
onTogglePasswordVisibility: () {
context.read<ResetPasswordBloc>().add(
const ConfirmPasswordVisibilityToggled(),
);
},
hasError: state.showConfirmPasswordError,
errorText: state.confirmPasswordErrorText,
textInputAction: TextInputAction.done,
readOnly: isLoading,
onChanged: (val) {
if (state.showConfirmPasswordError) {
context.read<ResetPasswordBloc>().add(
const ResetConfirmPasswordErrorToggled(
false));
}
},
onSubmitted: (_) => _onResetPressed(context, state),
),
SizedBox(height: 24.h),
],
),
),
),
// ===== RESET BUTTON =====
CustomButton(
text: "Reset Password",
isLoading: isLoading,
onPressed: () => _onResetPressed(context, state),
),
SizedBox(height: 24.h),
],
),
),
);
},
),
),
);
}
Widget _buildValidationRow(String text, bool isValid) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: Row(
children: [
Icon(
isValid ? Icons.check_circle : Icons.circle,
size: 16.sp,
color: isValid ? AppColors.successGreen : const Color(0xFFD1D1D1),
),
SizedBox(width: 10.w),
Text(
text,
style: GoogleFonts.poppins(
fontSize: 13.sp,
color: isValid ? AppColors.successGreen : AppColors.textGrey,
),
),
],
),
);
}
}