nhance_partner/lib/presentation/screens/login/login_screen.dart
2025-09-30 10:50:05 +05:30

1489 lines
57 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 'dart:convert';
import 'dart:io';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:jwt_decode/jwt_decode.dart';
import 'package:nhance_partner/core/config/env.dart';
import 'package:nhance_partner/core/services/platform_helper.dart';
import 'package:nhance_partner/data/utils/validators.dart';
import 'package:pinput/pinput.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../core/routing/routes.dart';
import '../../../data/services/auth_service.dart';
import '../../../data/utils/commonLoaderButton.dart';
import '../../../data/utils/toastNotification.dart';
import '../../layouts/responsive_layout.dart';
import '../../providers/manager_provider.dart';
import '../../providers/userRoleProvider.dart';
import '../../themes/indicators/text_field_theme.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({super.key});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _PhoneNumberController = TextEditingController();
final TextEditingController _otpController = TextEditingController();
final TextEditingController _countryController = TextEditingController();
final FirebaseAuth _auth = FirebaseAuth.instance;
late String _verificationId;
int? _resendToken;
bool _isLoading = false;
bool _obscurePassword = true;
int switcherStatus = 1;
int selectedIndex = 1;
bool enteredEmailOrMobile = false;
dynamic clickedButtonName;
dynamic token;
dynamic fCMToken;
// Future<void> _handleLogin() async {
// if (!_formKey.currentState!.validate()) return;
//
// setState(() => _isLoading = true);
//
// // Simulate API call
// await Future.delayed(const Duration(seconds: 2));
//
// // TODO: Replace with your AuthService
// final success =
// _emailController.text == "test@example.com" &&
// // _passwordController.text == "password";
//
// // setState(() => _isLoading = false);
//
// print("Succes - $success");
// if (success) {
// print("Ckeck 1");
// if (mounted) {
// print("Ckeck 2");
// await AuthService.saveToken('1234');
// final dynamic token = AuthService.saveToken('1234');
// print('token - $token');
//
// print('AppRoutes.home');
// context.go(AppRoutes.home);
// print("Ckeck 3");
// }
// } else {
// if (mounted) {
// ScaffoldMessenger.of(
// context,
// ).showSnackBar(const SnackBar(content: Text("Invalid credentials")));
// }
// }
// }
final defaultPinTheme = PinTheme(
width: 56,
height: 56,
textStyle: TextStyle(
fontSize: 20,
color: Color.fromRGBO(30, 60, 87, 1),
fontWeight: FontWeight.w600,
),
decoration: BoxDecoration(
border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
borderRadius: BorderRadius.circular(20),
),
);
@override
void initState() {
super.initState();
_countryController.text = "+91";
}
Future<void> initNotification() async {
try {
// await _firebaseMessaging.requestPermission();
fCMToken = await FirebaseMessaging.instance.getToken();
print('Token : $fCMToken');
if (fCMToken != null) {
// await sendDeviceToken(fCMToken);
}
} catch (e) {
print("Error getting FCM Token: $e");
}
}
Future<void> _saveUserRole(String token) async {
final prefs = await SharedPreferences.getInstance();
final decodedToken = Jwt.parseJwt(token);
final Map<String, dynamic>? data = decodedToken['data'];
String role;
if (data == null || !data.containsKey('role_id')) {
role = "agent";
} else {
final dynamic roleId = data['role_id'];
if (roleId.toString() == "1") {
role = "manager";
} else if (roleId.toString() == "2") {
role = "staff";
} else {
role = "agent"; // fallback if unexpected value
}
}
// Save globally
ref.read(userRoleProvider.notifier).state = role;
print("✅ User role set globally: $role");
await prefs.setString('userRole', role);
// final userRole = ref.watch(userRoleProvider);
}
Future<void> _saveIdsFromToken(String token) async {
final prefs = await SharedPreferences.getInstance();
final Map<String, dynamic> decodedToken = Jwt.parseJwt(token);
print('decodedToken : $decodedToken');
final dynamic data = decodedToken['data'];
if (data != null) {
// Manager ID
final dynamic managerIdRaw = data['manager_id'];
final int? managerId = managerIdRaw is int
? managerIdRaw
: int.tryParse(managerIdRaw.toString());
if (managerId != null) {
ref.read(managerIdProvider.notifier).state = managerId;
print("✅ Manager ID saved globally: $managerId");
await prefs.setInt('managerId', managerId);
} else {
print("⚠️ Manager ID is null or invalid");
}
// User ID
final dynamic userIdRaw = data['id'];
final int? userId = userIdRaw is int
? userIdRaw
: int.tryParse(userIdRaw.toString());
if (userId != null) {
ref.read(userIdProvider.notifier).state = userId;
print("✅ User ID saved globally: $userId");
await prefs.setInt('userId', userId);
} else {
print("⚠️ User ID is null or invalid");
}
} else {
print("⚠️ No data found in decodedToken");
}
// final managerId = ref.watch(managerIdProvider);
// final userId = ref.watch(userIdProvider);
}
// Future<void> _saveManagerId(String token) async {
// Map<String, dynamic>? decodedToken = await Jwt.parseJwt(token);
// print('decodedToken : $decodedToken');
// final dynamic managerIdRaw = decodedToken['data']?['manager_id'];
// final int? managerId = managerIdRaw is int
// ? managerIdRaw
// : int.tryParse(managerIdRaw.toString());
//
// if (managerId != null) {
// ref.read(managerIdProvider.notifier).state = managerId;
// print("Manager ID saved globally: $managerId");
// } else {
// print("Manager ID is null or invalid");
// }
// print(managerId);
// // final managerId = ref.watch(managerIdProvider);
// }
Future<bool> sendMobileOrEmailVerify(String loginUser) async {
// await Future.delayed(const Duration(seconds: 5));
try {
if (_formKey.currentState!.validate()) {
String apiEndpoint;
if (loginUser == 'agent') {
apiEndpoint = switcherStatus == 1
? Env.apiUrl + 'auth/verifyAgentWithMobileNumber'
: Env.apiUrl + 'auth/verifyAgentWithEmailId';
} else {
apiEndpoint = switcherStatus == 1
? Env.apiUrl + 'auth/verifyStaffWithMobileNumber'
: Env.apiUrl + 'auth/verifyStaffWithEmailId';
}
Map<String, dynamic> payload = switcherStatus == 1
? {'mobile': _PhoneNumberController.text}
: {'email': _emailController.text};
final response = await http.post(
Uri.parse(apiEndpoint),
body: json.encode(payload),
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
'App-Signature': Env.App_Signature,
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
final verification = loginUser == 'agent'
? data['data']['agent_verification']
: data['data']['Staff_verification'];
final message = data['data']['message'];
if (verification == true) {
if (switcherStatus == 1) {
_verifyPhoneNumber();
} else {
setState(() {
enteredEmailOrMobile = true;
});
ToastHelper.showSuccessToast(context, 'OTP sent successfully');
}
return true; // ✅ success
} else {
ToastHelper.showErrorToast(context, message);
return false; // ❌ fail
}
} else {
ToastHelper.showErrorToast(context, 'Something went wrong');
return false; // ❌ fail
}
}
return false;
} catch (e) {
ToastHelper.showErrorToast(context, 'Something went wrong');
print('Error: $e');
return false;
}
}
Future<void> verifyMobileOrMailOTP(otpVerifyStatus) async {
try {
// Select API endpoint
final String apiEndpoint = (clickedButtonName == 'agent')
? '${Env.apiUrl}auth/getVerifiedAgentData'
: '${Env.apiUrl}auth/getVerifiedStaffData';
// Build payload depending on switcher status
final Map<String, dynamic> payload = (switcherStatus == 1)
? {
'otp_verification': otpVerifyStatus,
'mobile': _PhoneNumberController.text.trim(),
}
: {'otp': otpVerifyStatus, 'email': _emailController.text.trim()};
// Send request
final response = await http.post(
Uri.parse(apiEndpoint),
body: json.encode(payload),
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
'App-Signature': Env.App_Signature,
},
);
// Check response
if (response.statusCode == 200) {
final Map<String, dynamic> data = json.decode(response.body);
final String status = data['status']?.toString() ?? 'error';
token = data['data']; // can be string or object
print('Response status: ${response.statusCode}');
print('Response body: $data');
if (status == 'success') {
ToastHelper.showSuccessToast(context, 'OTP verified successfully');
await AuthService.saveToken(token);
await _saveUserRole(token);
await _saveIdsFromToken(token);
final managerId = ref.watch(managerIdProvider);
print('managerId -- $managerId');
final userRole = ref.watch(userRoleProvider);
print('userRole -- $userRole');
context.go(AppRoutes.dashboard);
} else {
ToastHelper.showErrorToast(context, data['message'] ?? 'Invalid OTP');
}
} else {
ToastHelper.showErrorToast(
context,
'Server error: ${response.statusCode}',
);
}
} catch (e, stacktrace) {
print('Error verifying OTP: $e');
print(stacktrace);
ToastHelper.showErrorToast(
context,
'Something went wrong. Please try again.',
);
}
}
Future<void> verifyFirebaseMobileOTP() async {
try {
// setState(() {
// _isLoading = true;
// });
// Later, in verify screen:
SharedPreferences prefs = await SharedPreferences.getInstance();
dynamic verificationId = prefs.getString('verificationId');
final credential = PhoneAuthProvider.credential(
verificationId: verificationId, // Make sure this is up-to-date
smsCode: _otpController.text,
);
print('credential $credential');
final userCredential = await FirebaseAuth.instance.signInWithCredential(
credential,
);
// await FirebaseAuth.instance.signInWithCredential(credential);
print('otp firebase check');
if (userCredential.user != null) {
print('otp firebase check done');
bool otpVerifyStatus = true;
await verifyMobileOrMailOTP(otpVerifyStatus);
} else {
ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again.');
}
// await FirebaseAuth.instance.signInWithCredential(credential).then((userCredential) async {
// if (userCredential.user != null) {
// bool otpVerifyStatus = true;
// generateToken(otpVerifyStatus);
// } else {
// setState(() {
// _isLoading = false;
// });
// ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
// }
// });
} on FirebaseAuthException catch (e) {
if (e.code == 'code-expired') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('OTP expired. Please request a new one.'),
duration: Duration(seconds: 2),
),
);
ToastHelper.showErrorToast(
context,
'OTP expired. Please request a new one.',
);
} else if (e.code == 'invalid-verification-code') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Invalid OTP entered. Please try again.'),
duration: Duration(seconds: 2),
),
);
ToastHelper.showErrorToast(
context,
'Invalid OTP entered. Please try again.',
);
} else if (e.code == 'session-expired') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Session expired. Try restarting the verification.'),
duration: Duration(seconds: 2),
),
);
ToastHelper.showErrorToast(
context,
'Session expired. Try restarting the verification.',
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.message}'),
duration: Duration(seconds: 2),
),
);
ToastHelper.showErrorToast(context, 'Error: ${e.message}');
}
} catch (e) {
// setState(() {
// _isLoading = false;
// });
print('Error: $e');
ToastHelper.showErrorToast(
context,
'Failed to verify OTP. Please try again.',
);
}
}
Future<void> _verifyPhoneNumber() async {
var enteredMobileNumber = _PhoneNumberController.text;
var countryCode = _countryController.text;
print('${countryCode + enteredMobileNumber}');
await _auth.verifyPhoneNumber(
phoneNumber: '${countryCode + enteredMobileNumber}',
timeout: const Duration(seconds: 60),
verificationCompleted: (PhoneAuthCredential credential) async {
await _auth.signInWithCredential(credential);
// ToastHelper.showSuccessToast(context, 'Verified Successfully!');
// setState(() {
// _isLoading = false;
// });
},
verificationFailed: (FirebaseAuthException e) {
print('Verification Failed: ${e.code} - ${e.message}');
String errorMessage;
if (e.code == 'invalid-app-credential') {
errorMessage = 'Invalid Credential. Please try again.';
} else if (e.code == 'invalid-phone-number') {
errorMessage = 'The provided phone number is not valid.';
} else if (e.code == 'too-many-requests') {
errorMessage = 'Too many requests. Try again later.';
} else {
errorMessage = 'Verification Failed: ${e.message}';
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage), duration: Duration(seconds: 2)),
);
ToastHelper.showSuccessToast(context, errorMessage);
setState(() {
_isLoading = false;
});
},
codeSent: (String verificationId, int? resendToken) async {
setState(() {
_verificationId = verificationId;
_resendToken = resendToken;
});
// When getting the verificationId
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('verificationId', _verificationId);
setState(() {
enteredEmailOrMobile = true;
});
ToastHelper.showSuccessToast(
context,
'Verification code sent to ${enteredMobileNumber}',
);
// setState(() {
// _isLoading = false;
// });
},
codeAutoRetrievalTimeout: (String verificationId) async {
setState(() {
_verificationId = verificationId;
});
final prefs = await SharedPreferences.getInstance();
await prefs.setString('verificationId', verificationId);
ToastHelper.showSuccessToast(context, 'Code auto-retrieval timed out.');
setState(() {
_isLoading = false;
});
},
);
}
void _resendCode(String mobileNumber, int? resendToken) async {
await _auth.verifyPhoneNumber(
phoneNumber: '${_countryController.text + mobileNumber}',
timeout: const Duration(seconds: 60),
forceResendingToken: resendToken,
verificationCompleted: (PhoneAuthCredential credential) async {
await _auth.signInWithCredential(credential);
},
verificationFailed: (FirebaseAuthException e) {
if (e.code == 'invalid-phone-number') {
print('The provided phone number is not valid.');
}
},
codeSent: (String verificationId, int? resendToken) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('verificationId', verificationId);
setState(() {
_verificationId = verificationId;
_resendToken = resendToken;
});
ToastHelper.showSuccessToast(
context,
'Verification code resent to ${mobileNumber}',
);
},
codeAutoRetrievalTimeout: (String verificationId) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('verificationId', verificationId);
setState(() {
_verificationId = verificationId;
});
},
);
}
// Future<void> _verifyPhoneNumber() async {
// var enteredMobileNumber = mobileController.text;
// var countryCode = countryController.text;
// print('${countryCode + enteredMobileNumber}');
// await _auth.verifyPhoneNumber(
// phoneNumber: '${countryCode + enteredMobileNumber}',
// timeout: const Duration(seconds: 60),
// verificationCompleted: (PhoneAuthCredential credential) async {
// await _auth.signInWithCredential(credential);
// // ToastHelper.showSuccessToast(context, 'Verified Successfully!');
// // setState(() {
// // _isLoading = false;
// // });
// },
// verificationFailed: (FirebaseAuthException e) {
// print('Verification Failed: ${e.code} - ${e.message}');
// String errorMessage;
// if (e.code == 'invalid-app-credential') {
// errorMessage = 'Invalid Credential. Please try again.';
// } else if (e.code == 'invalid-phone-number') {
// errorMessage = 'The provided phone number is not valid.';
// } else if (e.code == 'too-many-requests') {
// errorMessage = 'Too many requests. Try again later.';
// } else {
// errorMessage = 'Verification Failed: ${e.message}';
// }
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(errorMessage),
// duration: Duration(seconds: 2),
// ),
// );
//
// ToastHelper.showSuccessToast(context, errorMessage);
// setState(() {
// _isLoading = false;
// });
// },
// codeSent: (String verificationId, int? resendToken) async {
// setState(() {
// _verificationId = verificationId;
// _resendToken = resendToken;
// });
//
// // When getting the verificationId
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('verificationId', _verificationId);
//
//
// ToastHelper.showSuccessToast(
// context, 'Verification code sent to ${enteredMobileNumber}');
//
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MyVerify(
// verificationId: _verificationId,
// mobileNumber: enteredMobileNumber,
// resendToken: _resendToken,
// onResendCode: _resendCode, // Pass the phone number
// ),
// ),
// );
// setState(() {
// _isLoading = false;
// });
// },
// codeAutoRetrievalTimeout: (String verificationId) async{
// setState(() {
// _verificationId = verificationId;
// });
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('verificationId', verificationId);
// ToastHelper.showSuccessToast(context, 'Code auto-retrieval timed out.');
// setState(() {
// _isLoading = false;
// });
// },
// );
// }
//
// void _resendCode(String mobileNumber, int? resendToken) async {
// await _auth.verifyPhoneNumber(
// phoneNumber: '${countryController.text + mobileNumber}',
// timeout: const Duration(seconds: 60),
// forceResendingToken: resendToken,
// verificationCompleted: (PhoneAuthCredential credential) async {
// await _auth.signInWithCredential(credential);
// },
// verificationFailed: (FirebaseAuthException e) {
// if (e.code == 'invalid-phone-number') {
// print('The provided phone number is not valid.');
// }
// },
// codeSent: (String verificationId, int? resendToken) async{
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('verificationId', verificationId);
// setState(() {
// _verificationId = verificationId;
// _resendToken = resendToken;
// });
// ToastHelper.showSuccessToast(
// context, 'Verification code resent to ${mobileNumber}');
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MyVerify(
// verificationId: _verificationId,
// mobileNumber: mobileNumber,
// resendToken: _resendToken,
// onResendCode: _resendCode,
// ),
// ),
// );
// },
// codeAutoRetrievalTimeout: (String verificationId) async{
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('verificationId', verificationId);
// setState(() {
// _verificationId = verificationId;
// });
// },
// );
// }
// Future<void> sendMobileOrEmailVerify() async {
// if (!_formKey.currentState!.validate()) return;
// setState(() {
// enteredEmailOrMobile = true;
// });
// }
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: LayoutBuilder(
builder: (context, constraints) {
if (!ResponsiveLayout.isMobile(context)) {
// Web layout
return Row(
children: [
Expanded(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
"assets/login/nhance-partner-logo.png",
height: 45,
),
const SizedBox(height: 40),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Welcome Back",
style: TextStyle(
fontSize: 50,
fontWeight: FontWeight.w600,
color: Color(0xFF425C5C),
),
),
const SizedBox(height: 20),
Image.asset(
"assets/login/login-content.png",
height:
MediaQuery.of(context).size.height *
0.5, // 40% of screen height
fit: BoxFit.contain,
),
const SizedBox(height: 10),
const Text(
'${'"'}Manage your policies, track quotations, and grow your business all in one place.${'"'}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Color(0xFF000000),
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
),
),
// Right: Login form
Expanded(
child: Container(
// padding: const EdgeInsets.all(40),
decoration: const BoxDecoration(
color: Colors.white,
image: DecorationImage(
image: AssetImage(
"assets/login/login-bg.png",
), // <-- your image path
fit: BoxFit.cover, // cover entire container
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40),
bottomLeft: Radius.circular(40),
),
),
child: Center(
// <-- center vertically + horizontally
child: SizedBox(
width: 600, // keep fixed width like first design
child: _LoginForm(context),
),
),
),
),
],
);
} else {
// Mobile layout
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 15),
Image.asset(
"assets/login/nhance-partner-logo.png",
height: 35,
),
const SizedBox(height: 10),
Text(
"Welcome Back",
style: GoogleFonts.inter(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Color(0xFF425C5C),
),
),
const SizedBox(height: 10),
Image.asset("assets/login/login-content.png", height: 120),
// const SizedBox(height: 10),
Container(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 50),
child: Text(
'${'"'}Manage your policies, track quotations, and grow your business all in one place.${'"'}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xFF000000),
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 10),
Container(
width: double.infinity,
height:
MediaQuery.of(context).size.height *
0.8, // full screen height
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF466F6C), Color(0xFF86D5CF)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40),
topRight: Radius.circular(40),
),
),
child: _LoginForm(context),
),
],
),
);
}
},
),
);
}
Widget _LoginForm(BuildContext context) {
const focusedBorderColor = Colors.white;
const fillColor = Color.fromRGBO(243, 246, 249, 0);
const borderColor = Color.fromRGBO(23, 171, 144, 0.4);
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Login to your account",
style: TextStyle(
fontSize: ResponsiveLayout.isMobile(context) ? 20 : 35,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
const SizedBox(height: 10),
Text(
"Enter your email or mobile number to receive a one time password.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: ResponsiveLayout.isMobile(context) ? 12 : 18,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(50),
),
child: Row(
children: [
buildTab("Mobile", 0),
const SizedBox(width: 10),
buildTab("Email", 1),
],
),
),
],
),
const SizedBox(height: 30),
Container(
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
width: 400,
height: 70,
child: Column(
children: [
if (switcherStatus == 0)
ThemedFormField(
hintText: 'Email',
backgroundColor: Colors.white,
validator: (value) =>
Validators.email(value, "Email"),
// imgPath: "MiscIconAssetPath.person",
controller: _emailController,
),
if (switcherStatus == 1)
ThemedFormField(
hintText: 'Mobile Number',
backgroundColor: Colors.white,
validator: (value) =>
Validators.phone(value, "Phone"),
// imgPath: "MiscIconAssetPath.person",
controller: _PhoneNumberController,
// txtwidth: MediaQuery.of(context).size.width * 0.5,
keyboardType: TextInputType.number,
maxLength: 10,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
// readOnly: true,
),
// const SizedBox(height: 16),
// ThemedFormField(
// hintText: 'Password',
// backgroundColor: Colors.white,
// validator: (value) =>
// Validators.passwordlogIn(value, "Password"),
// // imgPath: "MiscIconAssetPath.person",
// controller: _passwordController,
// isObscurable: true,
// // txtwidth: MediaQuery.of(context).size.width * 0.5,
// // keyboardType: TextInputType.number,
// // maxLength: 10,
// // inputFormatters: [FilteringTextInputFormatter.digitsOnly],
// // readOnly: true,
// ),
],
),
),
const SizedBox(height: 20),
if (!enteredEmailOrMobile)
SizedBox(
width: 400,
child: SizedBox(
width: double.infinity,
height: 50,
child: CommonButton(
text: "Login as Agent",
backgroundColor: Color(0xFF436462),
onPressed: () async {
setState(() {
clickedButtonName = 'agent';
});
// ⏳ Wait for API to finish
final success = await sendMobileOrEmailVerify(
'agent',
);
if (success) {
// API success → loader stops immediately
print("✅ API success");
} else {
// API failed → loader also stops immediately
print("❌ API failed");
}
},
),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Color(0xFF436462),
// padding: const EdgeInsets.symmetric(vertical: 16),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8),
// ),
// ),
// onPressed: () {
// sendMobileOrEmailVerify('agent');
// setState(() {
// clickedButtonName = 'agent';
// });
// },
// child: Text(
// 'Login as Agent',
// style: TextStyle(
// fontSize: ResponsiveLayout.isMobile(context)
// ? 18
// : 20,
// fontWeight: FontWeight.w600,
// color: Colors.white,
// ),
// ),
// ),
),
),
if (!ResponsiveLayout.isMobile(context) &&
!enteredEmailOrMobile) ...[
SizedBox(height: 10),
Container(
child: Text(
'OR',
style: TextStyle(
fontSize: ResponsiveLayout.isMobile(context) ? 14 : 18,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
SizedBox(height: 10),
SizedBox(
width: 400,
child: SizedBox(
width: double.infinity,
height: 50,
child: CommonButton(
text: "Login as Staff",
backgroundColor: Color(0xFF436462),
onPressed: () async {
setState(() {
clickedButtonName = 'staff';
});
// ⏳ Wait for API to finish
final success = await sendMobileOrEmailVerify(
'staff',
);
if (success) {
// API success → loader stops immediately
print("✅ API success");
} else {
// API failed → loader also stops immediately
print("❌ API failed");
}
},
),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Color(0xFF436462),
// padding: const EdgeInsets.symmetric(vertical: 16),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8),
// ),
// ),
// onPressed: () {
// sendMobileOrEmailVerify('staff');
// setState(() {
// clickedButtonName = 'staff';
// });
// },
// child: Text(
// 'Login as Staff',
// style: TextStyle(
// fontSize: ResponsiveLayout.isMobile(context)
// ? 18
// : 20,
// fontWeight: FontWeight.w600,
// color: Colors.white,
// ),
// ),
// ),
),
),
],
// if (enteredEmailOrMobile) ...[
// Container(
// child: Text(
// 'OTP',
// style: TextStyle(
// fontSize: ResponsiveLayout.isMobile(context) ? 14 : 18,
// fontWeight: FontWeight.w600,
// color: Colors.white,
// ),
// ),
// ),
// Container(
// child: Pinput(
// length: 6,
// defaultPinTheme: defaultPinTheme,
// separatorBuilder: (index) => const SizedBox(width: 8),
// showCursor: true,
// controller: _otpController,
// // validator: _validatePin,
// errorPinTheme: defaultPinTheme.copyBorderWith(
// border: Border.all(color: Colors.redAccent),
// ),
// hapticFeedbackType: HapticFeedbackType.lightImpact,
// onCompleted: (pin) {
// debugPrint('onCompleted: $pin');
// },
// onChanged: (value) {
// debugPrint('onChanged: $value');
// },
// cursor: Column(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Container(
// margin: const EdgeInsets.only(bottom: 9),
// width: 22,
// height: 1,
// color: focusedBorderColor,
// ),
// ],
// ),
// focusedPinTheme: defaultPinTheme.copyWith(
// decoration: defaultPinTheme.decoration!.copyWith(
// borderRadius: BorderRadius.circular(8),
// border: Border.all(color: focusedBorderColor),
// ),
// ),
// submittedPinTheme: defaultPinTheme.copyWith(
// decoration: defaultPinTheme.decoration!.copyWith(
// color: fillColor,
// borderRadius: BorderRadius.circular(19),
// border: Border.all(color: focusedBorderColor),
// ),
// ),
// ),
// ),
// const SizedBox(height: 20),
// SizedBox(
// width: 400,
// child: SizedBox(
// width: double.infinity,
// height: 50,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Color(0xFF436462),
// padding: const EdgeInsets.symmetric(vertical: 16),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8),
// ),
// ),
// onPressed: () {
// // sendMobileOrEmailVerify('staff');
// },
// child: Text(
// 'Continue',
// style: TextStyle(
// fontSize: ResponsiveLayout.isMobile(context)
// ? 18
// : 20,
// fontWeight: FontWeight.w600,
// color: Colors.white,
// ),
// ),
// ),
// ),
// ),
// ],
AnimatedSwitcher(
duration: const Duration(
milliseconds: 500,
), // animation speed
switchInCurve: Curves.easeInOutCirc,
switchOutCurve: Curves.easeOutCirc,
child: enteredEmailOrMobile
? Column(
key: const ValueKey('otp_block'),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Center(
child: Text(
'OTP',
style: TextStyle(
fontSize: ResponsiveLayout.isMobile(context)
? 14
: 18,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
const SizedBox(height: 10),
Container(
alignment: Alignment.center,
child: Pinput(
length: 6,
defaultPinTheme: defaultPinTheme,
separatorBuilder: (index) =>
const SizedBox(width: 8),
showCursor: true,
controller: _otpController,
errorPinTheme: defaultPinTheme.copyBorderWith(
border: Border.all(color: Colors.redAccent),
),
hapticFeedbackType:
HapticFeedbackType.lightImpact,
onCompleted: (pin) =>
debugPrint('onCompleted: $pin'),
onChanged: (value) =>
debugPrint('onChanged: $value'),
cursor: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
margin: const EdgeInsets.only(bottom: 9),
width: 22,
height: 1,
color: focusedBorderColor,
),
],
),
focusedPinTheme: defaultPinTheme.copyWith(
decoration: defaultPinTheme.decoration!
.copyWith(
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: focusedBorderColor,
),
),
),
submittedPinTheme: defaultPinTheme.copyWith(
decoration: defaultPinTheme.decoration!
.copyWith(
color: fillColor,
borderRadius: BorderRadius.circular(19),
border: Border.all(
color: focusedBorderColor,
),
),
),
),
),
const SizedBox(height: 20),
Container(
alignment: Alignment.center, // center the text
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'Didnt Receive Code? ', // normal text
style: TextStyle(
fontSize: ResponsiveLayout.isMobile(context)
? 12
: 14,
fontWeight: FontWeight.normal,
color: Colors.white,
),
children: [
TextSpan(
text: 'Resend', // bold clickable part
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
decoration: TextDecoration
.underline, // optional
),
recognizer: TapGestureRecognizer()
..onTap = () {
print('Resend clicked');
print(enteredEmailOrMobile);
print(clickedButtonName);
if (_PhoneNumberController
.text
.isNotEmpty) {
print('1224');
_resendCode(
_PhoneNumberController.text,
_resendToken,
);
} else if (_PhoneNumberController
.text
.isEmpty) {
print('1227');
sendMobileOrEmailVerify(
clickedButtonName,
);
} else {
print('data not found');
}
},
),
],
),
),
),
const SizedBox(height: 20),
Center(
child: SizedBox(
width: 400,
child: CommonButton(
text: "Continue",
backgroundColor: Color(0xFF436462),
onPressed: () async {
if (switcherStatus == 1) {
await verifyFirebaseMobileOTP();
} else {
await verifyMobileOrMailOTP(
_otpController.text,
);
}
},
),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: const Color(0xFF436462),
// padding: const EdgeInsets.symmetric(
// vertical: 16,
// ),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8),
// ),
// ),
// onPressed: () {
// if (switcherStatus == 1) {
// verifyFirebaseMobileOTP();
// } else {
// verifyMobileOrMailOTP(_otpController.text);
// }
// },
// child: Text(
// 'Continue',
// style: TextStyle(
// fontSize: ResponsiveLayout.isMobile(context)
// ? 18
// : 20,
// fontWeight: FontWeight.w600,
// color: Colors.white,
// ),
// ),
// ),
),
),
],
)
: const SizedBox.shrink(), // Empty widget when false
),
SizedBox(height: 20),
],
),
),
),
],
);
}
Widget buildTab(String title, int index) {
final isSelected = selectedIndex == index + 1;
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index + 1;
enteredEmailOrMobile = false;
_PhoneNumberController.text = '';
_emailController.text = '';
_otpController.text = '';
_PhoneNumberController.clear();
_emailController.clear();
_otpController.clear();
if (selectedIndex == 2) {
switcherStatus = 0;
} else {
switcherStatus = 1;
}
});
_formKey.currentState?.reset();
},
child: Container(
padding: EdgeInsets.only(
top: 6.5,
bottom: 6.0,
left: 40.0,
right: 40.0,
),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF436462) : Colors.transparent,
boxShadow: isSelected
? [
BoxShadow(
color: isSelected
? const Color(0xFF436462)
: Colors.transparent, // Grey shadow
spreadRadius: 0.2,
blurRadius: 1,
offset: const Offset(0, 1), // Horizontal, Vertical
),
]
: [],
borderRadius: BorderRadius.circular(16),
),
child: Text(
title,
style: GoogleFonts.poppins(
fontSize: 12,
color: isSelected ? Colors.white : Colors.black,
fontWeight: FontWeight.w500,
),
),
),
);
}
}
//
// @override
// Widget build(BuildContext context) {
// final isWeb = MediaQuery.of(context).size.width > 600;
//
// return Scaffold(
// body: Center(
// child: Container(
// constraints: const BoxConstraints(maxWidth: 400),
// padding: const EdgeInsets.all(16.0),
// decoration: isWeb
// ? BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(8),
// boxShadow: [
// BoxShadow(
// blurRadius: 12,
// color: Colors.black.withOpacity(0.1),
// ),
// ],
// )
// : null,
// child: Form(
// key: _formKey,
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// // App Logo
// Image.asset('assets/logo.png', width: 80, height: 80),
// const SizedBox(height: 20),
//
// // Environment label
// Text(
// "Environment: ${Env.envName.toUpperCase()}",
// style: const TextStyle(fontSize: 14, color: Colors.grey),
// ),
// const SizedBox(height: 20),
//
// ThemedFormField(
// hintText: 'Email',
// backgroundColor: Colors.white,
// validator: (value) => Validators.email(value, "Email"),
// // imgPath: "MiscIconAssetPath.person",
// controller: _emailController,
// // txtwidth: MediaQuery.of(context).size.width * 0.5,
// // keyboardType: TextInputType.number,
// // maxLength: 10,
// // inputFormatters: [FilteringTextInputFormatter.digitsOnly],
// // readOnly: true,
// ),
//
// const SizedBox(height: 16),
// ThemedFormField(
// hintText: 'Password',
// backgroundColor: Colors.white,
// validator: (value) =>
// Validators.passwordlogIn(value, "Password"),
// // imgPath: "MiscIconAssetPath.person",
// controller: _passwordController,
// isObscurable: true,
// // txtwidth: MediaQuery.of(context).size.width * 0.5,
// // keyboardType: TextInputType.number,
// // maxLength: 10,
// // inputFormatters: [FilteringTextInputFormatter.digitsOnly],
// // readOnly: true,
// ),
//
// // Password
// const SizedBox(height: 24),
//
// // Login Button
// SizedBox(
// width: double.infinity,
// child: ElevatedButton(
// onPressed: _isLoading ? null : _handleLogin,
// child: _isLoading
// ? const SizedBox(
// height: 20,
// width: 20,
// child: CircularProgressIndicator(
// strokeWidth: 2,
// valueColor: AlwaysStoppedAnimation<Color>(
// Colors.white,
// ),
// ),
// )
// : const Text("Login"),
// ),
// ),
//
// const SizedBox(height: 12),
//
// // Forgot password link
// TextButton(
// onPressed: () {
// // TODO: Navigate to forgot password screen
// },
// child: const Text("Forgot password?"),
// ),
// ],
// ),
// ),
// ),
// ),
// );
// }