Files
driver_tripz/lib/views/auth/captin/login_captin.dart
Hamza-Ayed 441385069c 25-8-6-1
2025-08-06 01:10:52 +03:00

525 lines
19 KiB
Dart
Executable File

import 'dart:io';
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
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 '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/info.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 '../../../controller/functions/encrypt_decrypt.dart';
import '../../../controller/functions/overlay_permisssion.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/mycircular.dart';
import '../country_widget.dart';
class LoginCaptin extends StatelessWidget {
final AuthController authController = Get.put(AuthController());
final LoginDriverController controller = Get.put(LoginDriverController());
LoginCaptin({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<LoginDriverController>(
builder: (controller) {
return Scaffold(
backgroundColor: AppColor.secondaryColor,
body: SafeArea(
child: Center(
child: _buildBodyContent(context, controller),
),
),
);
},
);
}
/// Determines which UI to show based on the driver's progress.
Widget _buildBodyContent(
BuildContext context, LoginDriverController controller) {
if (box.read(BoxName.agreeTerms) != 'agreed') {
return _buildAgreementPage(context, controller);
}
if (box.read(BoxName.countryCode) == null) {
return _buildCountryPickerPage(); // Improved wrapper for country picker
}
if (box.read(BoxName.locationPermission) != 'true') {
return _buildLocationPermissionPage(context, controller);
}
// Main login flow
return _buildLoginFlow(context, controller);
}
/// Manages the transition between social login options and the manual form.
Widget _buildLoginFlow(
BuildContext context, LoginDriverController controller) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return FadeTransition(opacity: animation, child: child);
},
child: controller.showManualForm
? _buildManualFormUI(
context, controller) // The new unified manual form
: _buildLoginUI(
context, controller), // Main screen with social options
);
}
/// Redesigned UI for the main login screen with social options.
Widget _buildLoginUI(BuildContext context, LoginDriverController controller) {
return SingleChildScrollView(
key: const ValueKey('socialLogin'),
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CircleAvatar(
radius: 65,
backgroundColor: Colors.white.withOpacity(0.9),
child: ClipOval(
child: Image.asset(
'assets/images/logo.png', // Using PNG for better quality and performance
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),
_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,
),
),
),
),
],
),
);
}
/// Builds the social login buttons (Google, Apple, and manual option).
Widget _buildSocialLoginOptions(
BuildContext context, LoginDriverController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Google Login
_buildSocialButton(
text: 'Sign In with Google'.tr,
icon: FontAwesome.google,
backgroundColor: AppColor.redColor,
onPressed: () async {
await GoogleSignInHelper().signInFromLogin();
},
),
// Apple Login (for iOS only)
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),
// Divider with "OR"
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),
// Manual Login with Email/Password
_buildSocialButton(
text: 'Continue with Email'.tr,
icon: Icons.email_outlined,
backgroundColor: AppColor.primaryColor,
onPressed: () =>
controller.toggleManualFormView(), // New simplified logic
),
],
);
}
/// A unified and dynamic UI for both Manual Login and Registration.
Widget _buildManualFormUI(
BuildContext context, LoginDriverController controller) {
// Determine if it's in registration mode from the controller
final isRegister = controller.isRegisterMode;
return SingleChildScrollView(
key: const ValueKey('manualForm'),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: 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: [
// Back button to return to social login options
Align(
alignment: Alignment.topLeft,
child: IconButton(
icon: const Icon(Icons.arrow_back,
color: AppColor.primaryColor),
onPressed: () => controller.toggleManualFormView(),
),
),
const SizedBox(height: 10),
Text(
isRegister ? '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: isRegister ? 'Create Account'.tr : 'Login'.tr,
onPressed: () {
if (controller.formKey.currentState!.validate()) {
if (isRegister) {
// Registration Logic
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 {
// Login Logic
controller.loginWithGoogleCredential(
controller.passwordController.text,
controller.emailController.text,
);
}
}
},
),
const SizedBox(height: 20),
// Dynamic toggle button for Login/Register
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style:
AppStyle.subtitle.copyWith(color: Colors.grey[700]),
children: [
TextSpan(
text: isRegister
? 'Already have an account? '.tr
: 'Don\'t have an account? '.tr,
),
TextSpan(
text: isRegister ? 'Login'.tr : 'Register'.tr,
style: TextStyle(
color: AppColor.primaryColor,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () => controller.toggleRegisterMode(),
),
],
),
),
)
],
),
),
),
),
),
);
}
/// A helper method to create styled TextFormFields.
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),
),
),
);
}
/// A helper for creating consistent social login buttons.
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),
),
);
}
/// A wrapper page for the Country Picker for a better UX.
Widget _buildCountryPickerPage() {
return Scaffold(
backgroundColor: AppColor.secondaryColor,
appBar: AppBar(
title: Text("Select Your Country".tr, style: AppStyle.headTitle2),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
),
body: CountryPicker(), // Your existing country picker widget
);
}
// --- The following pages are kept as they are well-designed ---
/// UI for the Terms and Conditions agreement page.
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, color: Colors.black87),
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 = () {
Get.defaultDialog(
title: 'Privacy Policy'.tr,
content: SizedBox(
height: Get.height * 0.6,
child: SingleChildScrollView(
child:
HtmlWidget(AppInformation.privacyPolicy)),
),
);
}),
TextSpan(text: " and acknowledge our Privacy Policy.".tr),
],
),
),
const Spacer(),
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()
: () {}, // Button is disabled if not agreed
),
),
],
),
);
}
/// UI for the Location Permission request page.
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,
onPressed: () async {
await getLocationPermission();
if (await Permission.location.isGranted) {
box.write(BoxName.locationPermission, 'true');
controller.update();
}
},
kolor: AppColor.greenColor,
),
const SizedBox(height: 16),
TextButton(
onPressed: () => openAppSettings(),
child: Text("Open Settings".tr,
style: const TextStyle(color: AppColor.blueColor)),
),
],
),
);
}
}