import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_navigation_flutter/google_navigation_flutter.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../models/delivery.dart'; import '../services/location_permission_service.dart'; import '../components/navigation_tc_dialog.dart'; class NavigationPage extends ConsumerStatefulWidget { final Delivery delivery; final double destinationLatitude; final double destinationLongitude; final VoidCallback? onNavigationComplete; final VoidCallback? onNavigationCancelled; const NavigationPage({ super.key, required this.delivery, required this.destinationLatitude, required this.destinationLongitude, this.onNavigationComplete, this.onNavigationCancelled, }); @override ConsumerState createState() => _NavigationPageState(); } class _NavigationPageState extends ConsumerState { late GoogleMapsNavigationViewController _navigationViewController; late LocationPermissionService _permissionService; bool _isNavigationInitialized = false; bool _hasLocationPermission = false; @override void initState() { super.initState(); _permissionService = LocationPermissionService(); _initializeNavigation(); } Future _initializeNavigation() async { try { final hasPermission = await _permissionService.hasLocationPermission(); if (!hasPermission) { if (mounted) { _showPermissionDialog(); } return; } setState(() { _hasLocationPermission = true; }); if (mounted) { _initializeNavigationSession(); } } catch (e) { if (mounted) { _showErrorDialog('Initialization error: ${e.toString()}'); } } } Future _initializeNavigationSession() async { try { await GoogleMapsNavigationViewController.initializeNavigationSession(); if (mounted) { setState(() { _isNavigationInitialized = true; }); // Set destination after session is initialized await _setDestination(); } } catch (e) { if (mounted) { _showErrorDialog('Failed to initialize navigation: ${e.toString()}'); } } } Future _setDestination() async { try { final destination = NavigationDisplayOptions( showDestinationMarkers: true, ); final waypoint = Waypoint( title: widget.delivery.name, target: LatLng( latitude: widget.destinationLatitude, longitude: widget.destinationLongitude, ), ); await _navigationViewController.setDestinations( destinations: [waypoint], displayOptions: destination, ); // Listen for location updates _navigationViewController.addOnLocationUpdatedListener((location) { debugPrint( 'Location updated: ${location.latitude}, ${location.longitude}', ); }); // Listen for navigation events _navigationViewController.addOnNavigationUIEnabledListener((isEnabled) { debugPrint('Navigation UI enabled: $isEnabled'); }); // Listen for waypoint reached _navigationViewController.addOnArrivalListener((arrival) { _handleArrival(arrival); }); } catch (e) { if (mounted) { _showErrorDialog('Failed to set destination: ${e.toString()}'); } } } void _handleArrival(NavInfo navInfo) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( AppLocalizations.of(context)?.navigationArrived ?? 'You have arrived at the destination', ), duration: const Duration(seconds: 3), ), ); // Call completion callback widget.onNavigationComplete?.call(); } } void _showPermissionDialog() { final l10n = AppLocalizations.of(context); showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: Text(l10n?.locationPermissionRequired ?? 'Location Permission'), content: Text( l10n?.locationPermissionMessage ?? 'This app requires location permission to navigate to deliveries.', ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); widget.onNavigationCancelled?.call(); }, child: Text(l10n?.cancel ?? 'Cancel'), ), TextButton( onPressed: () { Navigator.of(context).pop(); _requestLocationPermission(); }, child: Text(l10n?.requestPermission ?? 'Request Permission'), ), ], ), ); } Future _requestLocationPermission() async { final result = await _permissionService.requestLocationPermission(); if (!mounted) return; result.when( granted: () { setState(() { _hasLocationPermission = true; }); _initializeNavigationSession(); }, denied: () { _showErrorDialog( AppLocalizations.of(context)?.locationPermissionDenied ?? 'Location permission denied. Navigation cannot proceed.', ); widget.onNavigationCancelled?.call(); }, permanentlyDenied: () { _showPermissionSettingsDialog(); }, error: (message) { _showErrorDialog(message); widget.onNavigationCancelled?.call(); }, ); } void _showPermissionSettingsDialog() { final l10n = AppLocalizations.of(context); showDialog( context: context, builder: (context) => AlertDialog( title: Text(l10n?.permissionPermanentlyDenied ?? 'Permission Required'), content: Text( l10n?.openSettingsMessage ?? 'Location permission is permanently denied. Please enable it in app settings.', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text(l10n?.cancel ?? 'Cancel'), ), TextButton( onPressed: () { _permissionService.openAppSettings(); Navigator.of(context).pop(); }, child: Text(l10n?.openSettings ?? 'Open Settings'), ), ], ), ); } void _showErrorDialog(String message) { showDialog( context: context, builder: (context) => AlertDialog( title: Text(AppLocalizations.of(context)?.error ?? 'Error'), content: Text(message), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); widget.onNavigationCancelled?.call(); }, child: Text(AppLocalizations.of(context)?.ok ?? 'OK'), ), ], ), ); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Scaffold( appBar: AppBar( title: Text( '${l10n?.navigatingTo ?? 'Navigating to'}: ${widget.delivery.name}', ), elevation: 0, ), body: _hasLocationPermission && _isNavigationInitialized ? GoogleMapsNavigationView( onViewCreated: (controller) { _navigationViewController = controller; }, initialCameraPosition: CameraPosition( target: LatLng( latitude: widget.destinationLatitude, longitude: widget.destinationLongitude, ), zoom: 15, ), useMarkerClusteringForDynamicMarkers: true, zoomGesturesEnabled: true, scrollGesturesEnabled: true, navigationUIEnabled: true, mapToolbarEnabled: true, ) : Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text( l10n?.initializingNavigation ?? 'Initializing navigation...', ), ], ), ), floatingActionButton: _hasLocationPermission && _isNavigationInitialized ? FloatingActionButton( onPressed: () { widget.onNavigationCancelled?.call(); Navigator.of(context).pop(); }, child: const Icon(Icons.close), ) : null, ); } @override void dispose() { super.dispose(); } }