import 'dart:io'; import 'package:sefer_driver/constant/box_name.dart'; import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart'; import 'package:sefer_driver/views/notification/available_rides_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:flutter_font_icons/flutter_font_icons.dart'; import 'package:sefer_driver/views/home/Captin/home_captain/drawer_captain.dart'; import 'package:sefer_driver/views/widgets/mycircular.dart'; import 'package:bubble_head/bubble.dart'; import '../../../../constant/colors.dart'; import '../../../../constant/info.dart'; import '../../../../constant/style.dart'; import '../../../../controller/functions/location_controller.dart'; import '../../../../controller/functions/overlay_permisssion.dart'; import '../../../../controller/functions/package_info.dart'; import '../../../../controller/home/captin/home_captain_controller.dart'; import '../../../../print.dart'; import '../../../widgets/circle_container.dart'; import '../driver_map_page.dart'; import 'widget/connect.dart'; import 'widget/left_menu_map_captain.dart'; import '../../../../main.dart'; // ================================================================= // STEP 1: Modify your LocationController // ================================================================= /* In your `location_controller.dart` file, change `myLocation` and `heading` to be observable by adding `.obs`. This will allow other parts of your app, like the map, to automatically react to changes. // BEFORE: // LatLng myLocation = LatLng(....); // double heading = 0.0; // AFTER: final myLocation = const LatLng(30.0444, 31.2357).obs; // Default to Cairo or a sensible default final heading = 0.0.obs; // When you update these values elsewhere in your controller, // make sure to update their `.value` property. // e.g., myLocation.value = newLatLng; // e.g., heading.value = newHeading; */ // ================================================================= // STEP 2: Modify your HomeCaptainController // ================================================================= /* In your `home_captain_controller.dart` file, you need to add logic to listen for changes from the LocationController and animate the camera. class HomeCaptainController extends GetxController { // ... your existing variables (mapController, carIcon, etc.) // Make sure you have a reference to the GoogleMapController GoogleMapController? mapHomeCaptainController; @override void onInit() { super.onInit(); _setupLocationListener(); } void onMapCreated(GoogleMapController controller) { mapHomeCaptainController = controller; // Any other map setup logic } // THIS IS THE NEW LOGIC TO ADD void _setupLocationListener() { final locationController = Get.find(); // The 'ever' worker from GetX listens for changes to an observable variable. // Whenever `heading` or `myLocation` changes, it will call our method. ever(locationController.heading, (_) => _updateCameraPosition()); ever(locationController.myLocation, (_) => _updateCameraPosition()); } void _updateCameraPosition() { final locationController = Get.find(); if (mapHomeCaptainController != null) { final newPosition = CameraPosition( target: locationController.myLocation.value, zoom: 17.5, // A bit closer for a navigation feel tilt: 50.0, // A nice 3D perspective bearing: locationController.heading.value, // This rotates the map ); // Animate the camera smoothly to the new position and rotation mapHomeCaptainController!.animateCamera( CameraUpdate.newCameraPosition(newPosition), ); } } // ... rest of your controller code } */ // ================================================================= // STEP 3: Update the HomeCaptain Widget // ================================================================= class HomeCaptain extends StatelessWidget { HomeCaptain({super.key}); final LocationController locationController = Get.put(LocationController()); final HomeCaptainController homeCaptainController = Get.put(HomeCaptainController()); @override Widget build(BuildContext context) { Get.put(HomeCaptainController()); WidgetsBinding.instance.addPostFrameCallback((_) async { closeOverlayIfFound(); checkForUpdate(context); getPermissionOverlay(); showDriverGiftClaim(context); }); return Scaffold( appBar: AppBar( elevation: 2, flexibleSpace: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.white, Colors.grey.shade50], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), spreadRadius: 1, blurRadius: 4, ), ], ), ), title: Row( children: [ Image.asset( 'assets/images/logo.gif', height: 32, width: 35, ), const SizedBox(width: 8), Text( AppInformation.appName.split(' ')[0].toString().tr, style: AppStyle.title.copyWith( fontSize: 22, fontWeight: FontWeight.w600, color: AppColor.blueColor, ), ), ], ), actions: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: MyCircleContainer( child: Text( homeCaptainController.countRefuse.toString(), style: AppStyle.title, ), ), ), Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), spreadRadius: 1, blurRadius: 4, ), ], ), child: Row( children: [ _MapControlButton( icon: Icons.satellite_alt, tooltip: 'Change Map Type'.tr, onPressed: homeCaptainController.changeMapType, ), _MapControlButton( icon: Icons.streetview_sharp, tooltip: 'Toggle Traffic'.tr, onPressed: homeCaptainController.changeMapTraffic, ), _MapControlButton( icon: Icons.my_location, // Changed for clarity tooltip: 'Center on Me'.tr, onPressed: () { // This button now just re-centers without changing rotation if (homeCaptainController.mapHomeCaptainController != null) { homeCaptainController.mapHomeCaptainController! .animateCamera(CameraUpdate.newLatLngZoom( Get.find().myLocation, 17.5, )); } }, ), ], ), ), const SizedBox(width: 8), ], ), drawer: CupertinoDrawerCaptain(), body: Stack( children: [ // FIX: Replaced nested GetBuilder/Obx with a single GetX widget. // GetX handles both observable (.obs) variables and standard controller updates. GetBuilder(builder: (controller) { return controller.isLoading ? const MyCircularProgressIndicator() : GoogleMap( onMapCreated: controller.onMapCreated, minMaxZoomPreference: const MinMaxZoomPreference(6, 18), initialCameraPosition: CameraPosition( // Use .value to get the latest location from the reactive variable target: locationController.myLocation, zoom: 15, ), onCameraMove: (position) { CameraPosition( target: locationController.myLocation, zoom: 17.5, // A bit closer for a navigation feel tilt: 50.0, // A nice 3D perspective bearing: locationController.heading, // This rotates the map ); }, markers: { Marker( markerId: MarkerId('MyLocation'.tr), // Use .value for position and rotation from the reactive variable position: locationController.myLocation, rotation: locationController.heading, // IMPORTANT: These two properties make the marker look // correct when the map is tilted and rotating. flat: true, anchor: const Offset(0.5, 0.5), icon: controller.carIcon, ) }, mapType: controller.mapType ? MapType.satellite : MapType.terrain, myLocationButtonEnabled: false, // Disable default button myLocationEnabled: false, // We use our custom marker trafficEnabled: controller.mapTrafficON, buildingsEnabled: true, mapToolbarEnabled: true, zoomControlsEnabled: false, // Cleaner UI for navigation ); }), // The rest of your UI remains the same... Positioned( bottom: 10, right: Get.width * .1, left: Get.width * .1, child: const ConnectWidget()), Positioned( top: 5, right: Get.width * .05, left: Get.width * .05, child: GetBuilder( builder: (homeCaptainController) { return Container( decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.white, Colors.white70], ), borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), spreadRadius: 2, blurRadius: 8, offset: const Offset(0, 2), ), ], ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), width: Get.width * .8, height: 120, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppColor.greenColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ const Icon( Entypo.wallet, color: AppColor.greenColor, size: 20, ), const SizedBox(width: 8), Text( '${"Today".tr}: ${(homeCaptainController.totalMoneyToday)}', style: AppStyle.title.copyWith( color: AppColor.greenColor, fontWeight: FontWeight.bold, ), ), ], ), ), Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppColor.yellowColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ const Icon( Entypo.wallet, color: AppColor.yellowColor, size: 20, ), const SizedBox(width: 8), Text( '${AppInformation.appName}: ${(homeCaptainController.totalMoneyInSEFER)}', style: AppStyle.title.copyWith( color: AppColor.yellowColor, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${'Total Points is'.tr}: ${(homeCaptainController.totalPoints)}', style: AppStyle.title.copyWith( fontSize: 16, fontWeight: FontWeight.w600, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: int.parse((homeCaptainController .countRideToday)) < 5 ? AppColor.accentColor : int.parse((homeCaptainController .countRideToday)) > 5 && int.parse((homeCaptainController .countRideToday)) < 10 ? AppColor.yellowColor : AppColor.greenColor, ), child: Row( children: [ const Icon( Icons.directions_car_rounded, color: Colors.white, size: 18, ), const SizedBox(width: 4), Text( '${"Ride Today : ".tr}: ${(homeCaptainController.countRideToday)}', style: AppStyle.title.copyWith( color: Colors.white, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ], ), ); }, ), ), Positioned( bottom: 65, right: Get.width * .1, left: Get.width * .1, child: GetBuilder( builder: (homeCaptainController) => Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), spreadRadius: 2, blurRadius: 8, offset: const Offset(0, 2), ), ], ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.timer_outlined, color: AppColor.greenColor), const SizedBox(width: 8), Text( 'Active Duration:'.tr, style: AppStyle.title, ), const SizedBox(width: 4), Text( (homeCaptainController.stringActiveDuration), style: AppStyle.title.copyWith( fontWeight: FontWeight.bold, color: AppColor.greenColor, ), ), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.access_time, color: AppColor.accentColor), const SizedBox(width: 8), Text( 'Total Connection Duration:'.tr, style: AppStyle.title, ), const SizedBox(width: 4), Text( (homeCaptainController.totalDurationToday), style: AppStyle.title.copyWith( fontWeight: FontWeight.bold, color: AppColor.accentColor, ), ), ], ), ], ), ), ), ), Positioned( bottom: Get.height * .2, right: 6, child: Column( children: [ Platform.isAndroid ? AnimatedContainer( duration: const Duration(microseconds: 200), width: homeCaptainController.widthMapTypeAndTraffic, decoration: BoxDecoration( border: Border.all(color: AppColor.blueColor), color: AppColor.secondaryColor, borderRadius: BorderRadius.circular(15)), child: IconButton( onPressed: () async { Bubble().startBubbleHead(sendAppToBackground: true); }, icon: Image.asset( 'assets/images/logo1.png', fit: BoxFit.cover, width: 35, height: 35, ), ), ) : const SizedBox(), const SizedBox( height: 5, ), AnimatedContainer( duration: const Duration(microseconds: 200), width: homeCaptainController.widthMapTypeAndTraffic, decoration: BoxDecoration( border: Border.all(color: AppColor.blueColor), color: AppColor.secondaryColor, borderRadius: BorderRadius.circular(15)), child: IconButton( onPressed: () { Get.to(() => const AvailableRidesPage()); }, icon: const Icon( Icons.train_sharp, size: 29, color: AppColor.blueColor, ), ), ), const SizedBox( height: 5, ), GetBuilder( builder: (homeCaptainController) { return box.read(BoxName.rideStatus) == 'Applied' || box.read(BoxName.rideStatus) == 'Begin' ? Positioned( bottom: Get.height * .2, right: 6, child: AnimatedContainer( duration: const Duration(microseconds: 200), width: homeCaptainController.widthMapTypeAndTraffic, decoration: BoxDecoration( border: Border.all(color: AppColor.blueColor), color: AppColor.secondaryColor, borderRadius: BorderRadius.circular(15)), child: GestureDetector( onLongPress: () { box.write(BoxName.rideStatus, 'delete'); homeCaptainController.update(); }, child: IconButton( onPressed: () { box.read(BoxName.rideStatus) == 'Applied' ? { Get.to( () => PassengerLocationMapPage(), arguments: box .read(BoxName.rideArguments)), Get.put(MapDriverController()) .changeRideToBeginToPassenger() } : { Get.to( () => PassengerLocationMapPage(), arguments: box .read(BoxName.rideArguments)), Get.put(MapDriverController()) .startRideFromStartApp() }; }, icon: const Icon( Icons.directions_rounded, size: 29, color: AppColor.blueColor, ), ), ), ), ) : const SizedBox(); }) ], ), ), leftMainMenuCaptainIcons(), ], ), ); } } // These helper widgets and functions remain unchanged showFirstTimeOfferNotification(BuildContext context) async { bool isFirstTime = _checkIfFirstTime(); if (isFirstTime) { WidgetsBinding.instance.addPostFrameCallback((_) { showDialog( context: context, builder: (BuildContext context) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), elevation: 0, backgroundColor: Colors.transparent, child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( shape: BoxShape.rectangle, color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: const [ BoxShadow( color: Colors.black26, blurRadius: 10.0, offset: Offset(0.0, 10.0), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Welcome Offer!'.tr, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 15), Text( 'As a new driver, you\'re eligible for a special offer!'.tr, textAlign: TextAlign.center, style: const TextStyle(fontSize: 16), ), const SizedBox(height: 20), Stack( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.green, borderRadius: BorderRadius.circular(15), ), child: Text( '300 LE'.tr, style: const TextStyle( color: Colors.white, fontSize: 25, fontWeight: FontWeight.bold, ), ), ), Positioned( right: -10, top: -10, child: Container( padding: const EdgeInsets.all(5), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: const Icon( Icons.attach_money, color: Colors.white, size: 20, ), ), ), ], ), const SizedBox(height: 20), Text( 'for your first registration!'.tr, style: const TextStyle(fontSize: 16), ), const SizedBox(height: 20), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.green, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 10), child: Text( "Get it Now!".tr, style: const TextStyle(fontSize: 18, color: Colors.white), ), ), onPressed: () { _markAsNotFirstTime(); Navigator.of(context).pop(); }, ), ], ), ), ); }, ); }); } } bool _checkIfFirstTime() { if (box.read(BoxName.isFirstTime).toString() == '') { return true; } else { return false; } } void _markAsNotFirstTime() { box.write(BoxName.isFirstTime, 'false'); } class _MapControlButton extends StatelessWidget { final IconData icon; final VoidCallback onPressed; final String tooltip; const _MapControlButton({ required this.icon, required this.onPressed, required this.tooltip, }); @override Widget build(BuildContext context) { return Tooltip( message: tooltip, child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(12), onTap: onPressed, child: Container( padding: const EdgeInsets.all(8), child: Icon( icon, size: 24, color: AppColor.blueColor, ), ), ), ), ); } }