forgot password done
This commit is contained in:
parent
36975e716f
commit
a509583cb6
@ -15,12 +15,29 @@ class LoginWidget extends StatefulWidget {
|
||||
@override
|
||||
_LoginWidgetState createState() => _LoginWidgetState();
|
||||
}
|
||||
|
||||
enum LoginStep { login, forgotEmail, otpReset }
|
||||
class _LoginWidgetState extends State<LoginWidget> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
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<void> storeUserDetails(String token) async {
|
||||
try {
|
||||
@ -47,7 +64,7 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
void _login(BuildContext context) async {
|
||||
Future<void> _login(BuildContext context) async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
const String url = '$apiUrl/auth/login';
|
||||
|
||||
@ -95,6 +112,106 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _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<LoginWidget> {
|
||||
const SizedBox(height: 18),
|
||||
|
||||
/// **Email Field**
|
||||
if (!_isForgotPassword && !_showOtpResetFields) ...[
|
||||
_buildLabel("Email Address"),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
@ -277,22 +395,263 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user