import 'package:Intaleq/controller/functions/crud.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:get/get.dart'; import 'package:Intaleq/constant/box_name.dart'; import 'package:Intaleq/constant/colors.dart'; import 'package:Intaleq/constant/style.dart'; import 'package:Intaleq/main.dart'; import 'package:Intaleq/views/widgets/my_scafold.dart'; import 'package:path/path.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../constant/info.dart'; import '../../controller/auth/apple_signin_controller.dart'; import '../../controller/auth/login_controller.dart'; import '../widgets/elevated_btn.dart'; import 'otp_page.dart'; class LoginPage extends StatelessWidget { final controller = Get.put(LoginController()); final AuthController authController = Get.put(AuthController()); LoginPage({super.key}); @override Widget build(BuildContext context) { Get.put(LoginController()); Get.put(CRUD()); return GetBuilder( builder: (controller) => MyScafolld( title: 'Login'.tr, isleading: false, body: [ if (box.read(BoxName.agreeTerms) != 'agreed') _buildAgreementPage(context, controller) else if (box.read(BoxName.locationPermission) != 'true') _buildLocationPermissionDialog(context, controller) else PhoneNumberScreen() ], ), ); } // ───────────────────────────────────────────────────────────────────────── // SHARED HELPERS // ───────────────────────────────────────────────────────────────────────── /// Subtle geometric background — two soft circles, no heavy blur needed. Widget _buildBackground(BuildContext context) { final isDark = Theme.of(context).brightness == Brightness.dark; return Stack( children: [ // Base gradient Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isDark ? [const Color(0xFF0D0D14), const Color(0xFF161622)] : [const Color(0xFFF8F9FF), const Color(0xFFEFF1FB)], ), ), ), // Top-right accent circle Positioned( top: -80, right: -60, child: Container( width: 260, height: 260, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ AppColor.primaryColor.withOpacity(isDark ? 0.18 : 0.12), AppColor.primaryColor.withOpacity(0.0), ], ), ), ), ), // Bottom-left accent circle Positioned( bottom: -100, left: -80, child: Container( width: 320, height: 320, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ AppColor.primaryColor.withOpacity(isDark ? 0.12 : 0.08), AppColor.primaryColor.withOpacity(0.0), ], ), ), ), ), ], ); } /// Glassy card container used across screens. Widget _glassCard({ required Widget child, required bool isDark, EdgeInsets padding = const EdgeInsets.all(20), double radius = 20, }) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(radius), color: isDark ? Colors.white.withOpacity(0.05) : Colors.white.withOpacity(0.75), border: Border.all( color: isDark ? Colors.white.withOpacity(0.08) : Colors.white.withOpacity(0.9), width: 1, ), boxShadow: [ BoxShadow( color: isDark ? Colors.black.withOpacity(0.3) : Colors.black.withOpacity(0.06), blurRadius: 24, offset: const Offset(0, 8), ), ], ), padding: padding, child: child, ); } /// Pill-shaped icon badge with gradient background. Widget _iconBadge(IconData icon, bool isDark) { return Container( width: 80, height: 80, decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ AppColor.primaryColor, AppColor.primaryColor.withOpacity(0.7), ], ), boxShadow: [ BoxShadow( color: AppColor.primaryColor.withOpacity(0.35), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: Icon(icon, color: Colors.white, size: 36), ); } /// Section divider line. Widget _divider(bool isDark) => Container( height: 1, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.transparent, isDark ? Colors.white.withOpacity(0.1) : Colors.black.withOpacity(0.08), Colors.transparent, ], ), ), ); // ───────────────────────────────────────────────────────────────────────── // AGREEMENT PAGE // ───────────────────────────────────────────────────────────────────────── Widget _buildAgreementPage(BuildContext context, LoginController controller) { final isDark = Theme.of(context).brightness == Brightness.dark; final textMain = isDark ? Colors.white : const Color(0xFF1A1A2E); final textSub = isDark ? Colors.white60 : const Color(0xFF6B7280); return Stack( children: [ _buildBackground(context), SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), child: Column( children: [ // ── Header ────────────────────────────────────────────── const SizedBox(height: 20), _iconBadge(Icons.policy_outlined, isDark), const SizedBox(height: 16), Text( "passenger agreement".tr, textAlign: TextAlign.center, style: AppStyle.headTitle2.copyWith( color: textMain, fontSize: 22, fontWeight: FontWeight.w700, letterSpacing: 0.3, ), ), const SizedBox(height: 10), // Subtitle with link RichText( textAlign: TextAlign.center, text: TextSpan( style: AppStyle.title.copyWith( height: 1.6, color: textSub, fontSize: 13.5, ), children: [ TextSpan( text: "To become a passenger, you must review and agree to the " .tr, ), TextSpan( text: 'Terms of Use'.tr, style: TextStyle( decoration: TextDecoration.underline, decorationColor: AppColor.primaryColor, color: AppColor.primaryColor, fontWeight: FontWeight.w600, ), recognizer: TapGestureRecognizer() ..onTap = () { launchUrl(Uri.parse( 'https://intaleq.xyz/intaleq/privacy_policy.php')); }, ), TextSpan(text: " and acknowledge our Privacy Policy.".tr), ], ), ), const SizedBox(height: 16), _divider(isDark), const SizedBox(height: 12), // ── Policy scroll area ────────────────────────────────── Expanded( child: _glassCard( isDark: isDark, padding: EdgeInsets.zero, child: ClipRRect( borderRadius: BorderRadius.circular(20), child: SingleChildScrollView( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(18), child: HtmlWidget( box.read(BoxName.lang).toString() == 'ar' ? AppInformation.privacyPolicyArabic : AppInformation.privacyPolicy, textStyle: TextStyle( color: textSub, fontSize: 13, height: 1.7, ), ), ), ), ), ), const SizedBox(height: 12), _divider(isDark), // ── Checkbox row ──────────────────────────────────────── _glassCard( isDark: isDark, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), child: CheckboxListTile( title: Text( 'I Agree'.tr, style: AppStyle.title.copyWith( color: textMain, fontWeight: FontWeight.w600, fontSize: 14, ), ), value: controller.isAgreeTerms, onChanged: (value) => controller.changeAgreeTerm(), activeColor: AppColor.primaryColor, checkColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6)), controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, dense: true, ), ), const SizedBox(height: 12), // ── CTA Button ────────────────────────────────────────── _buildPrimaryButton( label: 'Continue'.tr, enabled: controller.isAgreeTerms, onPressed: controller.isAgreeTerms ? () => controller.saveAgreementTerms() : () {}, isDark: isDark, ), const SizedBox(height: 8), ], ), ), ), ], ); } // ───────────────────────────────────────────────────────────────────────── // EMAIL / PASSWORD FORM // ───────────────────────────────────────────────────────────────────────── Widget buildEmailPasswordForm( BuildContext context, LoginController controller) { final isDark = Theme.of(context).brightness == Brightness.dark; final textMain = isDark ? Colors.white : const Color(0xFF1A1A2E); final textSub = isDark ? Colors.white60 : const Color(0xFF6B7280); return Stack( children: [ _buildBackground(context), SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Logo / badge Center(child: _iconBadge(Icons.lock_outline_rounded, isDark)), const SizedBox(height: 20), Text( 'Welcome Back'.tr, textAlign: TextAlign.center, style: AppStyle.headTitle2.copyWith( color: textMain, fontSize: 26, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 6), Text( 'Sign in to continue'.tr, textAlign: TextAlign.center, style: AppStyle.title.copyWith(color: textSub, fontSize: 14), ), const SizedBox(height: 32), _glassCard( isDark: isDark, padding: const EdgeInsets.all(24), child: Form( key: controller.formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Email field _buildTextField( controller: controller.emailController, label: 'Email'.tr, hint: 'Your email address'.tr, icon: Icons.email_outlined, isDark: isDark, keyboardType: TextInputType.emailAddress, validator: (value) => value == null || value.isEmpty || !value.contains('@') || !value.contains('.') ? 'Enter a valid email'.tr : null, ), const SizedBox(height: 16), // Password field _buildTextField( controller: controller.passwordController, label: 'Password'.tr, hint: 'Your password'.tr, icon: Icons.lock_outline, isDark: isDark, obscureText: true, validator: (value) => value == null || value.isEmpty ? 'Enter your password'.tr : null, ), const SizedBox(height: 28), GetBuilder( builder: (controller) => controller.isloading ? Center( child: SizedBox( width: 28, height: 28, child: CircularProgressIndicator( color: AppColor.primaryColor, strokeWidth: 2.5, ), ), ) : _buildPrimaryButton( label: 'Submit'.tr, enabled: true, isDark: isDark, onPressed: () { if (controller.formKey.currentState! .validate()) { controller.login(); } }, ), ), ], ), ), ), ], ), ), ), ), ], ); } // ───────────────────────────────────────────────────────────────────────── // LOCATION PERMISSION PAGE // ───────────────────────────────────────────────────────────────────────── Widget _buildLocationPermissionDialog( BuildContext context, LoginController controller) { final isDark = Theme.of(context).brightness == Brightness.dark; final textMain = isDark ? Colors.white : const Color(0xFF1A1A2E); final textSub = isDark ? Colors.white60 : const Color(0xFF6B7280); return Stack( children: [ _buildBackground(context), SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Spacer(flex: 2), // Animated-look stacked circles around icon Stack( alignment: Alignment.center, children: [ // Outer glow ring Container( width: 140, height: 140, decoration: BoxDecoration( shape: BoxShape.circle, color: AppColor.primaryColor.withOpacity(0.08), border: Border.all( color: AppColor.primaryColor.withOpacity(0.2), width: 1, ), ), ), // Mid ring Container( width: 108, height: 108, decoration: BoxDecoration( shape: BoxShape.circle, color: AppColor.primaryColor.withOpacity(0.12), border: Border.all( color: AppColor.primaryColor.withOpacity(0.3), width: 1, ), ), ), // Core badge _iconBadge(Icons.location_on_outlined, isDark), ], ), const SizedBox(height: 36), Text( 'Enable Location Access'.tr, style: AppStyle.headTitle2.copyWith( color: textMain, fontSize: 24, fontWeight: FontWeight.w700, letterSpacing: 0.2, ), textAlign: TextAlign.center, ), const SizedBox(height: 14), Text( 'We need your location to find nearby drivers for pickups and drop-offs.' .tr, textAlign: TextAlign.center, style: AppStyle.title.copyWith( color: textSub, fontSize: 14.5, height: 1.6, ), ), const SizedBox(height: 40), // Feature chips row Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _featureChip( Icons.speed_outlined, 'Fast matching'.tr, isDark), const SizedBox(width: 10), _featureChip(Icons.shield_outlined, 'Secure'.tr, isDark), const SizedBox(width: 10), _featureChip(Icons.near_me_outlined, 'Nearby'.tr, isDark), ], ), const Spacer(flex: 3), _buildPrimaryButton( label: 'Next'.tr, enabled: true, isDark: isDark, onPressed: () async => await controller.getLocationPermission(), ), const SizedBox(height: 16), ], ), ), ), ], ); } // ───────────────────────────────────────────────────────────────────────── // SHARED SMALL WIDGETS // ───────────────────────────────────────────────────────────────────────── /// Reusable styled text field. Widget _buildTextField({ required TextEditingController controller, required String label, required String hint, required IconData icon, required bool isDark, bool obscureText = false, TextInputType? keyboardType, String? Function(String?)? validator, }) { final fill = isDark ? Colors.white.withOpacity(0.05) : Colors.white; final border = isDark ? Colors.white.withOpacity(0.1) : const Color(0xFFE5E7EB); return TextFormField( controller: controller, obscureText: obscureText, keyboardType: keyboardType, style: TextStyle( color: isDark ? Colors.white : const Color(0xFF1A1A2E), fontSize: 14.5, ), decoration: InputDecoration( labelText: label, hintText: hint, prefixIcon: Icon(icon, size: 20, color: isDark ? Colors.white38 : const Color(0xFF9CA3AF)), labelStyle: TextStyle( color: isDark ? Colors.white38 : const Color(0xFF9CA3AF), fontSize: 13.5, ), hintStyle: TextStyle( color: isDark ? Colors.white24 : const Color(0xFFD1D5DB), ), filled: true, fillColor: fill, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: BorderSide(color: border, width: 1), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: BorderSide(color: AppColor.primaryColor, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: const BorderSide(color: Color(0xFFEF4444), width: 1), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: const BorderSide(color: Color(0xFFEF4444), width: 1.5), ), ), validator: validator, ); } /// Full-width gradient primary button. Widget _buildPrimaryButton({ required String label, required bool enabled, required VoidCallback onPressed, required bool isDark, }) { return SizedBox( width: double.infinity, height: 54, child: DecoratedBox( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), gradient: enabled ? LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ AppColor.primaryColor, AppColor.primaryColor.withOpacity(0.8), ], ) : null, color: enabled ? null : (isDark ? Colors.white12 : const Color(0xFFE5E7EB)), boxShadow: enabled ? [ BoxShadow( color: AppColor.primaryColor.withOpacity(0.32), blurRadius: 16, offset: const Offset(0, 6), ) ] : null, ), child: ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), ), child: Text( label, style: TextStyle( color: enabled ? Colors.white : (isDark ? Colors.white38 : const Color(0xFF9CA3AF)), fontWeight: FontWeight.w600, fontSize: 15, letterSpacing: 0.4, ), ), ), ), ); } /// Small chip used on location page. Widget _featureChip(IconData icon, String text, bool isDark) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(30), color: isDark ? Colors.white.withOpacity(0.06) : Colors.white.withOpacity(0.8), border: Border.all( color: isDark ? Colors.white.withOpacity(0.1) : Colors.black.withOpacity(0.07), width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 14, color: AppColor.primaryColor), const SizedBox(width: 5), Text( text, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: isDark ? Colors.white70 : const Color(0xFF374151), ), ), ], ), ); } }