494 lines
18 KiB
Dart
Executable File
494 lines
18 KiB
Dart
Executable File
import 'dart:io';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
|
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
import '../../../constant/box_name.dart';
|
|
import '../../../constant/colors.dart';
|
|
import '../../../constant/info.dart';
|
|
import '../../../constant/links.dart';
|
|
import '../../../constant/style.dart';
|
|
import '../../../controller/auth/apple_sigin.dart';
|
|
import '../../../controller/auth/captin/login_captin_controller.dart';
|
|
import '../../../controller/auth/google_sign.dart';
|
|
import '../../../main.dart';
|
|
import '../../../print.dart';
|
|
import '../../widgets/elevated_btn.dart';
|
|
import '../../widgets/mycircular.dart';
|
|
import 'contact_us_page.dart';
|
|
import 'otp_page.dart'; // تأكد من وجود هذا الملف لديك
|
|
|
|
class LoginCaptin extends StatefulWidget {
|
|
const LoginCaptin({super.key});
|
|
|
|
@override
|
|
State<LoginCaptin> createState() => _LoginCaptinState();
|
|
}
|
|
|
|
class _LoginCaptinState extends State<LoginCaptin> with WidgetsBindingObserver {
|
|
final AuthController authController = Get.put(AuthController());
|
|
final LoginDriverController controller = Get.put(LoginDriverController());
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
// فحص الإذن عند فتح الصفحة مباشرة
|
|
controller.getLocationPermission();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
// التحقق عند العودة من الإعدادات
|
|
if (state == AppLifecycleState.resumed) {
|
|
controller.getLocationPermission();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GetBuilder<LoginDriverController>(
|
|
builder: (controller) {
|
|
return Scaffold(
|
|
backgroundColor: AppColor.secondaryColor,
|
|
body: SafeArea(
|
|
child: Center(
|
|
child: _buildBodyContent(context, controller),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildBodyContent(
|
|
BuildContext context, LoginDriverController controller) {
|
|
// 1. صفحة الموافقة على الشروط
|
|
if (box.read(BoxName.agreeTerms) != 'agreed') {
|
|
return _buildAgreementPage(context, controller);
|
|
}
|
|
|
|
// 2. صفحة إذن الموقع
|
|
if (box.read(BoxName.locationPermission) != 'true') {
|
|
return _buildLocationPermissionPage(context, controller);
|
|
}
|
|
|
|
// 3. صفحة تسجيل الدخول (رقم الهاتف)
|
|
return PhoneNumberScreen();
|
|
}
|
|
|
|
// --- صفحة الشروط والأحكام ---
|
|
Widget _buildAgreementPage(
|
|
BuildContext context, LoginDriverController controller) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(24.0),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(Icons.policy_outlined,
|
|
size: 80, color: AppColor.primaryColor),
|
|
const SizedBox(height: 20),
|
|
Text('Driver Agreement'.tr,
|
|
textAlign: TextAlign.center, style: AppStyle.headTitle2),
|
|
const SizedBox(height: 30),
|
|
RichText(
|
|
textAlign: TextAlign.center,
|
|
text: TextSpan(
|
|
style: AppStyle.title.copyWith(height: 1.5),
|
|
children: [
|
|
TextSpan(
|
|
text:
|
|
"To become a driver, you must review and agree to the "
|
|
.tr),
|
|
TextSpan(
|
|
text: 'Terms of Use'.tr,
|
|
style: const TextStyle(
|
|
decoration: TextDecoration.underline,
|
|
color: AppColor.blueColor,
|
|
fontWeight: FontWeight.bold),
|
|
recognizer: TapGestureRecognizer()
|
|
..onTap = () {
|
|
launchUrl(
|
|
Uri.parse('${AppLink.server}/privacy_policy.php'),
|
|
mode: LaunchMode.externalApplication);
|
|
}),
|
|
TextSpan(text: " and acknowledge our Privacy Policy.".tr),
|
|
],
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey.shade300),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(12),
|
|
child: HtmlWidget(box.read(BoxName.lang).toString() == 'ar'
|
|
? AppInformation.privacyPolicyArabic
|
|
: AppInformation.privacyPolicy),
|
|
),
|
|
),
|
|
),
|
|
CheckboxListTile(
|
|
title: Text('I Agree'.tr, style: AppStyle.title),
|
|
value: controller.isAgreeTerms,
|
|
onChanged: (value) => controller.changeAgreeTerm(),
|
|
activeColor: AppColor.primaryColor,
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
),
|
|
const SizedBox(height: 16),
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: MyElevatedButton(
|
|
title: 'Continue'.tr,
|
|
onPressed: controller.isAgreeTerms
|
|
? () => controller.saveAgreementTerms()
|
|
: () {},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// --- صفحة إذن الموقع ---
|
|
Widget _buildLocationPermissionPage(
|
|
BuildContext context, LoginDriverController controller) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(24.0),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
const Icon(Icons.location_on_outlined,
|
|
size: 80, color: AppColor.primaryColor),
|
|
const SizedBox(height: 20),
|
|
Text(
|
|
'Location Access Required'.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.headTitle2,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'We need access to your location to match you with nearby passengers and provide accurate navigation.'
|
|
.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.title,
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Please allow location access "all the time" to receive ride requests even when the app is in the background.'
|
|
.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.title.copyWith(
|
|
color: AppColor.greenColor, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 40),
|
|
MyElevatedButton(
|
|
title: "Allow Location Access".tr,
|
|
kolor: AppColor.greenColor,
|
|
onPressed: () async {
|
|
box.write(BoxName.locationPermission, 'true');
|
|
controller.update();
|
|
// 1. طلب إذن الموقع العادي (أثناء الاستخدام) أولاً
|
|
var status = await Permission.location.request();
|
|
|
|
if (status.isGranted) {
|
|
// 2. إذا كنت تحتاج "طوال الوقت" (Background)، اطلبه الآن بشكل منفصل
|
|
// ملاحظة: في أندرويد 11+ سينقلك هذا لصفحة إعدادات خاصة
|
|
var backgroundStatus =
|
|
await Permission.locationAlways.request();
|
|
|
|
if (backgroundStatus.isGranted) {
|
|
box.write(BoxName.locationPermission, 'true');
|
|
controller.update();
|
|
} else {
|
|
// المستخدم وافق على "أثناء الاستخدام" فقط، يمكنك المشي في الحال
|
|
// أو إجباره، حسب منطق تطبيقك (تطبيق سائق يفضل Always)
|
|
box.write(BoxName.locationPermission, 'true');
|
|
controller.update();
|
|
}
|
|
} else if (status.isPermanentlyDenied) {
|
|
// إذا كانت الحالة مرفوضة نهائياً، يجب فتح الإعدادات
|
|
openAppSettings();
|
|
}
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
TextButton(
|
|
onPressed: () => openAppSettings(),
|
|
child: Text("Open Settings".tr,
|
|
style: const TextStyle(color: AppColor.blueColor)),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// --- واجهة تسجيل الدخول اليدوي/الاجتماعي (للاستخدام المستقبلي إذا لزم الأمر) ---
|
|
Widget _buildLoginUI(BuildContext context, LoginDriverController controller) {
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Image.asset('assets/images/logo.gif', height: 120, width: 120),
|
|
const SizedBox(height: 20),
|
|
Text(
|
|
'Driver Portal'.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.headTitle2.copyWith(fontSize: 28),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Sign in to start your journey'.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.subtitle,
|
|
),
|
|
const SizedBox(height: 40),
|
|
if (controller.isGoogleDashOpen)
|
|
_buildManualLoginForm(context, controller, isRegistration: true)
|
|
else if (Platform.isIOS && controller.isTest == 0)
|
|
_buildManualLoginForm(context, controller, isRegistration: false)
|
|
else
|
|
_buildSocialLoginOptions(context, controller),
|
|
const SizedBox(height: 32),
|
|
Center(
|
|
child: GestureDetector(
|
|
onTap: () => Get.to(() => ContactUsPage()),
|
|
child: Text(
|
|
'Need help? Contact Us'.tr,
|
|
style: AppStyle.subtitle.copyWith(
|
|
color: AppColor.blueColor,
|
|
decoration: TextDecoration.underline,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSocialLoginOptions(
|
|
BuildContext context, LoginDriverController controller) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Text(
|
|
'Sign in with a provider for easy access'.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.title,
|
|
),
|
|
const SizedBox(height: 24),
|
|
_buildSocialButton(
|
|
text: 'Sign In with Google'.tr,
|
|
icon: FontAwesome.google,
|
|
backgroundColor: AppColor.redColor,
|
|
onPressed: () async {
|
|
GoogleSignInHelper().signInFromLogin();
|
|
},
|
|
),
|
|
if (Platform.isIOS) ...[
|
|
const SizedBox(height: 16),
|
|
_buildSocialButton(
|
|
text: 'Sign in with Apple'.tr,
|
|
icon: Icons.apple,
|
|
backgroundColor: Colors.black,
|
|
onPressed: () async {
|
|
User? user = await authController.signInWithApple();
|
|
if (user != null) {
|
|
box.write(BoxName.emailDriver, user.email.toString());
|
|
box.write(BoxName.driverID, user.uid);
|
|
controller.loginWithGoogleCredential(
|
|
user.uid,
|
|
user.email.toString(),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
children: [
|
|
const Expanded(child: Divider()),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
child: Text('Or'.tr, style: AppStyle.subtitle),
|
|
),
|
|
const Expanded(child: Divider()),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
MyElevatedButton(
|
|
title: 'Create Account with Email'.tr,
|
|
onPressed: () => controller.changeGoogleDashOpen(),
|
|
kolor: AppColor.blueColor,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildManualLoginForm(
|
|
BuildContext context, LoginDriverController controller,
|
|
{required bool isRegistration}) {
|
|
return Card(
|
|
elevation: 8,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20.0),
|
|
child: Form(
|
|
key: controller.formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Text(
|
|
isRegistration ? 'Create Driver Account'.tr : 'Driver Login'.tr,
|
|
textAlign: TextAlign.center,
|
|
style: AppStyle.headTitle2,
|
|
),
|
|
const SizedBox(height: 24),
|
|
_buildTextFormField(
|
|
controller: controller.emailController,
|
|
labelText: 'Email'.tr,
|
|
hintText: 'Enter your email'.tr,
|
|
prefixIcon: Icons.email_outlined,
|
|
keyboardType: TextInputType.emailAddress,
|
|
validator: (value) {
|
|
if (value == null || !GetUtils.isEmail(value)) {
|
|
return 'Please enter a valid email'.tr;
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 20),
|
|
GetBuilder<LoginDriverController>(
|
|
id: 'passwordVisibility',
|
|
builder: (_) => _buildTextFormField(
|
|
controller: controller.passwordController,
|
|
labelText: 'Password'.tr,
|
|
hintText: 'Enter your password'.tr,
|
|
prefixIcon: Icons.lock_outline,
|
|
obscureText: controller.isPasswordHidden,
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
controller.isPasswordHidden
|
|
? Icons.visibility_off
|
|
: Icons.visibility,
|
|
color: AppColor.primaryColor,
|
|
),
|
|
onPressed: () => controller.togglePasswordVisibility(),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.length < 6) {
|
|
return 'Password must be at least 6 characters'.tr;
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(height: 30),
|
|
controller.isloading
|
|
? const Center(child: MyCircularProgressIndicator())
|
|
: MyElevatedButton(
|
|
title:
|
|
(isRegistration ? 'Create Account'.tr : 'Login'.tr),
|
|
onPressed: () {
|
|
if (controller.formKey.currentState!.validate()) {
|
|
if (isRegistration) {
|
|
String email = controller.emailController.text;
|
|
String uniqueId =
|
|
controller.generateUniqueIdFromEmail(email);
|
|
box.write(BoxName.driverID, uniqueId);
|
|
box.write(BoxName.emailDriver, email);
|
|
controller.loginUsingCredentialsWithoutGoogle(
|
|
controller.passwordController.text,
|
|
email,
|
|
);
|
|
} else {
|
|
controller.loginWithGoogleCredential(
|
|
controller.passwordController.text,
|
|
controller.emailController.text,
|
|
);
|
|
}
|
|
}
|
|
},
|
|
),
|
|
if (isRegistration)
|
|
TextButton(
|
|
onPressed: () => controller.changeGoogleDashOpen(),
|
|
child: Text(
|
|
'Back to other sign-in options'.tr,
|
|
style: TextStyle(color: AppColor.primaryColor),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
TextFormField _buildTextFormField({
|
|
required TextEditingController controller,
|
|
required String labelText,
|
|
required String hintText,
|
|
required IconData prefixIcon,
|
|
required String? Function(String?) validator,
|
|
bool obscureText = false,
|
|
Widget? suffixIcon,
|
|
TextInputType keyboardType = TextInputType.text,
|
|
}) {
|
|
return TextFormField(
|
|
controller: controller,
|
|
validator: validator,
|
|
obscureText: obscureText,
|
|
keyboardType: keyboardType,
|
|
decoration: InputDecoration(
|
|
labelText: labelText,
|
|
hintText: hintText,
|
|
prefixIcon: Icon(prefixIcon, color: AppColor.primaryColor),
|
|
suffixIcon: suffixIcon,
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.0)),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12.0),
|
|
borderSide:
|
|
const BorderSide(color: AppColor.primaryColor, width: 2.0),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSocialButton({
|
|
required String text,
|
|
required IconData icon,
|
|
required Color backgroundColor,
|
|
required VoidCallback onPressed,
|
|
}) {
|
|
return ElevatedButton.icon(
|
|
icon: Icon(icon, color: Colors.white),
|
|
label:
|
|
Text(text, style: const TextStyle(color: Colors.white, fontSize: 16)),
|
|
onPressed: onPressed,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: backgroundColor,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
),
|
|
);
|
|
}
|
|
}
|