diff --git a/lib/components/dark_mode_map.dart b/lib/components/dark_mode_map.dart index ac58144..a4384a9 100644 --- a/lib/components/dark_mode_map.dart +++ b/lib/components/dark_mode_map.dart @@ -1,3 +1,5 @@ +import 'dart:io' show Platform; +import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:google_navigation_flutter/google_navigation_flutter.dart'; import '../models/delivery.dart'; @@ -31,6 +33,7 @@ class _DarkModeMapComponentState extends State { bool _isStartingNavigation = false; String _loadingMessage = 'Initializing...'; Brightness? _lastBrightness; + bool _isMapViewReady = false; @override void initState() { @@ -242,6 +245,23 @@ class _DarkModeMapComponentState extends State { debugPrint('Navigation started successfully'); + // On iOS Simulator in debug mode, start simulation to provide location updates + // The iOS Simulator doesn't provide continuous location updates for custom locations, + // so we use the SDK's built-in simulation to simulate driving along the route. + // This is only needed for testing on iOS Simulator - real devices work without this. + if (kDebugMode && Platform.isIOS) { + try { + // Start simulating the route with a speed multiplier for testing + // speedMultiplier: 1.0 = normal speed, 5.0 = 5x faster for quicker testing + await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRouteWithOptions( + SimulationOptions(speedMultiplier: 5.0), + ); + debugPrint('Simulation started for iOS Simulator testing'); + } catch (e) { + debugPrint('Could not start simulation: $e'); + } + } + // Reapply dark mode style after navigation starts if (mounted) { await _applyDarkModeStyle(); @@ -279,6 +299,17 @@ class _DarkModeMapComponentState extends State { Future _stopNavigation() async { try { + // Stop simulation if it was running (iOS Simulator) + if (kDebugMode && Platform.isIOS) { + try { + // Remove simulated user location to stop the simulation + await GoogleMapsNavigator.simulator.removeUserLocation(); + debugPrint('Simulation stopped'); + } catch (e) { + debugPrint('Could not stop simulation: $e'); + } + } + await GoogleMapsNavigator.stopGuidance(); await GoogleMapsNavigator.clearDestinations(); if (mounted) { @@ -312,7 +343,7 @@ class _DarkModeMapComponentState extends State { // Calculate dynamic padding for bottom button bar final topPadding = 0.0; - final bottomPadding = 110.0; + final bottomPadding = 60.0; return Stack( children: [ @@ -323,20 +354,40 @@ class _DarkModeMapComponentState extends State { bottom: bottomPadding, ), child: GoogleMapsNavigationView( + // Enable navigation UI automatically when guidance starts + // This is critical for iOS to display turn-by-turn directions, ETA, distance + initialNavigationUIEnabledPreference: NavigationUIEnabledPreference.automatic, onViewCreated: (controller) async { _navigationController = controller; - // Apply dark mode style with a small delay to ensure map is ready - await Future.delayed(const Duration(milliseconds: 500)); + + // Wait longer for the map to be fully initialized on Android + // This helps prevent crashes when the view is disposed during initialization + await Future.delayed(const Duration(milliseconds: 1000)); // Safety check: ensure widget is still mounted before proceeding if (!mounted) return; + // Mark map as ready only after the delay + _isMapViewReady = true; + + // Enable navigation UI elements (header with turn directions, footer with ETA/distance) + // This is required for iOS to show trip info, duration, and ETA + try { + await controller.setNavigationUIEnabled(true); + await controller.setNavigationHeaderEnabled(true); + await controller.setNavigationFooterEnabled(true); + await controller.setNavigationTripProgressBarEnabled(true); + debugPrint('Navigation UI elements enabled'); + } catch (e) { + debugPrint('Error enabling navigation UI: $e'); + } + await _applyDarkModeStyle(); // Wrap camera animation in try-catch to handle "No valid view found" errors // This can happen on Android when the view isn't fully ready try { - if (mounted && _navigationController != null) { + if (mounted && _navigationController != null && _isMapViewReady) { await controller.animateCamera( CameraUpdate.newLatLngZoom(initialPosition, 12), ); @@ -345,7 +396,7 @@ class _DarkModeMapComponentState extends State { debugPrint('Camera animation error (view may not be ready): $e'); // Retry once after a longer delay await Future.delayed(const Duration(milliseconds: 1000)); - if (mounted && _navigationController != null) { + if (mounted && _navigationController != null && _isMapViewReady) { try { await controller.animateCamera( CameraUpdate.newLatLngZoom(initialPosition, 12), @@ -379,8 +430,8 @@ class _DarkModeMapComponentState extends State { ], ), padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, + horizontal: 12, + vertical: 8, ), child: Row( children: [ @@ -518,14 +569,14 @@ class _DarkModeMapComponentState extends State { return Material( color: backgroundColor, - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.circular(6), child: InkWell( onTap: onPressed, - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.circular(6), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 14, - vertical: 14, + horizontal: 8, + vertical: 10, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -534,15 +585,15 @@ class _DarkModeMapComponentState extends State { Icon( icon, color: textColor, - size: 24, + size: 18, ), - const SizedBox(width: 8), + const SizedBox(width: 6), Text( label, style: TextStyle( color: textColor, fontWeight: FontWeight.w600, - fontSize: 18, + fontSize: 14, ), ), ],