Skip to content

[Bug]: setDestinations error #373

@ahmedibrahim240

Description

@ahmedibrahim240

Is there an existing issue for this?

  • I have searched the existing issues

Description of the bug

Always the case cancellation after that get networkerror

Flutter version

3.29.3

Package version

^0.6.1

Native SDK versions

  • I haven't changed the version of the native SDKs

Flutter Doctor Output

[√] Flutter (Channel stable, 3.29.3, on Microsoft Windows [Version 10.0.26100.4061], locale en-US) [338ms]
• Flutter version 3.29.3 on channel stable at C:\src\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision ea121f8859 (7 weeks ago), 2025-04-11 19:10:07 +0000
• Engine revision cf56914b32
• Dart version 3.7.2
• DevTools version 2.42.3

[√] Windows Version (Windows 11 or higher, 24H2, 2009) [2.0s]

[√] Android toolchain - develop for Android devices (Android SDK version 35.0.1) [2.3s]
• Android SDK at C:\Users\Kahraba\AppData\Local\Android\Sdk
• Platform android-35, build-tools 35.0.1
• ANDROID_HOME = C:\Users\Kahraba\AppData\Local\Android\Sdk
• Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: flutter config --jdk-dir="path/to/jdk".
• Java version OpenJDK Runtime Environment (build 21.0.5+-13047016-b750.29)
• All Android licenses accepted.

[√] Chrome - develop for the web [26ms]
• Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[X] Visual Studio - develop Windows apps [25ms]
X Visual Studio not installed; this is necessary to develop Windows apps.
Download at https://visualstudio.microsoft.com/downloads/.
Please install the "Desktop development with C++" workload, including all of its default components

[√] Android Studio (version 2024.3) [23ms]
• Android Studio at C:\Program Files\Android\Android Studio
• Flutter plugin can be installed from:
https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 21.0.5+-13047016-b750.29)

[√] VS Code (version 1.100.2) [21ms]
• VS Code at C:\Users\Kahraba\AppData\Local\Programs\Microsoft VS Code
• Flutter extension version 3.110.0

[√] Connected device (4 available) [337ms]
• RMX3710 (mobile) • O76HEYQKHEQ85XTC • android-arm64 • Android 14 (API 34)
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.26100.4061]
• Chrome (web) • chrome • web-javascript • Google Chrome 137.0.7151.55
• Edge (web) • edge • web-javascript • Microsoft Edge 136.0.3240.50

[√] Network resources [986ms]
• All expected network resources are available.

! Doctor found issues in 1 category.

Steps to reproduce

Just try to create a path.

Expected vs Actual Behavior

i can't set Destinations

Code Sample

`import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmaps;
import 'package:google_navigation_flutter/google_navigation_flutter.dart';
import 'package:tayaar/core/helper/extensions/general_extension.dart';
import 'package:tayaar/core/services/navigation/navigation_service.dart';
import 'package:tayaar/core/services/navigation/routes_api_helper.dart';
import 'package:tayaar/core/theme/app_colors.dart';
import 'package:tayaar/core/theme/app_spacing.dart';
import 'package:tayaar/core/widget/app_back_button.dart';
import 'package:tayaar/core/widget/app_button.dart';

class NavigationPage extends StatefulWidget {
  final gmaps.LatLng destination;
  final String? destinationName;

  const NavigationPage({super.key, required this.destination, this.destinationName});

  @override
  State<NavigationPage> createState() => _NavigationPageState();
}

class _NavigationPageState extends State<NavigationPage> {
  bool _isNavigating = false;
  StreamSubscription<NavigationStatus>? _navigationStatusSubscription;
  NavigationStatus? _navigationStatus;
  bool _isInitializing = true;
  String? _errorMessage;
  bool _navigationSessionInitialized = false;
  final NavigationTravelMode _travelMode = NavigationTravelMode.driving;

  // Controller for Google Maps Navigation
  GoogleNavigationViewController? _navigationViewController; // Used in _buildMainContent

  // Stream subscriptions for navigation events
  StreamSubscription? _remainingTimeOrDistanceChangedSubscription;
  StreamSubscription? _arrivalSubscription;
  StreamSubscription? _roadSnappedLocationUpdatedSubscription;
  StreamSubscription? _reroutingSubscription;
  List<NavigationWaypoint> _waypoints = <NavigationWaypoint>[];

  // Navigation state
  int _remainingDistance = 0; // in meters
  int _remainingTime = 0; // in seconds
  double _currentSpeed = 0; // in m/s
  // User location - used for map positioning during navigation
  LatLng? _userLocation;

  @override
  void initState() {
    super.initState();
    _initNavigation();
    _initializeNavigationSession().then((_) {
      // Start navigation automatically when the screen opens
      if (mounted && _navigationSessionInitialized) {
        _startNavigation();
      }
    });
  }

  Future<void> _initNavigation() async {
    try {
      bool isInitialized = await GoogleMapsNavigator.isInitialized();
      logError('Navigation SDK initialized: $isInitialized');

      if (!isInitialized) {
        logError('❌ Navigation SDK not initialized');
        // قد تحتاج إعداد MapView أولاً
      }
      logError('Navigation SDK initialized: $isInitialized');
      setState(() {
        // Initialize with some default values
        _navigationStatus = NavigationStatus(
          distance: 5000, // 5 km
          duration: const Duration(minutes: 15),
          speed: 45, // 45 km/h
        );
        _remainingDistance = 5000;
        _remainingTime = 900; // 15 minutes in seconds
        _currentSpeed = 12.5; // 45 km/h in m/s
      });
      // GoogleMapsNavigator.en;

      // Don't start navigation automatically, let user press the button
    } catch (e) {
      setState(() {
        _errorMessage = 'Failed to initialize navigation: $e';
      });
      logError('Navigation initialization error: $e');
    }
  }

  Future<void> _initializeNavigationSession() async {
    try {
      // Check if terms are accepted
      if (!await GoogleMapsNavigator.areTermsAccepted()) {
        await GoogleMapsNavigator.showTermsAndConditionsDialog(
          'Tayaar Navigation',
          'Tayaar',
        );
      }

      // Add a small delay before initialization to ensure resources are ready
      await Future.delayed(const Duration(milliseconds: 300));

      // Initialize navigation session with explicit options
      await GoogleMapsNavigator.initializeNavigationSession(
        taskRemovedBehavior: TaskRemovedBehavior.continueService,
      );

      // Log initialization status
      bool isInitialized = await GoogleMapsNavigator.isInitialized();
      logError('Navigation SDK initialized after attempt: $isInitialized');

      // Retry initialization if not successful
      if (!isInitialized) {
        logError('Retrying navigation session initialization...');
        await Future.delayed(const Duration(milliseconds: 500));
        await GoogleMapsNavigator.initializeNavigationSession(
          taskRemovedBehavior: TaskRemovedBehavior.continueService,
        );
        isInitialized = await GoogleMapsNavigator.isInitialized();
        logError('Navigation SDK initialized after retry: $isInitialized');
      }

      // Set up listeners for navigation events
      await _setupNavigationListeners();

      setState(() {
        _navigationSessionInitialized = true;
        _isInitializing = false;
      });
    } catch (e) {
      setState(() {
        _isInitializing = false;
        _errorMessage = 'Failed to initialize navigation session: $e';
      });
      logError('Navigation session initialization error: $e');
    }
  }

  Future<void> _setupNavigationListeners() async {
    // Clear any existing listeners
    _clearNavigationListeners();

    // Set up remaining time and distance listener
    _remainingTimeOrDistanceChangedSubscription =
        GoogleMapsNavigator.setOnRemainingTimeOrDistanceChangedListener((event) {
          if (mounted) {
            setState(() {
              _remainingDistance = event.remainingDistance.toInt();
              _remainingTime = event.remainingTime.toInt();

              // Update navigation status for the overlay
              _navigationStatus = NavigationStatus(
                distance: _remainingDistance.toDouble(),
                duration: Duration(seconds: _remainingTime),
                speed: _currentSpeed,
              );
            });
          }
        });

    // Set up arrival listener
    _arrivalSubscription = GoogleMapsNavigator.setOnArrivalListener((event) {
      logError('Arrived at destination');
      _stopNavigation();
    });

    // Set up road snapped location listener to get current location
    GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener((event) {
      if (mounted) {
        setState(() {
          _userLocation = event.location;
        });
      }
    }).then((subscription) {
      _roadSnappedLocationUpdatedSubscription = subscription;
    });

    // Set up rerouting listener
    _reroutingSubscription = GoogleMapsNavigator.setOnReroutingListener(() {
      if (mounted) {
        logError('Rerouting...');
      }
    });
  }

  void _clearNavigationListeners() {
    _remainingTimeOrDistanceChangedSubscription?.cancel();
    _remainingTimeOrDistanceChangedSubscription = null;

    _arrivalSubscription?.cancel();
    _arrivalSubscription = null;

    _roadSnappedLocationUpdatedSubscription?.cancel();
    _roadSnappedLocationUpdatedSubscription = null;

    _reroutingSubscription?.cancel();
    _reroutingSubscription = null;
  }

  // Flag to track if navigation is in progress to prevent multiple simultaneous requests
  bool _isNavigationInProgress = false;

  Future<void> _startNavigation() async {
    // Prevent multiple simultaneous navigation requests
    if (_isNavigationInProgress) {
      logError('Navigation already in progress, ignoring new request');
      return;
    }
    
    try {
      _isNavigationInProgress = true;
      
      if (!_navigationSessionInitialized) {
        setState(() {
          _errorMessage = 'Navigation session not initialized';
          _isNavigationInProgress = false;
        });
        return;
      }

      setState(() {
        _isNavigating = true;
        _errorMessage = null;
      });

      // Create waypoint for destination
      final waypoint = NavigationWaypoint.withLatLngTarget(
        title: widget.destinationName ?? 'Destination',
        target: LatLng(
          latitude: widget.destination.latitude,
          longitude: widget.destination.longitude,
        ),
      );

      // Get current location for origin waypoint (if available)
      final originWaypoint = NavigationWaypoint.withLatLngTarget(
        title: 'Origin',

        target: LatLng(
          latitude: _userLocation?.latitude ?? widget.destination.latitude - 0.01,
          longitude: _userLocation?.longitude ?? widget.destination.longitude - 0.01,
        ),
      );

      // Create list of waypoints with origin and destination
      _waypoints = [originWaypoint, waypoint];

      try {
        // Create destinations object with waypoints
        Destinations? destinations;
        
        // Wait a moment to ensure any previous navigation requests are fully completed
        await Future.delayed(const Duration(milliseconds: 500));
        
        // First try with just the destination waypoint (simpler route calculation)
        final destinationWaypoint = NavigationWaypoint.withLatLngTarget(
          title: widget.destinationName ?? 'Destination',
          target: LatLng(
            latitude: widget.destination.latitude,
            longitude: widget.destination.longitude,
          ),
        );
        
        final simpleDestinations = Destinations(
          waypoints: [destinationWaypoint],
          routingOptions: RoutingOptions(travelMode: NavigationTravelMode.driving),
          displayOptions: NavigationDisplayOptions(showDestinationMarkers: true),
        );
        
        logError("Trying simple destination first");
        late NavigationRouteStatus routeStatus;
        
        // Try setting destinations with just the destination waypoint
        routeStatus = await GoogleMapsNavigator.setDestinations(
          simpleDestinations,
        ).timeout(const Duration(seconds: 30));
        
        // If simple destination fails, try with route API
        if (routeStatus != NavigationRouteStatus.statusOk) {
          logError("Simple destination failed with status: $routeStatus, trying with Routes API");
          destinations = await _buildDestinationsWithRoutesApi();
          destinations ??= _buildDestinations();
          logError("_buildDestinationsWithRoutesApi:$destinations");
          
          // Wait a moment before trying again to avoid cancellation
          await Future.delayed(const Duration(milliseconds: 300));
          
          routeStatus = await GoogleMapsNavigator.setDestinations(
            destinations!,
          ).timeout(const Duration(seconds: 30));
        }
        logError("NavigationRouteStatus:$routeStatus");
        // Check if route was created successfully

        if (routeStatus == NavigationRouteStatus.statusOk) {
          logError('Navigation route created successfully');
          await _startGuidanceAndListeners();
        } else {
          // If direct navigation fails, try with just the destination waypoint
          logError(
            'Navigation route creation failed with both waypoints. Trying with just destination...',
          );

          final destinationsSimple = Destinations(
            waypoints: [waypoint],
            routingOptions: RoutingOptions(travelMode: NavigationTravelMode.driving),
            displayOptions: NavigationDisplayOptions(),
          );

          final retryStatus = await GoogleMapsNavigator.setDestinations(
            destinationsSimple,
          );

          if (retryStatus == NavigationRouteStatus.statusOk) {
            logError('Navigation route created successfully with destination only');
            await _startGuidanceAndListeners();
          } else {
            setState(() {
              _isNavigating = false;
              _errorMessage = 'Failed to create navigation route: $retryStatus';
            });
            logError('Navigation route creation failed: $retryStatus');
          }
        }
      } catch (routeError) {
        setState(() {
          _isNavigating = false;
          _errorMessage = 'Route creation error: $routeError';
        });
        logError('Route creation error: $routeError');
      }
    } catch (e) {
      setState(() {
        _isNavigating = false;
        _errorMessage = 'Error starting navigation: $e';
      });
      logError('Navigation start error: $e');
    }
  }

  Destinations? _buildDestinations() {
    // Show delayed calculating route message.

    return Destinations(
      waypoints: _waypoints,
      displayOptions: NavigationDisplayOptions(
        showDestinationMarkers: false,
        showStopSigns: true,
        showTrafficLights: true,
      ),
      routingOptions: RoutingOptions(travelMode: _travelMode),
    );
  }

  Future<Destinations?> _buildDestinationsWithRoutesApi() async {
    logError('Using route token from Routes API.');

    List<String> routeTokens = <String>[];
    try {
      routeTokens = await getRouteToken(_waypoints);
      logError("routeTokens:$routeTokens");
    } catch (e) {
      logError('Failed to get route tokens from Routes API. $e');
      return null;
    }

    if (routeTokens.isEmpty) {
      logError('Failed to get route tokens from Routes API.');
      return null;
    } else if (routeTokens.length > 1) {
      logError(
        'More than one route token received from Routes API. Using the first one.',
      );
    }

    return Destinations(
      waypoints: _waypoints,
      displayOptions: NavigationDisplayOptions(showDestinationMarkers: true),
      // routingOptions: RoutingOptions(),
      routeTokenOptions: RouteTokenOptions(
        routeToken: routeTokens.first, // Uses first fetched route token.
        travelMode: NavigationTravelMode.driving,
      ),
    );
  }

  Future<void> _startGuidanceAndListeners() async {
    // Start guidance
    await GoogleMapsNavigator.startGuidance();

    // Start listening to navigation status updates from our service as well
    _navigationStatusSubscription?.cancel();
    _navigationStatusSubscription = NavigationService.getNavigationStatusStream(
      widget.destination,
    ).listen((status) {
      if (mounted) {
        setState(() {
          _navigationStatus = status;
          _currentSpeed = status.speed;
        });
      }
    });
  }

  Future<void> _stopNavigation() async {
    try {
      // Cancel navigation status subscription
      _navigationStatusSubscription?.cancel();
      _navigationStatusSubscription = null;

      // Clear navigation listeners
      _clearNavigationListeners();

      // Stop navigation using GoogleMapsNavigator
      try {
        await GoogleMapsNavigator.stopGuidance();
        logError('Navigation stopped successfully');
      } catch (guidanceError) {
        logError('Error stopping guidance: $guidanceError');
      }

      // Only update UI if the widget is still mounted
      if (mounted) {
        setState(() {
          _isNavigating = false;
        });
        logError('Navigation stopped');
      }
    } catch (e) {
      logError('Error stopping navigation: $e');
      // Even if there's an error, we should update the UI state if mounted
      if (mounted) {
        setState(() {
          _isNavigating = false;
        });
      }
    }
  }

  @override
  void dispose() {
    // Cancel all subscriptions
    _navigationStatusSubscription?.cancel();
    _clearNavigationListeners();

    // Check if navigation is still active and stop it
    if (_navigationSessionInitialized) {
      try {
        // Just stop guidance without trying to update UI or use the controller
        GoogleMapsNavigator.stopGuidance().catchError((error) {
          logError('Error stopping guidance during dispose: $error');
        });
      } catch (e) {
        logError('Error during dispose: $e');
      }
    }

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Theme(
      // Apply AppCompat theme for Google Maps Navigation components
      data: Theme.of(context).copyWith(
        // Ensure we have AppCompat compatible colors
        primaryColor: Colors.blue,
        colorScheme: ColorScheme.fromSwatch().copyWith(
          primary: Colors.blue,
          secondary: Colors.blueAccent,
        ),
        appBarTheme: const AppBarTheme(
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
        ),
      ),
      child: Scaffold(
        body: Stack(
          children: [
            // Main content
            _buildMainContent(),

            // Navigation info overlay
            if (_isNavigating && _navigationStatus != null) _buildNavigationInfoOverlay(),

            // Top bar with back button
            _buildTopBar(),

            // Navigation info at the bottom (without start button)
            if (_isNavigating) _buildNavigationInfo(),
          ],
        ),
      ),
    );
  }

  Widget _buildMainContent() {
    if (_isInitializing) {
      return const Center(child: CircularProgressIndicator());
    }

    if (_errorMessage != null) {
      return Center(
        child: Padding(
          padding: EdgeInsets.all(AppSpacing.horizontalSpacing),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                _errorMessage!,
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 16.sp, color: Colors.red),
              ),
              SizedBox(height: 20.h),
              AppButton(text: 'Try Again', onPressed: _initNavigation),
            ],
          ),
        ),
      );
    }

    if (!_isNavigating) {
      return Center(
        child: Padding(
          padding: EdgeInsets.all(AppSpacing.horizontalSpacing),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Preparing navigation to ${widget.destinationName ?? "destination"}...',
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600),
              ),
              SizedBox(height: 20.h),
              const CircularProgressIndicator(),
            ],
          ),
        ),
      );
    }

    // Display Google Maps Navigation view within the app
    return GoogleMapsNavigationView(
      onViewCreated: (GoogleNavigationViewController controller) {
        // Store the controller for later use
        _navigationViewController = controller;

        // Only proceed with setup if the widget is still mounted
        if (!mounted) return;

        try {
          // Enable my location
          controller.setMyLocationEnabled(true);

          // If we have a user location, move camera to it
          if (_userLocation != null) {
            controller.moveCamera(
              CameraUpdate.newLatLngZoom(
                LatLng(
                  latitude: _userLocation!.latitude,
                  longitude: _userLocation!.longitude,
                ),
                15.0,
              ),
            );
          }

          // Set navigation UI preferences - wrap in try/catch to handle potential errors
          try {
            controller.setNavigationUIEnabled(true);
            controller.setNavigationHeaderEnabled(true);
            controller.setNavigationFooterEnabled(true);
            controller.setNavigationTripProgressBarEnabled(true);
            controller.setRecenterButtonEnabled(true);
            controller.setSpeedLimitIconEnabled(true);
          } catch (uiError) {
            logError('Error setting UI preferences: $uiError');
          }

          // If already navigating, start guidance
          if (_isNavigating) {
            controller.followMyLocation(CameraPerspective.tilted);
          }

          // Create waypoint for destination using the correct LatLng constructor
          final latLng = LatLng(
            latitude: widget.destination.latitude,
            longitude: widget.destination.longitude,
          );

          final waypoint = NavigationWaypoint.withLatLngTarget(
            title: widget.destinationName ?? 'Destination',
            target: latLng,
          );

          // Create destinations object with waypoints
          final destinations = Destinations(
            waypoints: [waypoint],
            routingOptions: RoutingOptions(travelMode: NavigationTravelMode.driving),
            displayOptions: NavigationDisplayOptions(),
          );

          // Start navigation using GoogleMapsNavigator
          GoogleMapsNavigator.setDestinations(destinations)
              .then((routeStatus) {
                // Check if widget is still mounted before updating UI
                if (!mounted) return;

                // Check for successful route creation - using the correct enum value
                // The exact value depends on the package version, but common values are SUCCESS, OK, or 0
                if (routeStatus == 0) {
                  // Using numeric value for compatibility
                  logError('Navigation route created successfully');

                  // Set up listeners for navigation events
                  _remainingTimeOrDistanceChangedSubscription =
                      GoogleMapsNavigator.setOnRemainingTimeOrDistanceChangedListener((
                        event,
                      ) {
                        if (mounted && _navigationStatus != null) {
                          // Update with the correct property names from the event
                          setState(() {
                            _remainingDistance = event.remainingDistance.toInt();
                            _remainingTime = event.remainingTime.toInt();

                            _navigationStatus = NavigationStatus(
                              distance: event.remainingDistance.toDouble(),
                              duration: Duration(seconds: event.remainingTime.toInt()),
                              speed: _navigationStatus!.speed, // Keep current speed
                            );
                          });
                        }
                      });

                  // Listen for arrival at destination
                  _arrivalSubscription = GoogleMapsNavigator.setOnArrivalListener((
                    event,
                  ) {
                    logError('Arrived at destination');
                    if (mounted) {
                      _stopNavigation();
                    }
                  });
                } else {
                  if (mounted) {
                    setState(() {
                      _isNavigating = false;
                      _errorMessage = 'Failed to create navigation route: $routeStatus';
                    });
                  }
                  logError('Navigation route creation failed: $routeStatus');
                }
              })
              .catchError((error) {
                if (mounted) {
                  setState(() {
                    _isNavigating = false;
                    _errorMessage = 'Navigation error: $error';
                  });
                }
                logError('Navigation error: $error');
              });
        } catch (setupError) {
          logError('Error during navigation view setup: $setupError');
          if (mounted) {
            setState(() {
              _errorMessage = 'Navigation setup error: $setupError';
            });
          }
        }
      },
      // Disable the default UI
      initialNavigationUIEnabledPreference: NavigationUIEnabledPreference.disabled,
    );
  }

  Widget _buildNavigationInfoOverlay() {
    final status = _navigationStatus!;
    final distanceInKm = (status.distance / 1000).toStringAsFixed(1);
    final minutes = status.duration.inMinutes;
    final speedInKmh = (status.speed * 3.6).toStringAsFixed(1); // Convert m/s to km/h

    return Positioned(
      top: 100.h,
      left: 16.w,
      right: 16.w,
      child: Container(
        padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
        decoration: BoxDecoration(
          color: AppColors.primary500,
          borderRadius: BorderRadius.circular(8.r),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.2),
              blurRadius: 8,
              offset: const Offset(0, 2),
            ),
          ],
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.navigation, color: Colors.white, size: 20.sp),
                SizedBox(width: 8.w),
                Text(
                  'Stay on',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 16.sp,
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ],
            ),
            SizedBox(height: 8.h),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Row(
                  children: [
                    Icon(Icons.directions_car, color: Colors.white, size: 16.sp),
                    SizedBox(width: 4.w),
                    Text(
                      '$distanceInKm km',
                      style: TextStyle(color: Colors.white, fontSize: 14.sp),
                    ),
                  ],
                ),
                Row(
                  children: [
                    Icon(Icons.access_time, color: Colors.white, size: 16.sp),
                    SizedBox(width: 4.w),
                    Text(
                      '$minutes min',
                      style: TextStyle(color: Colors.white, fontSize: 14.sp),
                    ),
                  ],
                ),
                Row(
                  children: [
                    Icon(Icons.speed, color: Colors.white, size: 16.sp),
                    SizedBox(width: 4.w),
                    Text(
                      '$speedInKmh km/h',
                      style: TextStyle(color: Colors.white, fontSize: 14.sp),
                    ),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildNavigationInfo() {
    return Positioned(
      bottom: 20.h,
      left: 16.w,
      right: 16.w,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          // Close navigation button
          Container(
            decoration: BoxDecoration(
              color: Colors.white,
              shape: BoxShape.circle,
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.2),
                  blurRadius: 6,
                  offset: const Offset(0, 2),
                ),
              ],
            ),
            child: IconButton(
              icon: Icon(Icons.close, size: 24.sp),
              onPressed: () {
                _stopNavigation();
                Navigator.pop(context);
              },
            ),
          ),

          // Time and distance container
          Container(
            padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(20.r),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.2),
                  blurRadius: 6,
                  offset: const Offset(0, 2),
                ),
              ],
            ),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.eco, color: Colors.green, size: 18.sp),
                SizedBox(width: 4.w),
                Text(
                  '${(_remainingTime / 60).ceil()} min',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.sp),
                ),
                Text(' • ', style: TextStyle(fontSize: 16.sp)),
                Text(
                  '${(_remainingDistance / 1000).toStringAsFixed(1)} km',
                  style: TextStyle(fontSize: 16.sp),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildTopBar() {
    return Positioned(
      top: 40.h,
      left: 16.w,
      right: 16.w,
      child: Row(
        children: [
          AppBackButton(
            onTap: () {
              _stopNavigation();
              Navigator.pop(context);
            },
          ),
          SizedBox(width: 16.w),
          Expanded(
            child: Text(
              widget.destinationName ?? 'Navigation',
              style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600),
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ],
      ),
    );
  }
}
`

Additional Context

No response

Metadata

Metadata

Assignees

Labels

status: investigatingThe issue is under investigation, which is determined to be non-trivial.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions