import 'dart:async'; import 'dart:developer'; import 'dart:math' as math; import 'dart:ui' as ui; import 'package:audioplayers/audioplayers.dart'; import 'package:wanisdriver/Provider/driver_provider.dart'; import 'package:wanisdriver/config/AppSnackbar.dart'; import 'package:wanisdriver/config/enum/ride_enum.dart'; import 'package:wanisdriver/utils/colors.dart'; import 'package:wanisdriver/widgets/cardtripinfo.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; import 'package:get_it/get_it.dart'; // import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:geolocator/geolocator.dart' hide Position; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:beemwidget/text_styles/beem_text_styles.dart'; import 'package:vibration/vibration.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import '../Provider/RideNotification.dart'; import '../Provider/RideProvider.dart'; import '../Provider/RideStatusProvider.dart'; import '../Provider/language_provider.dart'; import '../Provider/utils_singleton.dart'; import '../config/constants/arabe.dart' as Language; import '../config/custom_loader.dart'; import '../config/map_style.dart'; import '../main.dart'; import '../models/DriverLocationDto.dart'; import '../models/RideStop.dart'; import '../models/RideUpdateRequest.dart'; import '../routes/app_routes.dart'; import '../services/PubNubDriverManager.dart'; import '../services/audio_service.dart'; import '../widgets/RideStatusWidget/CancelPickupConfirmationWidget.dart'; import '../widgets/RideStatusWidget/CollectCash.dart'; import '../widgets/RideStatusWidget/GoOnlineConfirmationWidget.dart'; import '../widgets/RideStatusWidget/RateRide.dart'; import '../widgets/RideStatusWidget/rideSummery.dart'; import '../widgets/wanis_kids_video_call.dart'; import '../widgets/custom_button.dart'; import '../widgets/custom_text.dart'; import 'CancelRideDialog.dart'; import 'MainScreen.dart'; // import 'package:google_maps_flutter/google_maps_flutter.dart' as gms; import 'package:latlong2/latlong.dart' as latlng; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' hide LocationSettings; class StopItem { final RideStop stop; bool used; StopItem({required this.stop, this.used = false}); } extension ToMapbox on latlng.LatLng { Point get mb => Point(coordinates: Position(longitude, latitude)); } extension ListToMapbox on List { List get mb => map((p) => Position(p.longitude, p.latitude)).toList(); } // Screen height range enum enum ScreenHeightRange { small, // < 700 medium, // 700-900 large, // > 900 } class NavigationSecreenToClientsecreen extends StatefulWidget { @override _NavigationSecreenToClientState createState() => _NavigationSecreenToClientState(); } class _NavigationSecreenToClientState extends State with WidgetsBindingObserver { // ------------------------------------------------------------------ LOGIC MapboxMap? _controller; PointAnnotationManager? _pointAnnotationManager; PolylineAnnotationManager? _polylineAnnotationManager; bool _isFetchingRoute = false; bool _isGoTofinalPosition = false; bool _mapReady = false; bool _isDisposing = false; // ------------------------------------------------------------------ SCREEN HEIGHT MANAGEMENT double screenHeight = 0.0; ScreenHeightRange screenHeightRange = ScreenHeightRange.medium; // ------------------------------------------------------------------ STATE double currentZoomLevel = 17.0; latlng.LatLng? taxiPosition; latlng.LatLng? distinationposition; Set polylineCoordinates = {}; Completer? _currentModalCompleter; StreamSubscription? _positionStreamSubscription; bool _isLoading = true; bool _isprocessing = false; double distanceKm = 0; int etaSeconds = 0; // ------------------------------------------------------------------ Socket late DriverPubNubManager _pubnubManager; late RideProvider _rideProvider; late DriverProvider _driverProvider; late RideStatusProvider _rideStatusProvider; late RideNotification _rideNotificationProvider ; StreamSubscription? _rideUpdateSubscription; List stopItems = []; bool _isInitialized = false; final player = AudioPlayer(); Timer? _ridePollTimer; bool _isFollowing = true; AppLifecycleState _appLifecycleState = AppLifecycleState.resumed; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _playAudio(); // Use addPostFrameCallback to ensure providers are ready. WidgetsBinding.instance.addPostFrameCallback((_) { // Initialize screen height detection _initializeScreenHeight(); // Get providers once and store them. _rideProvider = context.read(); _pubnubManager = context.read(); _driverProvider = context.read(); _rideStatusProvider = context.read(); _rideNotificationProvider = context.read(); // Start the single, unified initialization flow. _initializeScreen(); }); } Future _initializeScreen() async { if(_isInitialized){ return; } setState(() { _isInitialized=true; }); log("๐ŸŸข [Init] _initializeScreen started."); // Check ride existence if (_rideProvider.currentride == null) { log("โŒ Aborting initialization: rideProvider.currentride is NULL."); if (mounted) { log("โžก๏ธ Navigating back to main screen."); Get.offAllNamed(AppRoutes.main); } return; } if (_rideStatusProvider.ridestatus != RideEnum.idle) { log("๐Ÿ”„ Handling ride status change: ${_rideStatusProvider.ridestatus}"); handleStatusChange(_rideStatusProvider.ridestatus); } else { log("๐Ÿ”„ No status found, defaulting to RideEnum.to_client"); handleStatusChange(RideEnum.to_client); } DriverLocationDto? locationdriver = await _driverProvider.getdriverlocaiton(_rideProvider.currentride!.driverId!); _pubnubManager.connect(uuid: utils.user!.username!); log("๐Ÿงน Clearing old rideUpdateSubscription (if any)."); await _rideUpdateSubscription?.cancel(); // _kafkaSubscription = KafkaHandler.instance.driverLocationStream.listen(_handleRideUpdate); _rideUpdateSubscription = _pubnubManager.rideUpdateStream.listen(_handleRideUpdate); log("๐Ÿ“Œ Updated rideProvider.currentride = ${_rideProvider.currentride}"); if (_rideProvider.currentride == null || _rideProvider.currentride?.startLocation == null) { log("โŒ Ride or startLocation is NULL. Aborting initialization."); AppSnackbar.showError("Error", "Could not load ride details."); Navigator.of(context).pop(); return; } // PubNub ride subscription log("๐Ÿ“ก Subscribing to ride channel..."); _pubnubManager.subscribeToRide( rideId: _rideProvider.currentride!.id, riderUsername: _rideProvider.currentride!.riderusername, ); // Stops log("๐Ÿ“ Initializing stops: ${_rideProvider.currentride?.stops}"); initStops(_rideProvider.currentride?.stops); // Destination if(_rideStatusProvider.ridestatus==RideEnum.start_ride){ distinationposition = latlng.LatLng( _rideProvider.getLatitude(_rideProvider.currentride!.endLocation), _rideProvider.getLongitude(_rideProvider.currentride!.endLocation), ); }else{ distinationposition = latlng.LatLng( _rideProvider.getLatitude(_rideProvider.currentride!.startLocation), _rideProvider.getLongitude(_rideProvider.currentride!.startLocation), ); } log("๐Ÿ“ Destination position = $distinationposition"); // Permissions log("โณ Ensuring permissions..."); await _ensurePermission(); log("โœ… Permissions granted."); // GPS First Fix log("โณ Initializing first GPS fix..."); await _initFirstFix(); log("โœ… First GPS fix acquired."); // Start GPS stream log("โ–ถ๏ธ Starting GPS stream..."); _startGpsStream(); // Start polling timer log("โ–ถ๏ธ Starting polling timer..."); _startPollingTimer(); // Final state update setState(() { _isLoading = false; if (stopItems.isEmpty) { _isGoTofinalPosition = true; } distanceKm = locationdriver?.distanceKm ?? 0; etaSeconds = locationdriver?.etaSeconds ?? 0; }); log("โœ… [Init Complete] _initializeScreen finished."); setState(() { _isInitialized=false; }); } // ------------------------------------------------------------------ SCREEN HEIGHT METHODS void _initializeScreenHeight() { screenHeight = MediaQuery.of(context).size.height; screenHeightRange = _getScreenHeightRange(screenHeight); log("Screen height detected: $screenHeight"); log("Screen height range: $screenHeightRange"); } ScreenHeightRange _getScreenHeightRange(double height) { if (height < 700) { return ScreenHeightRange.small; } else if (height <= 900) { return ScreenHeightRange.medium; } else { return ScreenHeightRange.large; } } double _getModalHeight(RideEnum rideStatus) { switch (rideStatus) { case RideEnum.check_availabilty_after_complete: switch (screenHeightRange) { case ScreenHeightRange.small: return screenHeight * 0.4; // Increased for small screens case ScreenHeightRange.medium: return screenHeight * 0.35; case ScreenHeightRange.large: return screenHeight * 0.3; // Decreased for large screens } case RideEnum.cancel_ride: switch (screenHeightRange) { case ScreenHeightRange.small: return screenHeight * 0.4; case ScreenHeightRange.medium: return screenHeight * 0.35; case ScreenHeightRange.large: return screenHeight * 0.3; } case RideEnum.collect_cash: switch (screenHeightRange) { case ScreenHeightRange.small: return screenHeight * 0.7; // Increased for small screens case ScreenHeightRange.medium: return screenHeight * 0.47; case ScreenHeightRange.large: return screenHeight * 0.45; // Decreased for large screens } case RideEnum.rate_client: switch (screenHeightRange) { case ScreenHeightRange.small: return 261.sp; // Fixed height with slight adjustment case ScreenHeightRange.medium: return 261.sp; case ScreenHeightRange.large: return 260.sp; } default: switch (screenHeightRange) { case ScreenHeightRange.small: return screenHeight * 0.4; case ScreenHeightRange.medium: return screenHeight * 0.4; case ScreenHeightRange.large: return screenHeight * 0.35; } } } double _getNavigationButtonBottom() { switch (screenHeightRange) { case ScreenHeightRange.small: return 240; // Closer to bottom for small screens case ScreenHeightRange.medium: return 270; case ScreenHeightRange.large: return 300; // More space from bottom for large screens } } double _getTopContainerPosition() { switch (screenHeightRange) { case ScreenHeightRange.small: return 60; // Closer to top for small screens case ScreenHeightRange.medium: return 50; case ScreenHeightRange.large: return 100; // More space from top for large screens } } // โญ๏ธ 3. Create the methods to start and handle the polling void _startPollingTimer() { // Cancel any existing timer to prevent duplicates. _ridePollTimer?.cancel(); // Start a new periodic timer that runs every 60 seconds. log("start pulling every 60sec ####################################"); _ridePollTimer = Timer.periodic(const Duration(seconds: 5), (timer) { _pollRideStatus(); }); } void _pollRideStatus() async { // Ensure the widget is still active. if (!mounted) { _ridePollTimer?.cancel(); // Cancel timer if widget is disposed return; } if (_rideProvider.currentride?.id != null) { log("Polling for ride status update..."); try { // This will fetch the latest ride data and notify listeners if there are changes. await _rideProvider.finbyidWithUpdate(_rideProvider.currentride!.id); } catch (e) { log("Error during ride polling: $e"); } } } Future _ensurePermission() async { LocationPermission perm = await Geolocator.checkPermission(); if (perm == LocationPermission.denied || perm == LocationPermission.deniedForever) { perm = await Geolocator.requestPermission(); if (perm == LocationPermission.denied || perm == LocationPermission.deniedForever) { throw 'location-permission-denied'; } } } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); _appLifecycleState = state; log("AppLifecycleState changed to: $state"); if (state == AppLifecycleState.resumed) { _rideProvider = context.read(); _pubnubManager = context.read(); _driverProvider = context.read(); _rideStatusProvider = context.read(); _rideNotificationProvider = context.read(); if(_rideProvider.currentride!=null){ _rideProvider.finbyidWithUpdate(_rideProvider.currentride!.id); _initializeScreen(); } } } Future _initFirstFix() async { // try real-time first (fast), then last known (fallback) try { final p = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.best, timeLimit: const Duration(seconds: 5), ); taxiPosition = latlng.LatLng(p.latitude, p.longitude); } on TimeoutException { final p = await Geolocator.getLastKnownPosition(); if (p != null) { taxiPosition = latlng.LatLng(p.latitude, p.longitude); } } } void initStops(List? stops) { stopItems = (stops ?? []).map((s) => StopItem(stop: s)).toList(); } bool isStopUsed(RideStop stop) { final item = stopItems.firstWhere((e) => e.stop.id == stop.id, orElse: () => StopItem(stop: stop)); return item.used; } void markStopUsed(RideStop stop, {bool used = true}) { final idx = stopItems.indexWhere((e) => e.stop.id == stop.id); if (idx != -1) { setState(() { stopItems[idx].used = used; }); } } StopItem? getNextStopItem() { try { return stopItems.firstWhere((item) => item.used == false); } catch (_) { return null; } } latlng.LatLng? moveToNextStop(RideProvider rideProvider) { final nextItem = getNextStopItem(); if (nextItem == null) return null; // build destination from nextItem.stop final loc = nextItem.stop.stopLocation; final pos = latlng.LatLng( rideProvider.getLatitude(loc), rideProvider.getLongitude(loc), ); // mark it used markStopUsed(nextItem.stop, used: true); return pos; } @override void didChangeDependencies() { super.didChangeDependencies(); if (_isLoading) { WidgetsBinding.instance.addPostFrameCallback((_) { _initializeScreen(); }); } } _checkride(){ if(_rideProvider.currentride==null){ return; } if(_rideStatusProvider.ridestatus == RideEnum.start_ride){ log("draw polyline =========>"); _drawFixedPolyline(_rideProvider.currentride!.encodedpolyline); } if(_rideStatusProvider.ridestatus == RideEnum.to_client){ log("draw polyline =========>"); _fetchShortestRoute(); } } void _startGpsStream() { _positionStreamSubscription?.cancel(); _positionStreamSubscription = Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.bestForNavigation, distanceFilter: 5), ).listen((pos) async { if (!mounted || _isDisposing) return; taxiPosition = latlng.LatLng(pos.latitude, pos.longitude); await _updateMapState(); // Add await // โœ… The camera following logic is kept. if (_isFollowing && _mapReady) { _safeAnimateTo(taxiPosition!); } }); } Future _updateDestination(latlng.LatLng newDest) async { if (distinationposition == newDest || !mounted) return; distinationposition = newDest; // Only fetch route if managers are ready if (_mapReady && _polylineAnnotationManager != null) { await _fetchShortestRoute(); } } void _handleRideUpdate(Map data) { log("NavigationScreen received update: $data"); if (!mounted) return; //check driver-location if (data.containsKey('driverId') && data.containsKey('latitude')) { final incomingDriverId = data['driverId']; if (incomingDriverId == utils.user?.driver?.id) { log('driver update data come !!! $data'); distanceKm = data['distanceKm']; etaSeconds = data['etaSeconds']; } } if (data['type'] == 'RIDE_CANCEL_USER') { GetIt.I().playSound(SoundEvent.riderCancelled); AppSnackbar.showInfo("Cancel", "User Cancel the ride"); // Post-frame logic with mounted check WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; Get.offNamed(AppRoutes.main); }); handleStatusChange(RideEnum.idle); } if (data['type'] == 'USER_COMING') { if (_appLifecycleState == AppLifecycleState.resumed) { // --- App is in the FOREGROUND --- // Show the snackbar as usual. AppSnackbar.showSuccess("Coming", "user is coming !!"); } else { // --- App is in the BACKGROUND, screen is off, or locked --- log("App is in background. Showing local notification for USER_COMING."); // TODO: Implement Local Notification, Screen Wake, and Vibration. // You will need to add plugins like 'flutter_local_notifications', // 'wakelock', and 'vibration' to your pubspec.yaml file to enable this. // Example implementation: // 1. Show a local notification flutterLocalNotificationsPlugin.show( 0, 'User is Coming!', 'A user has arrived at the pickup location.', const NotificationDetails( android: AndroidNotificationDetails( 'user_arrival_channel', // โœจ MUST MATCH "FULL_SCREEN_CHANNEL_ID" 'User Arrival Notifications', importance: Importance.max, priority: Priority.high, fullScreenIntent: true, ), ), ); // 2. Wake the screen up WakelockPlus.enable(); // 3. Vibrate the device Vibration.vibrate(duration: 500); // 4. Disable the wakelock after a short period to save battery // Future.delayed(const Duration(seconds: 15), () { // WakelockPlus.disable(); // }); } } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _isDisposing = true; player.dispose(); _rideUpdateSubscription?.cancel(); _positionStreamSubscription?.cancel(); _mapReady = false; try { _pointAnnotationManager?.deleteAll(); } catch (_) {} try { _polylineAnnotationManager?.deleteAll(); } catch (_) {} _pointAnnotationManager = null; _polylineAnnotationManager = null; _controller = null; super.dispose(); } Future _playAudio() async { // Play the audio from your assets folder once. await player.play(AssetSource('audio/tingSound.mp3')); } Future _fetchShortestRoute() async { log('_fetchShortestRoute called - isFetching: $_isFetchingRoute, manager: ${_polylineAnnotationManager != null}, mapReady: $_mapReady'); if (_isDisposing || _isFetchingRoute || _polylineAnnotationManager == null || !_mapReady) return; if (taxiPosition == null || distinationposition == null) return; // Add this check setState(() => _isFetchingRoute = true); try { final rd = await _rideProvider.fetchRouteInfoMapbox(taxiPosition!, distinationposition!); // Add null check for manager (could be disposed during async operation) if (_polylineAnnotationManager == null || !mounted) return; await _polylineAnnotationManager!.deleteAll(); await Future.delayed(const Duration(milliseconds: 100)); // Add small delay final points = rd.points.mb; if (points.isNotEmpty && points.length >= 2) { // Ensure minimum 2 points await _polylineAnnotationManager!.create( PolylineAnnotationOptions( geometry: LineString(coordinates: points), lineColor: Colors.deepOrange.value, lineWidth: 4.0, ), ); } await _updateAllMarkers(); } catch (e) { debugPrint('route error $e'); } finally { if (mounted) setState(() => _isFetchingRoute = false); } } // -------------------------------------------------------------- MODALS (unchanged) void onConfirmPressed(type, rideid) async { final result = await context .read() .confirmPayment(rideId: rideid, paymentType: type); if (result) { handleStatusChange(RideEnum.idle); WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; MainScreen.globalKey.currentState?.navigateToScreen(0); }); /* _closeSheetAnd( () async { // 2. Now close the sheet AND change status handleStatusChange(RideEnum.idle); }, tabIndexToGo: 0, );*/ } else { // show error message AppSnackbar.showError("Error", 'Payment failed !!'); } } Future handleStatusChange(RideEnum newStatus) async { if (_isDisposing || !mounted) { return; } _rideStatusProvider.updatestates(newStatus); if (newStatus == RideEnum.idle) { _isDisposing = true; _ridePollTimer?.cancel(); _positionStreamSubscription?.cancel(); log("Log Clear RideProvider !ยงยงยงยงยง!ยงยงยงยงยง!ยงยงยงยง!ยงยงยงยงยง!"); bool driver = await _driverProvider.update_availabilty_always_online(); if (driver && utils.user?.driver!.available == true) { _rideProvider.clear(); } } else { log('ideprovider.finbyid---'); await _rideProvider.findbyid(_rideProvider.currentride!.id); } _rideNotificationProvider.rideconnectionsocket(); if (newStatus == RideEnum.rate_client || newStatus == RideEnum.check_availabilty_after_complete || newStatus == RideEnum.collect_cash) { if (_isDisposing) return; _showRideStatusModal(newStatus); } } // ALSO UPDATE YOUR NAVIGATION SCREEN _closeSheetAnd method: void _closeSheetAnd( void Function() afterPop, { int? tabIndexToGo, }) { // 1. Complete the completer first _currentModalCompleter?.complete(); // 2. Check if we can pop before attempting if (!mounted || !Navigator.canPop(context)) { // If we can't pop, just run the afterPop logic WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; if (tabIndexToGo != null) { MainScreen.globalKey.currentState?.navigateToScreen(tabIndexToGo); } afterPop(); }); return; } // 3. Pop safely Navigator.of(context).pop(); // 4. Run follow-up work next frame WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; if (tabIndexToGo != null) { print("tabIndexToGo--------------------------$tabIndexToGo"); MainScreen.globalKey.currentState?.navigateToScreen(tabIndexToGo); } afterPop(); }); } void _showRideStatusModal(RideEnum rideStatus) async { final BuildContext parentCtx = context; final currentRide = _rideProvider.currentride; // โญ๏ธ FIX: Add a guard clause to prevent showing the modal with null data if (currentRide == null && (rideStatus == RideEnum.collect_cash || rideStatus == RideEnum.rate_client)) { log("Error: Tried to show modal for $rideStatus but currentRide is null. Aborting."); // If the ride disappeared unexpectedly, go back to a safe state. handleStatusChange(RideEnum.idle); Get.offNamed(AppRoutes.main); return; } if (_currentModalCompleter != null && !_currentModalCompleter!.isCompleted) { _currentModalCompleter!.complete(); Navigator.of(context).pop(); } _currentModalCompleter = Completer(); await showModalBottomSheet( context: parentCtx, isScrollControlled: true, backgroundColor: Colors.transparent, isDismissible: rideStatus == RideEnum.rate_client || rideStatus == RideEnum.collect_cash ? false : true, enableDrag: rideStatus == RideEnum.rate_client || rideStatus == RideEnum.collect_cash ? false : true, builder: (BuildContext context) { double modalHeight = _getModalHeight(rideStatus); return Container( height: modalHeight, width: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.only( topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 10.0, offset: const Offset(0, -4), ), ], ), padding: const EdgeInsets.all(10.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ switch (rideStatus) { // โ”€โ”€ 1) GO ONLINE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ RideEnum.check_availabilty_after_complete => GoOnlineConfirmationWidget( onYesPressed: () { _closeSheetAnd( () async { // 2. Now close the sheet AND change status handleStatusChange(RideEnum.idle); }, tabIndexToGo: 0, ); }, onNoPressed: () { // no async work needed here _closeSheetAnd( () { handleStatusChange(RideEnum.idle); }, tabIndexToGo: 0, ); }, ), // โ”€โ”€ 2) CANCEL PICK-UP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ RideEnum.cancel_ride => CancelPickupConfirmationWidget( onYesPressed: () => _closeSheetAnd( () => handleStatusChange(RideEnum.idle), tabIndexToGo: 0, ), onNoPressed: () => _closeSheetAnd( () => handleStatusChange(RideEnum.idle), tabIndexToGo: 0, ), ), // โ”€โ”€ 3) COLLECT CASH โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ RideEnum.collect_cash => ConfirmPriceWidget( clientName: currentRide!.ridername, price: currentRide.totalPrice ?? 14, currency: 'TND', laodingCash: context.watch().isLoadingPaymentCash, laodingTpe: context.watch().isLoadingPaymentTpe, onConfirmCashPressed: () => onConfirmPressed('CASH', currentRide.id), onConfirmTpePressed: () => onConfirmPressed('TPE', currentRide.id), ), // โ”€โ”€ 4) RATE CLIENT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ RideEnum.rate_client => RateRiderWidget( clientname: currentRide!.ridername, clientImage: currentRide.userimageurl, onRatingSelected: print, onRateRider: (rating) { _rideProvider.rate_user_current_ride(rating); _closeSheetAnd(() => handleStatusChange(RideEnum.collect_cash)); }, ), _ => const SizedBox.shrink(), } ], ), ); }, ); if (_currentModalCompleter != null && !_currentModalCompleter!.isCompleted) { _currentModalCompleter!.complete(); } } // put this just under gms.GoogleMapController? _controller; Future _safeAnimateTo(latlng.LatLng pos) async { if (_isDisposing || !_mapReady || _controller == null) return; try { await _controller!.flyTo( CameraOptions( center: Point( coordinates: Position( pos.longitude ?? 10.6346, // Sousse longitude fallback pos.latitude ?? 35.8256 // Sousse latitude fallback ) ), zoom: 15.0, ), MapAnimationOptions(duration: 1000), ); } catch (e) { log('Camera animation error: $e'); } } // This method takes latitude and longitude as arguments and launches Google Maps void _onNavigationClicked(double lat, double lng) async { final uri = Uri.parse( 'https://www.google.com/maps/dir/?api=1&destination=$lat,$lng&travelmode=driving'); if (await canLaunchUrl(uri)) await launchUrl(uri); } Future _updateAllMarkers() async { // Safety checks - exit early if conditions aren't met if (!mounted || _isDisposing || _pointAnnotationManager == null || !_mapReady) { log('_updateAllMarkers: Skipping - mounted: $mounted, manager: ${_pointAnnotationManager != null}, mapReady: $_mapReady'); return; } final ride = _rideProvider.currentride; if (ride == null) { log('_updateAllMarkers: No current ride available'); return; } try { // Clear existing annotations first await _pointAnnotationManager!.deleteAll(); // Add a small delay to ensure clearing is complete await Future.delayed(const Duration(milliseconds: 50)); final List annotations = []; // 1. Driver marker (taxi position) if (taxiPosition != null) { try { final ByteData driverBytes = await rootBundle.load('assets/icons/taxi_car_marker.png'); final Uint8List driverImageData = driverBytes.buffer.asUint8List(); annotations.add(PointAnnotationOptions( geometry: taxiPosition!.mb, image: driverImageData, iconSize: 2.0, iconAnchor: IconAnchor.BOTTOM, // Anchor at bottom for better positioning // iconRotation: _currentBearing, // Apply GPS bearing )); log('Added driver marker at: ${taxiPosition!.latitude}, ${taxiPosition!.longitude}'); } catch (e) { log('Error loading driver marker: $e'); // Fallback: Add marker without custom icon annotations.add(PointAnnotationOptions( geometry: taxiPosition!.mb, iconSize: 1.0, )); } } // 2. Start location marker (Point A) try { final ByteData startBytes = await rootBundle.load('assets/markers/markerA.png'); final Uint8List startImageData = startBytes.buffer.asUint8List(); annotations.add(PointAnnotationOptions( geometry: Point(coordinates: Position( _rideProvider.getLongitude(ride.startLocation), _rideProvider.getLatitude(ride.startLocation), )), image: startImageData, iconSize: 2.0, iconAnchor: IconAnchor.BOTTOM, )); log('Added start marker at: ${_rideProvider.getLatitude(ride.startLocation)}, ${_rideProvider.getLongitude(ride.startLocation)}'); } catch (e) { log('Error loading start marker: $e'); // Fallback: Add marker without custom icon annotations.add(PointAnnotationOptions( geometry: Point(coordinates: Position( _rideProvider.getLongitude(ride.startLocation), _rideProvider.getLatitude(ride.startLocation), )), iconSize: 1.0, iconColor: Colors.green.value, )); } // 3. End location marker (Point B) try { final ByteData endBytes = await rootBundle.load('assets/markers/markerB.png'); final Uint8List endImageData = endBytes.buffer.asUint8List(); annotations.add(PointAnnotationOptions( geometry: Point(coordinates: Position( _rideProvider.getLongitude(ride.endLocation), _rideProvider.getLatitude(ride.endLocation), )), image: endImageData, iconSize: 2.0, iconAnchor: IconAnchor.BOTTOM, )); log('Added end marker at: ${_rideProvider.getLatitude(ride.endLocation)}, ${_rideProvider.getLongitude(ride.endLocation)}'); } catch (e) { log('Error loading end marker: $e'); // Fallback: Add marker without custom icon annotations.add(PointAnnotationOptions( geometry: Point(coordinates: Position( _rideProvider.getLongitude(ride.endLocation), _rideProvider.getLatitude(ride.endLocation), )), iconSize: 1.0, iconColor: Colors.red.value, )); } // 4. Add stop markers if they exist and are active if (stopItems.isNotEmpty) { for (int i = 0; i < stopItems.length; i++) { final stopItem = stopItems[i]; final stop = stopItem.stop; try { // You can use different markers for stops or reuse existing ones final ByteData stopBytes = await rootBundle.load('assets/markers/markerA.png'); // or create a stop-specific marker final Uint8List stopImageData = stopBytes.buffer.asUint8List(); annotations.add(PointAnnotationOptions( geometry: Point(coordinates: Position( _rideProvider.getLongitude(stop.stopLocation), _rideProvider.getLatitude(stop.stopLocation), )), image: stopImageData, iconSize: 1.5, iconAnchor: IconAnchor.BOTTOM, iconOpacity: stopItem.used ? 0.5 : 1.0, // Make used stops semi-transparent )); log('Added stop marker ${i + 1} at: ${_rideProvider.getLatitude(stop.stopLocation)}, ${_rideProvider.getLongitude(stop.stopLocation)} (used: ${stopItem.used})'); } catch (e) { log('Error loading stop marker $i: $e'); // Fallback for stop marker annotations.add(PointAnnotationOptions( geometry: Point(coordinates: Position( _rideProvider.getLongitude(stop.stopLocation), _rideProvider.getLatitude(stop.stopLocation), )), iconSize: 1.0, iconColor: stopItem.used ? Colors.grey.value : Colors.orange.value, )); } } } // Create all markers at once if we have any if (annotations.isNotEmpty) { await _pointAnnotationManager!.createMulti(annotations); log('Successfully created ${annotations.length} markers'); } else { log('No annotations to create'); } } catch (e) { log('Error in _updateAllMarkers: $e'); // Don't rethrow - just log the error and continue } } // โœ… ADDED: This is our new, single function for controlling all markers. Future _updateMapState() async { if (!mounted) return; await _updateAllMarkers(); } // โœ… ADDED: This function draws the fixed polyline one time. void _drawFixedPolyline(String encodedPolyline) async { log('_drawFixedPolyline called - encodedPolyline.length: ${encodedPolyline.length}, manager: ${_polylineAnnotationManager != null}'); if (encodedPolyline.isEmpty || _polylineAnnotationManager == null || !mounted) return; try { // await _polylineAnnotationManager!.deleteAll(); // await Future.delayed(const Duration(milliseconds: 100)); // // final points = _rideProvider.decodePolyline(encodedPolyline).mb; var startposition = latlng.LatLng( _rideProvider.getLatitude(_rideProvider.currentride!.startLocation), _rideProvider.getLongitude(_rideProvider.currentride!.startLocation), ); var distinationposition = latlng.LatLng( _rideProvider.getLatitude(_rideProvider.currentride!.endLocation), _rideProvider.getLongitude(_rideProvider.currentride!.endLocation), ); final rd = await _rideProvider.fetchRouteInfoMapbox(startposition, distinationposition); // Add null check for manager (could be disposed during async operation) if (_polylineAnnotationManager == null || !mounted) return; await _polylineAnnotationManager!.deleteAll(); await Future.delayed(const Duration(milliseconds: 100)); // Add small delay final points = rd.points.mb; if (points.isNotEmpty && points.length >= 2) { // Ensure minimum points await _polylineAnnotationManager!.create( PolylineAnnotationOptions( geometry: LineString(coordinates: points), lineColor: AppColors.primaryColor.value, lineWidth: 4.0, ), ); } } catch (e) { log('Error drawing fixed polyline: $e'); } } void _onMapCreated(MapboxMap mapboxMap) async { if (_isDisposing || !mounted) return; _controller = mapboxMap; // Wait for both managers to be created if (_isDisposing || !mounted) return; _pointAnnotationManager = await _controller!.annotations.createPointAnnotationManager(); if (_isDisposing || !mounted) return; _polylineAnnotationManager = await _controller!.annotations.createPolylineAnnotationManager(); // Add a debug log to verify managers are created log('Annotation managers created: point=${_pointAnnotationManager != null}, polyline=${_polylineAnnotationManager != null}'); _mapReady = true; if (mounted && !_isDisposing) { await _updateAllMarkers(); if (taxiPosition != null) { _safeAnimateTo(taxiPosition!); } _checkride(); } } Widget _someoneElseAccepted(RideProvider rideprovider) { bool hasNavigated = false; Future handleNavigation() async { if (hasNavigated) return; hasNavigated = true; handleStatusChange(RideEnum.idle); MainScreen.globalKey.currentState?.navigateToScreen(0); } // Start the 10s timer Future.delayed(const Duration(seconds: 1), () { if (mounted) { handleNavigation(); } }); return Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Txt( text: 'Someone else has already accepted this ride', style: TextStyle( fontSize: 25, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ?AppColors.darkPrimaryColor:AppColors.primaryColor, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ), Image.asset( "assets/images/tolateimage.png", width: 500, ), Txt( text: 'Please try booking another ride or refresh to see available options', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ), const SizedBox(height: 20), CustomButton( borderRadius: 25, height: 55, loadercolor: Colors.white, onClick: handleNavigation, title: 'Find Another Ride', width: MediaQuery.of(context).size.width * 0.75, backgroundColor: Theme.of(context).brightness == Brightness.dark ?AppColors.darkPrimaryColor:AppColors.primaryColor, ), ], ), ), ); } Widget _clientCanceled(RideProvider rideprovider) { bool hasNavigated = false; log("+++++++++++++++++++++++++++++++++++ currentride : ${rideprovider.currentride}"); String status = rideprovider.currentride?.currentridetypesocket ?? "RIDE_CANCEL_USER"; if(status=="RIDE_CANCEL_USER"){ GetIt.I().playSound(SoundEvent.riderCancelled); } log("+++++++++++++++++++++++++++++++++++ status $status"); Future handleNavigation() async { if (hasNavigated) return; hasNavigated = true; handleStatusChange(RideEnum.idle); MainScreen.globalKey.currentState?.navigateToScreen(0); } Future.delayed(const Duration(seconds: 1), () { if (mounted) { handleNavigation(); } }); return Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ status == "RIDE_CANCEL_USER" ? Txt( text: 'The client canceled this ride', style: TextStyle( fontSize: 25, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ?AppColors.darkPrimaryColor:AppColors.primaryColor, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ) : Txt( text: 'This ride cancelled by you', style: TextStyle( fontSize: 25, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ?AppColors.darkPrimaryColor:AppColors.primaryColor, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ), Image.asset( "assets/images/tolateimage.png", width: 500, ), Txt( text: 'Please try booking another ride or refresh to see available options', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.black45, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ), const SizedBox(height: 20), CustomButton( borderRadius: 25, height: 55, loadercolor: Colors.white, onClick: handleNavigation, title: 'Find Another Ride', width: MediaQuery.of(context).size.width * 0.75, backgroundColor: Theme.of(context).brightness == Brightness.dark ?AppColors.darkPrimaryColor:AppColors.primaryColor, ), ], ), ), ); } Widget _rideNotAvailable(RideProvider rideprovider) { bool hasNavigated = false; Future handleNavigation() async { if (hasNavigated) return; hasNavigated = true; handleStatusChange(RideEnum.idle); MainScreen.globalKey.currentState?.navigateToScreen(0); } Future.delayed(const Duration(seconds: 1), () { if (mounted) { handleNavigation(); } }); return Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Txt( text: 'This ride not available any more', style: TextStyle( fontSize: 25, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ?AppColors.secondaryColor:AppColors.primaryColor, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ), Image.asset( "assets/images/tolateimage.png", width: 500, ), Txt( text: 'Please try booking another ride or refresh to see available options', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, // color: Colors.black45, fontFamily: languageSingleton.currentLangCode == Language.ar ? 'Baloo_Bhaijaan_2' : 'Poppins', ), textAlign: TextAlign.center, ), const SizedBox(height: 20), CustomButton( borderRadius: 25, height: 55, loadercolor: Colors.white, onClick: handleNavigation, title: 'Find Another Ride', width: MediaQuery.of(context).size.width * 0.75, backgroundColor: Theme.of(context).brightness == Brightness.dark ?AppColors.darkPrimaryColor:AppColors.primaryColor, ), ], ), ), ); } // -------------------------------------------------------------- MAP & WIDGETS @override Widget build(BuildContext context) { // final socketMgr = context.watch(); // final connected = socketMgr.isConnected; final rideprovider = context.read(); log(' rideprovider.currentride?.driverId : ${rideprovider.currentride?.driverId} , utils.user?.driver.id : ${utils.user?.driver!.id}'); if (rideprovider.currentride != null) { if (rideprovider.currentride?.driverId != null && rideprovider.currentride?.driverId != utils.user?.driver!.id) { return _someoneElseAccepted(rideprovider); } log("new Ride data check 01: ${rideprovider.currentride}"); if (rideprovider.currentride?.rideStatus == "CANCELLED") { return _clientCanceled(rideprovider); } } if (rideprovider.currentride == null) { return _rideNotAvailable(rideprovider); } log("taxiPosition : $taxiPosition ,and distinationposition : $distinationposition"); if (taxiPosition == null || distinationposition == null) { return const Center( child: CustomLoader( logoSize: 60.0, dotSize: 10.0, // Adjust dot color as needed. )); } return Scaffold( body: Stack( children: [ MapWidget( key: const ValueKey("mapWidget"), // NEW CODE - FIXES CRASH cameraOptions: CameraOptions( center: Point( coordinates: Position( taxiPosition?.longitude ?? 10.6346, // Sousse longitude fallback taxiPosition?.latitude ?? 35.8256 // Sousse latitude fallback ) ), zoom: currentZoomLevel, ), styleUri: Theme.of(context).brightness == Brightness.dark ? MapboxStyles.DARK:MapboxStyles.MAPBOX_STREETS, onMapCreated: _onMapCreated, gestureRecognizers: >{ Factory( () => EagerGestureRecognizer(), ), }, ), // Slide Button Section Positioned at the bottom Positioned( bottom: 0, left: 0, right: 0, child: CardTripInfo( clientname: rideprovider.currentride!.ridername, duration: "${(etaSeconds/60).toStringAsFixed(2)} Min", price: rideprovider.currentride!.totalPrice ?? 14, distance: "${distanceKm.toStringAsFixed(2)} Km", rideStatus: context.watch().ridestatus, driverphonenumber: rideprovider.currentride!.riderphonenumber, userimageurl: rideprovider.currentride!.userimageurl, havestops: getNextStopItem() != null, isfinaldist: _isGoTofinalPosition, onCancel: () { showCancelRideBottomSheet(context, rideprovider); }, // onStatusChanged: handleStatusChange, onchangestate: (RideEnum newstatus) async { // โœ… Guard clauses to prevent multiple taps or errors log("start ########################################"); if (_isprocessing) return; if (!mounted) return; // Cancel poll timer for end-of-ride states if (newstatus == RideEnum.rate_client || newstatus == RideEnum.collect_cash) { _ridePollTimer?.cancel(); } setState(() { _isprocessing = true; }); try { // โœ… Main logic block final ride = rideprovider.currentride!; // Safety check: if the ride is no longer ongoing, exit to the main screen. if (ride.rideStatus != "ONGOING" && newstatus != RideEnum.rate_client && newstatus != RideEnum.collect_cash) { AppSnackbar.showInfo("Ride", "This ride is no longer active."); log("ride.rideStatus : ${ride.rideStatus}"); log("newstatus : ${newstatus}"); handleStatusChange(RideEnum.idle); MainScreen.globalKey.currentState?.navigateToScreen(0); return; // Exit the function } bool result = true; // Assume success unless an API call fails // --- Handle DRIVER_ARRIVED --- if (newstatus == RideEnum.driver_arrived) { log('Sending update: DRIVER_ARRIVED'); RideUpdateRequest request = RideUpdateRequest( rideId: ride.id, type: 'DRIVER_ARRIVED', message: "new ride update", riderusername: ride.riderusername, stoplocation: 'NOT_EXIST'); result = await rideprovider.send_update_ride(request); } // --- Handle START_RIDE or STOP --- else if (newstatus == RideEnum.start_ride || newstatus == RideEnum.stop) { log('Sending update: ${newstatus}'); // This handles multi-stop logic. If there are no more stops... if (stopItems.isEmpty || getNextStopItem() == null) { setState(() { _isGoTofinalPosition = true; distinationposition = latlng.LatLng( _rideProvider.getLatitude(_rideProvider.currentride!.endLocation), _rideProvider.getLongitude(_rideProvider.currentride!.endLocation), ); }); // 1. Send the RIDE_START update to the user RideUpdateRequest request = RideUpdateRequest( rideId: ride.id, type: 'RIDE_START', message: "new ride update", riderusername: ride.riderusername, stoplocation: 'NOT_EXIST'); result = await rideprovider.send_update_ride(request); if (!mounted || _isDisposing) return; // 2. Draw the final, fixed A-to-B polyline _drawFixedPolyline(ride.encodedpolyline); // 3. Animate the camera to show the whole route // final startPos = latlng.LatLng(_rideProvider.getLatitude(ride.startLocation), _rideProvider.getLongitude(ride.startLocation)); // final endPos = latlng.LatLng(_rideProvider.getLatitude(ride.endLocation), _rideProvider.getLongitude(ride.endLocation)); // // Create properly ordered LatLngBounds // final bounds = gms.LatLngBounds( // southwest: gms.LatLng(southwestLat, southwestLng), // northeast: gms.LatLng(northeastLat, northeastLng) // ); // final bounds = CoordinateBounds( // southwest: Point( // coordinates: Position(southwestLng, southwestLat), // ), // northeast: Point( // coordinates: Position(northeastLng, northeastLat), // ), // infiniteBounds: false, // ); // // await _controller?.setBounds( // CameraBoundsOptions(bounds: bounds), // ); } else { log('Sending update: ${newstatus}'); // --- Logic for handling intermediate stops --- final nextStop = moveToNextStop(rideprovider); RideUpdateRequest request = RideUpdateRequest( rideId: ride.id, type: 'RIDE_STOP', message: "new ride update", riderusername: ride.riderusername, stoplocation: '${nextStop!.latitude},${nextStop.longitude}' ); result = await rideprovider.send_update_ride(request); // This updates the dynamic route for the next stop await _updateDestination(nextStop); } } // --- Handle RIDE_END --- else if (newstatus == RideEnum.rate_client) { log('Sending update: RIDE_END'); RideUpdateRequest request = RideUpdateRequest( rideId: ride.id, type: 'RIDE_END', message: "new ride update", riderusername: ride.riderusername, stoplocation: 'NOT_EXIST'); result = await rideprovider.send_update_ride(request); } // โœ… Centralized error handling and state update if (!result) { AppSnackbar.showError("Error", "An error occurred. Please try again."); } else { // If the API call was successful, update the local state if (!mounted || _isDisposing) return; await handleStatusChange(newstatus); } } catch (e) { log("Error in onchangestate: $e"); AppSnackbar.showError("Error", "An unexpected error occurred."); } finally { // โœ… This GUARANTEES the loading spinner will be turned off. if (mounted) { setState(() { _isprocessing = false; }); } } }, ), ), // Navigation Button Positioned at the bottom-right (with responsive positioning) Positioned( bottom: _getNavigationButtonBottom(), right: 10, child: GestureDetector( onTap: () => _onNavigationClicked(distinationposition!.latitude, distinationposition!.longitude), child: Container( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), decoration: BoxDecoration( color: AppColors.bluegooglemap2, borderRadius: BorderRadius.circular( 30.0), // Rounded corners for the entire button ), child: Row( mainAxisSize: MainAxisSize.min, // To avoid taking up the full width crossAxisAlignment: CrossAxisAlignment.center, // Align vertically center children: [ Image.asset('assets/images/googlemapimage.png',width: 25,), const SizedBox(width: 8.0), // Add some spacing between the icon and the text const Txt( text: "Google Map", style: TextStyle( color: AppColors.white, fontSize: 20, fontFamily: 'Poppins', fontWeight: FontWeight.bold, ), ), ], ), ), ), ), if((context.watch().ridestatus==RideEnum.start_ride || context.watch().ridestatus==RideEnum.stop) && rideprovider.currentride?.currentridetypesocket == "RIDE_START") Positioned( top: _getTopContainerPosition()+100, left: 0, right: 0, child:WanisKidsVideoCall(ride: rideprovider.currentride!, isDriver: true) ), // Top container with responsive positioning Positioned( top: _getTopContainerPosition(), left: 0, right: 0, child: Column( children: [ Padding( padding: EdgeInsets.only(left: 22.sp, right: 33.sp), child: Container( decoration: BoxDecoration( color:Theme.of(context).brightness == Brightness.dark?AppColors.blackBackgroundColor:AppColors.whiteBackgroundColor, borderRadius: BorderRadius.circular(2), border: Border.all( color: Theme.of(context).brightness == Brightness.dark?AppColors.blackBackgroundColor:AppColors.whiteBackgroundColor, width: 1.0, ), ), child: Row( children: [ Padding( padding: EdgeInsets.only( left: 36.sp, bottom: 16.sp, top: 16.sp, right: 13.sp), child: SvgPicture.asset( 'assets/icons/currentlocation_map.svg', width: 15, ), ), Flexible( child: Text( rideprovider.currentride!.startLocationName, style: BeemTextStyles.poppinsRegular14, ), ), ], ), ), ), Padding( padding: EdgeInsets.only(left: 22.sp, right: 33.sp), child: Container( decoration: BoxDecoration( color: Theme.of(context).brightness == Brightness.dark?AppColors.blackBackgroundColor:AppColors.whiteBackgroundColor, borderRadius: BorderRadius.circular(2), border: Border.all( color: Theme.of(context).brightness == Brightness.dark?AppColors.blackBackgroundColor:AppColors.whiteBackgroundColor, width: 1.0, ), ), child: Row( children: [ Padding( padding: EdgeInsets.only( left: 36.sp, bottom: 16.sp, top: 16.sp, right: 13.sp), child: SvgPicture.asset( 'assets/icons/ic_pin.svg', ), ), Flexible( child: Text( rideprovider.currentride!.endLocationName, style: BeemTextStyles.poppinsRegular14, ), ), ], ), ), ), ], )) // Positioned( // top: 0, // left: 0, // right: 0, // child: ConnectionBanner(connected: connected), // ), ], ), ); } void showCancelRideBottomSheet( BuildContext context, RideProvider rideProvider) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, // Transparent background builder: (context) { return CancelRideDialog( onCancelConfirmed: (reason) async { if (_isprocessing) { return; } if (!mounted) return; setState(() { _isprocessing = true; }); log("Ride canceled for reason: $reason"); RideUpdateRequest request = RideUpdateRequest( rideId: rideProvider.currentride!.id, type: 'RIDE_CANCEL_DRIVER', message: "new ride update", riderusername: rideProvider.currentride!.riderusername, stoplocation: 'NOT_EXIST'); bool result = await rideProvider.send_update_ride(request); handleStatusChange(RideEnum.idle); // _pubnubManager.sendRideUpdate('RIDE_START','NOT_EXIST', rideId: rideprovider.currentride!.id, riderusername: rideprovider.currentride!.riderusername); if (!result) { AppSnackbar.showError( "Error", "Error occurred please try again later"); if (!mounted) return; setState(() { _isprocessing = false; }); return; } }, onCancelDismissed: () { print("User chose to wait for driver"); }, ); }, ); } }