From a509583cb667f9a7994dadc5d786281a36daa3d6 Mon Sep 17 00:00:00 2001 From: SurendarSuri30 Date: Sat, 3 May 2025 14:40:33 +0530 Subject: [PATCH] forgot password done --- .../authentication/login/login_widget.dart | 385 +++++++++++++++++- 1 file changed, 372 insertions(+), 13 deletions(-) diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index 36d5904..c6a64d6 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -15,12 +15,29 @@ class LoginWidget extends StatefulWidget { @override _LoginWidgetState createState() => _LoginWidgetState(); } - +enum LoginStep { login, forgotEmail, otpReset } class _LoginWidgetState extends State { final _formKey = GlobalKey(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); + final TextEditingController _otpController = TextEditingController(); + final TextEditingController _newPasswordController = TextEditingController(); + final TextEditingController _confirmPasswordController = TextEditingController(); bool _obscureText = true; + bool _isForgotPassword = false; + bool _showOtpResetFields = false; + // 🔹 Login Step Enum and State Variable + LoginStep _loginStep = LoginStep.login; + + @override + void dispose() { + _emailController.dispose(); + _passwordController.dispose(); + _otpController.dispose(); + _newPasswordController.dispose(); + _confirmPasswordController.dispose(); + super.dispose(); + } Future storeUserDetails(String token) async { try { @@ -47,7 +64,7 @@ class _LoginWidgetState extends State { } } - void _login(BuildContext context) async { + Future _login(BuildContext context) async { if (_formKey.currentState!.validate()) { const String url = '$apiUrl/auth/login'; @@ -95,6 +112,106 @@ class _LoginWidgetState extends State { } } + Future _handleSubmit(BuildContext context) async { + print('2'); + if (!_formKey.currentState!.validate()) return; + print('3'); + if (_isForgotPassword && !_showOtpResetFields) { + print('11'); + // Step 1: Send OTP + final url = '$apiUrl/forgotPassword/verifyUser'; + try { + final response = await http.post(Uri.parse(url), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({'email': _emailController.text.trim()}), + ); + + if (response.statusCode == 200) { + setState(() => _showOtpResetFields = true); + setState(() { + _isForgotPassword = false; + _showOtpResetFields = true; + // _clearAllFields(); + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("OTP sent to your email")), + ); + } else { + print(response); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("${jsonDecode(response.body)['messages']['error']}")), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Error: $e"))); + } + + } else if (!_isForgotPassword && _showOtpResetFields) { + print('22'); + // Step 2: Verify OTP & Reset Password + final url = '$apiUrl/forgotPassword/changePassword'; + try { + print('21'); + final response = await http.post(Uri.parse(url), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'email': _emailController.text.trim(), + 'otp': _otpController.text.trim(), + 'new_password': _newPasswordController.text.trim(), + 'confirm_password': _confirmPasswordController.text.trim(), + }), + ); + + if (response.statusCode == 200) { + print('22'); + setState(() { + _isForgotPassword = false; + _showOtpResetFields = false; + _clearAllFields(); + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Password reset successfully")), + ); + + + } else { + print('23'); + print('otp wrong'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Reset failed: ${jsonDecode(response.body)['message']}")), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Error: $e"))); + } + + } else { + // Login flow + _login(context); + } + } + + void _clearAllFields() { + _emailController.clear(); + _passwordController.clear(); + _otpController.clear(); + _newPasswordController.clear(); + _confirmPasswordController.clear(); + } + + void _onSubmit(BuildContext context) async { + if (_formKey.currentState!.validate()) { + if (!_isForgotPassword && !_showOtpResetFields) { + await _login(context); + } else { + print('1'); + _handleSubmit(context); + } + } + } + + + @override /// Layout @@ -188,6 +305,7 @@ class _LoginWidgetState extends State { const SizedBox(height: 18), /// **Email Field** + if (!_isForgotPassword && !_showOtpResetFields) ...[ _buildLabel("Email Address"), TextFormField( controller: _emailController, @@ -277,22 +395,263 @@ class _LoginWidgetState extends State { ), ], ), - const SizedBox(height: 20), - Center( - child: Text( - "Forgot Password", - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w300, - fontFamily: "Nunito", - color: Color(0xFF212121), // Text color - decoration: TextDecoration.underline, // Underline the text + ] else if (_isForgotPassword && !_showOtpResetFields) ...[ + _buildLabel("Email Address"), + TextFormField( + controller: _emailController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), + decoration: _inputDecoration("Enter your email address").copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, ), ), + validator: (value) => + value == null || value.isEmpty ? 'Required Email' : null, + ), + const SizedBox(height: 20), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () => _onSubmit(context), + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF12B24B), // Button color + foregroundColor: Colors.white, // Text color + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18)), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Submit", + style: TextStyle( + fontFamily: "Nunito", + fontWeight: FontWeight.w800, + fontSize: 15), + ), + SizedBox( + width: 3, + ), + Icon( + Icons.arrow_forward_sharp, + color: Colors.white, + ) + ], + ), + ), + ), + ), + ], + ), + ] else ...[ + _buildLabel("Email Address"), + TextFormField( + controller: _emailController, + readOnly: true, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), + decoration: _inputDecoration("Enter your email address").copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, + ), + ), + validator: (value) => + value == null || value.isEmpty ? 'Required Email' : null, + ), + const SizedBox(height: 16), + _buildLabel("OTP"), + TextFormField( + controller: _otpController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), + decoration: _inputDecoration("Enter your OTP").copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, + ), + ), + validator: (value) => + value == null || value.isEmpty ? 'Required OTP' : null, + ), + + const SizedBox(height: 16), + + _buildLabel("New Password"), + TextFormField( + controller: _newPasswordController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), + obscureText: _obscureText, + decoration: _inputDecoration("Enter your new password").copyWith( + prefixIcon: Icon( + Icons.key, + size: 16, + ), + suffixIcon: IconButton( + icon: Icon( + _obscureText ? Icons.visibility_off : Icons.visibility, + color: Color(0xFF12B24B), + size: 16, + ), + onPressed: () => setState(() => _obscureText = !_obscureText), + ), + ), + validator: (value) => + value == null || value.isEmpty ? 'Required New Password' : null, + ), + + const SizedBox(height: 16), + + _buildLabel("Confirm Password"), + TextFormField( + controller: _confirmPasswordController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), + obscureText: _obscureText, + decoration: _inputDecoration("Enter your confirm password").copyWith( + prefixIcon: Icon( + Icons.key, + size: 16, + ), + suffixIcon: IconButton( + icon: Icon( + _obscureText ? Icons.visibility_off : Icons.visibility, + color: Color(0xFF12B24B), + size: 16, + ), + onPressed: () => setState(() => _obscureText = !_obscureText), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Required New Password'; + } + if (value != _newPasswordController.text) { + return 'Passwords do not match'; + } + return null; + }, + + ), + const SizedBox(height: 20), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + if (!_formKey.currentState!.validate()) return; + setState(() { + _isForgotPassword = false; + _showOtpResetFields = true; + _passwordController.clear(); + }); + _onSubmit(context); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF12B24B), // Button color + foregroundColor: Colors.white, // Text color + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18)), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Submit", + style: TextStyle( + fontFamily: "Nunito", + fontWeight: FontWeight.w800, + fontSize: 15), + ), + SizedBox( + width: 3, + ), + Icon( + Icons.arrow_forward_sharp, + color: Colors.white, + ) + ], + ), + ), + ), + ), + ], + ), + ], + const SizedBox(height: 20), + if(!_isForgotPassword && !_showOtpResetFields) + Center( + child: TextButton( + onPressed: () { + setState(() { + _isForgotPassword = true; + _showOtpResetFields = false; + _passwordController.clear(); + }); + }, + child: Text( + "Forgot Password", + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w300, + fontFamily: "Nunito", + color: Color(0xFF212121), // Text color + decoration: TextDecoration.underline, // Underline the text + ), + ), + ), + + ), + if(_isForgotPassword || _showOtpResetFields) + Center( + child: TextButton( + onPressed: () { + setState(() { + _isForgotPassword = false; + _showOtpResetFields = false; + _clearAllFields(); + }); + }, + child: Text( + "Back to Login", + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w300, + fontFamily: "Nunito", + color: Color(0xFF212121), // Text color + decoration: TextDecoration.underline, // Underline the text + ), + ), + ), + ), const SizedBox(height: 20), - +if(!_isForgotPassword && !_showOtpResetFields) Container( width: 45, // Adjust size height: 45,