-
Notifications
You must be signed in to change notification settings - Fork 32
Description
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