793 lines
41 KiB
Dart
793 lines
41 KiB
Dart
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:nhance_app_pwa/pages/verify.dart';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import '../customAppBar/responsive.dart';
|
|
import '../customAppBar/toastHelper.dart';
|
|
import '../models/environment.dart';
|
|
import '../models/platform_helper_mobile.dart'
|
|
if (dart.library.html) '../models/platform_helper_other.dart';
|
|
|
|
class login extends StatefulWidget {
|
|
const login({Key? key});
|
|
|
|
@override
|
|
State<login> createState() => _loginState();
|
|
}
|
|
|
|
class _loginState extends State<login> {
|
|
TextEditingController countryController = TextEditingController();
|
|
TextEditingController mobileController = TextEditingController();
|
|
final _formKey = GlobalKey<FormState>();
|
|
bool _isCheckingToken = false;
|
|
dynamic empMobileNo;
|
|
|
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
|
|
late String _verificationId;
|
|
int? _resendToken;
|
|
bool _isLoading = false;
|
|
|
|
@override
|
|
void initState() {
|
|
countryController.text = "+91";
|
|
super.initState();
|
|
checkLoginPinOnMobile();
|
|
}
|
|
|
|
void checkLoginPinOnMobile() {
|
|
if (isMobilePlatform()) {
|
|
checkLoginPin(context);
|
|
}
|
|
}
|
|
|
|
Future<void> checkLoginPin(BuildContext context) async {
|
|
print('checkLoginPin');
|
|
try {
|
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
empMobileNo = prefs.getString('empMobileNo');
|
|
// _token = prefs.getString('token');
|
|
|
|
final response = await http.post(
|
|
Uri.parse(Environment.apiUrl + 'checkMpin'),
|
|
body: json.encode({'mobile_number': empMobileNo}),
|
|
headers: {
|
|
HttpHeaders.contentTypeHeader: 'application/json',
|
|
},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
Map<String, dynamic> data = json.decode(response.body);
|
|
if (data['status'] == 'success') {
|
|
print('data');
|
|
prefs.setString('mpinText', data['data']);
|
|
prefs.setString('mpin', data['Mpin']);
|
|
Navigator.pushReplacementNamed(context, 'pinPage');
|
|
}
|
|
} else {
|
|
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
throw Exception('Failed to verify pin number');
|
|
}
|
|
} catch (e) {
|
|
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
print('Error: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> verifyMobileNumber() async {
|
|
try {
|
|
if (_formKey.currentState!.validate()) {
|
|
setState(() {
|
|
_isLoading = true;
|
|
});
|
|
var enteredMobileNumber = mobileController.text;
|
|
final response = await http.post(
|
|
Uri.parse(Environment.apiUrl + 'verifyEmployeeNumber'),
|
|
body: json.encode({'mobile_number': enteredMobileNumber}),
|
|
headers: {
|
|
HttpHeaders.contentTypeHeader: 'application/json',
|
|
},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
Map<String, dynamic> data = json.decode(response.body);
|
|
bool userVerification = data['data']['user_verification'];
|
|
String message = data['data']['message'];
|
|
if (userVerification) {
|
|
final SharedPreferences prefs =
|
|
await SharedPreferences.getInstance();
|
|
var enteredMobileNumber = mobileController.text;
|
|
prefs.setString('empMobileNo', enteredMobileNumber);
|
|
|
|
_verifyPhoneNumber();
|
|
|
|
// ToastHelper.showSuccessToast(context, message);
|
|
} else {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
ToastHelper.showErrorToast(context, message);
|
|
print('Invalid mobile number');
|
|
}
|
|
} else {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
throw Exception('Failed to verify mobile number');
|
|
}
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
ToastHelper.showErrorToast(context, 'Something went wrong');
|
|
print('Error: $e');
|
|
}
|
|
}
|
|
|
|
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) {
|
|
ToastHelper.showSuccessToast(context, 'Verification Failed!');
|
|
if (e.code == 'invalid-phone-number') {
|
|
print('The provided phone number is not valid.');
|
|
}
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
},
|
|
codeSent: (String verificationId, int? resendToken) {
|
|
setState(() {
|
|
_verificationId = verificationId;
|
|
_resendToken = resendToken;
|
|
});
|
|
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) {
|
|
setState(() {
|
|
_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) {
|
|
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) {
|
|
setState(() {
|
|
_verificationId = verificationId;
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
Size _size = MediaQuery.of(context).size;
|
|
EdgeInsets marginInsets = EdgeInsets.zero;
|
|
|
|
if (Responsive.isDesktop(context)) {
|
|
marginInsets = const EdgeInsets.only(
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
top: 0,
|
|
);
|
|
} else if (Responsive.isMobile(context)) {
|
|
marginInsets = const EdgeInsets.only(
|
|
left: 25, // Example value for mobile
|
|
right: 25, // Example value for mobile
|
|
bottom: 0, // Example value for mobile
|
|
top: 0, // Example value for mobile
|
|
);
|
|
} else if (Responsive.isTablet(context)) {
|
|
marginInsets = const EdgeInsets.only(
|
|
left: 25, //// Example value for mobile
|
|
right: 25, // Example value for mobile
|
|
bottom: 0, // Example value for mobile
|
|
top: 0, // Example value for mobile
|
|
);
|
|
}
|
|
return WillPopScope(
|
|
onWillPop: () async {
|
|
// Close the app on mobile back button press
|
|
exit(0); // This will exit the app
|
|
return false; // Return false to prevent any other actions
|
|
},
|
|
child: Scaffold(
|
|
body: SingleChildScrollView(
|
|
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
|
child: Container(
|
|
height: _size.height,
|
|
color: Colors.white,
|
|
child: Stack(
|
|
children: [
|
|
Visibility(
|
|
visible: _size.width <= 1100,
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.only(
|
|
bottomLeft: Radius.circular(30),
|
|
bottomRight: Radius.circular(30),
|
|
),
|
|
child: Container(
|
|
height: _size.height / 3,
|
|
width: double.infinity,
|
|
color: Color(0xFFFFFCE5),
|
|
child: Stack(
|
|
children: [
|
|
Column(
|
|
children: [
|
|
SizedBox(
|
|
height: _size.height /
|
|
6.4), // Adjust the spacing between the rows
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment
|
|
.center, // Align to the center
|
|
children: [
|
|
Expanded(
|
|
flex: Responsive.isDesktop(context)
|
|
? 10
|
|
: 12,
|
|
child: Align(
|
|
alignment:
|
|
Responsive.isDesktop(context)
|
|
? Alignment.centerLeft
|
|
: Alignment.bottomCenter,
|
|
child: Image.asset(
|
|
'assets/nhance_app_logo.png',
|
|
width: 150,
|
|
height: 150,
|
|
)),
|
|
),
|
|
if (!Responsive.isMobile(context) &&
|
|
!Responsive.isTablet(context))
|
|
Expanded(
|
|
flex: 2,
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.click,
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// Add your navigation logic here
|
|
// For example, you can use Navigator.push to navigate to another page
|
|
Navigator.pushNamed(
|
|
context, 'hrLogin');
|
|
},
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment
|
|
.end, // Align to the end (right)
|
|
children: [
|
|
Text(
|
|
'HR Login',
|
|
style: TextStyle(
|
|
color: Color(
|
|
0xFF000000), // Text color
|
|
// Add other text styles as needed
|
|
),
|
|
),
|
|
SizedBox(width: 5),
|
|
Icon(
|
|
Icons
|
|
.east, // Icon for customer login
|
|
color: Colors
|
|
.black, // Adjust color as needed
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
margin: marginInsets,
|
|
alignment: Alignment.bottomCenter,
|
|
child: SingleChildScrollView(
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
flex: _size.width < 1100 ? 6 : 12,
|
|
child: Container(
|
|
margin: _size.width > 1100
|
|
? EdgeInsets.only(left: 20, right: 20)
|
|
: EdgeInsets.only(left: 0, right: 0),
|
|
child: Column(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
if (!Responsive.isMobile(context) &&
|
|
!Responsive.isTablet(context))
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
flex: 12,
|
|
child: Align(
|
|
alignment: Responsive
|
|
.isDesktop(context)
|
|
? Alignment.centerLeft
|
|
: Alignment
|
|
.bottomCenter, // Align to the start
|
|
child: _size.width <= 1100
|
|
? Image.asset(
|
|
'assets/nhance_app_logo.png',
|
|
width: 150,
|
|
height: 150,
|
|
)
|
|
: _size.width > 1100
|
|
? Image.asset(
|
|
'assets/nhance_app_logo.png',
|
|
width: 150,
|
|
height: 150,
|
|
)
|
|
: Image.asset(
|
|
'assets/nhance_app_logo.png',
|
|
width: 150,
|
|
height: 150,
|
|
),
|
|
)),
|
|
],
|
|
),
|
|
SizedBox(height: 10),
|
|
Container(
|
|
margin: Responsive.isDesktop(context)
|
|
? EdgeInsets.symmetric(
|
|
horizontal: 150)
|
|
: EdgeInsets.symmetric(
|
|
horizontal: 0),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
"Welcome to Nhance",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 10,
|
|
),
|
|
Container(
|
|
margin: Responsive.isDesktop(context)
|
|
? EdgeInsets.symmetric(
|
|
horizontal: 150)
|
|
: EdgeInsets.symmetric(
|
|
horizontal: 0),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
"Login with your mobile number and OTP to review and enroll for exciting health benefits for you and your family",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Color(0xFF000000)),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 20,
|
|
),
|
|
Container(
|
|
height: 55,
|
|
margin: Responsive.isDesktop(context)
|
|
? EdgeInsets.symmetric(
|
|
horizontal: 150)
|
|
: EdgeInsets.symmetric(
|
|
horizontal: 0),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(
|
|
width: 1, color: Colors.grey),
|
|
borderRadius:
|
|
BorderRadius.circular(10),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
SizedBox(
|
|
width: 10,
|
|
),
|
|
SizedBox(
|
|
width: 40,
|
|
child: TextField(
|
|
controller: countryController,
|
|
keyboardType:
|
|
TextInputType.number,
|
|
decoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
"|",
|
|
style: TextStyle(
|
|
fontSize: 33,
|
|
color: Colors.grey),
|
|
),
|
|
SizedBox(
|
|
width: 10,
|
|
),
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: mobileController,
|
|
keyboardType:
|
|
TextInputType.phone,
|
|
decoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
hintText:
|
|
"Enter your mobile number",
|
|
),
|
|
validator: (value) {
|
|
if (value == null ||
|
|
value.isEmpty) {
|
|
return 'Please enter your mobile number';
|
|
}
|
|
if (value.length != 10) {
|
|
return 'Mobile number must be 10 digits';
|
|
}
|
|
return null;
|
|
},
|
|
inputFormatters: <TextInputFormatter>[
|
|
FilteringTextInputFormatter
|
|
.digitsOnly,
|
|
LengthLimitingTextInputFormatter(
|
|
10),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 20,
|
|
),
|
|
Container(
|
|
margin: Responsive.isDesktop(context)
|
|
? EdgeInsets.symmetric(
|
|
horizontal: 150)
|
|
: EdgeInsets.symmetric(
|
|
horizontal: 0),
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
height: 45,
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor:
|
|
Color(0xFF00989E),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(10),
|
|
),
|
|
),
|
|
onPressed: _isLoading
|
|
? null
|
|
: verifyMobileNumber,
|
|
child: _isLoading
|
|
? CircularProgressIndicator(
|
|
valueColor:
|
|
AlwaysStoppedAnimation<
|
|
Color>(
|
|
Color(
|
|
0xFF00989E)),
|
|
)
|
|
: Text(
|
|
"Login With OTP",
|
|
style:
|
|
GoogleFonts.poppins(
|
|
color: Color(
|
|
0xFFFFFFFF)),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: _size.width <= 1100 ? 0 : 0,
|
|
),
|
|
_size.width > 1100
|
|
? Container(
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: 150),
|
|
child: Column(
|
|
children: [
|
|
SizedBox(height: 30),
|
|
Text(
|
|
"Benefits of Login",
|
|
style:
|
|
GoogleFonts.poppins(
|
|
fontSize: 20,
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
SizedBox(height: 15),
|
|
],
|
|
))
|
|
: SizedBox(),
|
|
_size.width > 1100
|
|
? Container(
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: 150),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
Expanded(
|
|
flex: 6,
|
|
child: Container(
|
|
padding: EdgeInsets
|
|
.symmetric(
|
|
vertical: 8),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.center,
|
|
children: [
|
|
Expanded(
|
|
child: Container(
|
|
padding: EdgeInsets
|
|
.symmetric(
|
|
vertical:
|
|
12),
|
|
decoration:
|
|
BoxDecoration(
|
|
border:
|
|
Border(
|
|
right:
|
|
BorderSide(
|
|
width: 1,
|
|
color: Colors
|
|
.black,
|
|
),
|
|
),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Icon(
|
|
Icons
|
|
.policy,
|
|
color: Color(
|
|
0xFFE26728)),
|
|
SizedBox(
|
|
height:
|
|
10),
|
|
Text(
|
|
"View Policy"),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Container(
|
|
padding: EdgeInsets
|
|
.symmetric(
|
|
vertical:
|
|
12),
|
|
child: Column(
|
|
children: [
|
|
Icon(
|
|
Icons
|
|
.edit,
|
|
color: Color(
|
|
0xFFE26728)),
|
|
SizedBox(
|
|
height:
|
|
10),
|
|
Text(
|
|
"Manage Claims"),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: SizedBox(
|
|
height: Responsive.isDesktop(
|
|
context)
|
|
? _size.height * 0.1
|
|
: _size.height * 0.2,
|
|
),
|
|
SizedBox(
|
|
height: _size.height * 0.1,
|
|
),
|
|
Container(
|
|
alignment: Alignment.bottomCenter,
|
|
padding:
|
|
EdgeInsets.symmetric(vertical: 8),
|
|
child: RichText(
|
|
textAlign: TextAlign.center,
|
|
text: TextSpan(
|
|
text:
|
|
'By continuing, you agree with our ',
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.black,
|
|
fontSize: 9,
|
|
),
|
|
children: <InlineSpan>[
|
|
WidgetSpan(
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors
|
|
.click,
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
Navigator.pushNamed(
|
|
context,
|
|
'privacypolicy');
|
|
},
|
|
child: Text(
|
|
'privacy policy ',
|
|
style:
|
|
GoogleFonts.poppins(
|
|
color:
|
|
Color(0xFF00989E),
|
|
fontSize: 9,
|
|
decoration:
|
|
TextDecoration
|
|
.underline,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
TextSpan(
|
|
text: 'and ',
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.black,
|
|
fontSize: 9,
|
|
),
|
|
),
|
|
WidgetSpan(
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors
|
|
.click,
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
Navigator.pushNamed(
|
|
context,
|
|
'termsofuse');
|
|
},
|
|
child: Text(
|
|
'terms of use',
|
|
style:
|
|
GoogleFonts.poppins(
|
|
color:
|
|
Color(0xFF00989E),
|
|
fontSize: 9,
|
|
decoration:
|
|
TextDecoration
|
|
.underline,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
if (_size.width > 1100)
|
|
Expanded(
|
|
flex: _size.width < 1100 ? 6 : 12,
|
|
child: LayoutBuilder(
|
|
builder: (BuildContext context,
|
|
BoxConstraints constraints) {
|
|
if (constraints.maxWidth > 600) {
|
|
return Image.asset(
|
|
'assets/login_web.jpg',
|
|
height: _size.height,
|
|
fit: BoxFit.cover,
|
|
);
|
|
} else {
|
|
return SizedBox();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)),
|
|
)));
|
|
}
|
|
}
|