ionic-planb-logistic-app-fl.../lib/pages/navigation_page.dart
Jean-Philippe Brule 57b81d1e95 Fix linting issues and code quality improvements
Resolve 62 linting issues identified by flutter analyze, reducing
total issues from 79 to 17. All critical warnings addressed.

Changes:
- Replace deprecated withOpacity() with withValues(alpha:) (25 instances)
- Remove unused imports from 9 files
- Remove unused variables and fields (6 instances)
- Fix Riverpod 3.0 state access violations in settings_page
- Remove unnecessary null-aware operators in navigation_page (6 instances)
- Fix unnecessary type casts in providers (4 instances)
- Remove unused methods: _getDarkMapStyle, _showPermissionDialog
- Simplify hover state management by removing unused _isHovered fields

Remaining 17 issues are info-level style suggestions and defensive
programming patterns that don't impact functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 01:39:35 -05:00

405 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_navigation_flutter/google_navigation_flutter.dart';
import 'package:planb_logistic/l10n/app_localizations.dart';
import '../models/delivery.dart';
import '../services/location_permission_service.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<NavigationPage> createState() => _NavigationPageState();
}
class _NavigationPageState extends ConsumerState<NavigationPage> {
GoogleNavigationViewController? _navigationViewController;
late LocationPermissionService _permissionService;
bool _isNavigationInitialized = false;
bool _hasLocationPermission = false;
bool _isControllerReady = false;
Brightness? _lastBrightness;
@override
void initState() {
super.initState();
_permissionService = LocationPermissionService();
_initializeNavigation();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Detect theme changes and reapply map style
final currentBrightness = Theme.of(context).brightness;
if (_lastBrightness != null &&
_lastBrightness != currentBrightness &&
_isControllerReady) {
_applyDarkModeStyle();
}
_lastBrightness = currentBrightness;
}
Future<void> _initializeNavigation() async {
try {
final hasPermission = await _permissionService.hasLocationPermission();
if (!hasPermission) {
if (mounted) {
await _requestLocationPermission();
}
return;
}
setState(() {
_hasLocationPermission = true;
_isNavigationInitialized = true;
});
} catch (e) {
if (mounted) {
_showErrorDialog('Initialization error: ${e.toString()}');
}
}
}
Future<void> _initializeNavigationSession() async {
try {
await GoogleMapsNavigator.initializeNavigationSession();
} catch (e) {
debugPrint('Navigation session initialization error: $e');
// Don't show error dialog, just log it
// The session might already be initialized
}
}
Future<void> _setDestination() async {
try {
final waypoint = NavigationWaypoint.withLatLngTarget(
title: widget.delivery.name,
target: LatLng(
latitude: widget.destinationLatitude,
longitude: widget.destinationLongitude,
),
);
final destinations = Destinations(
waypoints: [waypoint],
displayOptions: NavigationDisplayOptions(showDestinationMarkers: true),
);
await GoogleMapsNavigator.setDestinations(destinations);
// Start guidance automatically
await GoogleMapsNavigator.startGuidance();
// Reapply dark mode style after navigation starts
if (mounted) {
await _applyDarkModeStyle();
}
// Listen for arrival events
GoogleMapsNavigator.setOnArrivalListener((event) {
if (mounted) {
_handleArrival(event);
}
});
} catch (e) {
if (mounted) {
_showErrorDialog('Failed to set destination: ${e.toString()}');
}
}
}
void _handleArrival(OnArrivalEvent event) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('You have arrived at the destination'),
duration: Duration(seconds: 3),
),
);
// Call completion callback
widget.onNavigationComplete?.call();
}
}
Future<void> _requestLocationPermission() async {
final result = await _permissionService.requestLocationPermission();
if (!mounted) return;
result.when(
granted: () {
setState(() {
_hasLocationPermission = true;
_isNavigationInitialized = true;
});
_initializeNavigationSession();
},
denied: () {
_showErrorDialog(
AppLocalizations.of(context).locationPermissionDenied,
);
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),
content: Text(l10n.openSettingsMessage),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(l10n.cancel),
),
TextButton(
onPressed: () {
_permissionService.openAppSettings();
Navigator.of(context).pop();
},
child: Text(l10n.openSettings),
),
],
),
);
}
void _showErrorDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(AppLocalizations.of(context).errorTitle),
content: Text(message),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
widget.onNavigationCancelled?.call();
},
child: Text(AppLocalizations.of(context).ok),
),
],
),
);
}
Future<void> _applyDarkModeStyle() async {
if (_navigationViewController == null || !_isControllerReady) return;
try {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
if (isDarkMode) {
await _navigationViewController!.setMapStyle(_getDarkMapStyle());
} else {
await _navigationViewController!.setMapStyle(null);
}
} catch (e) {
debugPrint('Error applying map style: $e');
}
}
String _getDarkMapStyle() {
// Google Maps style JSON for dark mode with warm accents
return '''[
{
"elementType": "geometry",
"stylers": [{"color": "#212121"}]
},
{
"elementType": "labels.icon",
"stylers": [{"visibility": "off"}]
},
{
"elementType": "labels.text.fill",
"stylers": [{"color": "#757575"}]
},
{
"elementType": "labels.text.stroke",
"stylers": [{"color": "#212121"}]
},
{
"featureType": "administrative",
"elementType": "geometry",
"stylers": [{"color": "#757575"}]
},
{
"featureType": "administrative.country",
"elementType": "labels.text.fill",
"stylers": [{"color": "#9e9e9e"}]
},
{
"featureType": "administrative.land_parcel",
"stylers": [{"visibility": "off"}]
},
{
"featureType": "administrative.locality",
"elementType": "labels.text.fill",
"stylers": [{"color": "#bdbdbd"}]
},
{
"featureType": "administrative.neighborhood",
"stylers": [{"visibility": "off"}]
},
{
"featureType": "administrative.province",
"elementType": "labels.text.fill",
"stylers": [{"color": "#9e9e9e"}]
},
{
"featureType": "landscape",
"elementType": "geometry",
"stylers": [{"color": "#000000"}]
},
{
"featureType": "poi",
"elementType": "geometry",
"stylers": [{"color": "#383838"}]
},
{
"featureType": "poi",
"elementType": "labels.text.fill",
"stylers": [{"color": "#9e9e9e"}]
},
{
"featureType": "poi.park",
"elementType": "geometry",
"stylers": [{"color": "#181818"}]
},
{
"featureType": "poi.park",
"elementType": "labels.text.fill",
"stylers": [{"color": "#616161"}]
},
{
"featureType": "road",
"elementType": "geometry.fill",
"stylers": [{"color": "#2c2c2c"}]
},
{
"featureType": "road",
"elementType": "labels.text.fill",
"stylers": [{"color": "#8a8a8a"}]
},
{
"featureType": "road.arterial",
"elementType": "geometry",
"stylers": [{"color": "#373737"}]
},
{
"featureType": "road.highway",
"elementType": "geometry",
"stylers": [{"color": "#3c3c3c"}]
},
{
"featureType": "road.highway.controlled_access",
"elementType": "geometry",
"stylers": [{"color": "#4e4e4e"}]
},
{
"featureType": "road.local",
"elementType": "labels.text.fill",
"stylers": [{"color": "#616161"}]
},
{
"featureType": "transit",
"elementType": "labels.text.fill",
"stylers": [{"color": "#757575"}]
},
{
"featureType": "water",
"elementType": "geometry",
"stylers": [{"color": "#0c1221"}]
},
{
"featureType": "water",
"elementType": "labels.text.fill",
"stylers": [{"color": "#3d3d3d"}]
}
]''';
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: Text(
'${l10n.navigatingTo}: ${widget.delivery.name}',
),
elevation: 0,
),
body: _hasLocationPermission && _isNavigationInitialized
? GoogleMapsNavigationView(
onViewCreated: (controller) async {
_navigationViewController = controller;
_isControllerReady = true;
await _initializeNavigationSession();
await Future.delayed(const Duration(milliseconds: 500));
await _applyDarkModeStyle();
await _setDestination();
},
initialCameraPosition: CameraPosition(
target: LatLng(
latitude: widget.destinationLatitude,
longitude: widget.destinationLongitude,
),
zoom: 15,
),
)
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(l10n.initializingNavigation),
],
),
),
floatingActionButton: _hasLocationPermission && _isNavigationInitialized
? FloatingActionButton(
onPressed: () {
widget.onNavigationCancelled?.call();
Navigator.of(context).pop();
},
child: const Icon(Icons.close),
)
: null,
);
}
@override
void dispose() {
super.dispose();
}
}