MotorSignal-App/lib/features/map/map_screen.dart

215 lines
6.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:drift/drift.dart';
import '../../database/database.dart';
import '../../core/theme/map_style.dart';
import '../../services/location_service.dart';
class MapScreen extends ConsumerStatefulWidget {
final int? rideId;
const MapScreen({super.key, this.rideId});
@override
ConsumerState<MapScreen> createState() => _MapScreenState();
}
class _MapScreenState extends ConsumerState<MapScreen> {
GoogleMapController? _mapController;
MapType _currentMapType = MapType.normal;
bool _isLoadingHistory = true;
final Set<Polyline> _polylines = {};
final Set<Marker> _markers = {};
LatLng? _initialCameraPosition;
@override
void initState() {
super.initState();
if (widget.rideId != null) {
_loadRideHistory();
} else {
_isLoadingHistory = false;
}
}
Future<void> _loadRideHistory() async {
final db = ref.read(databaseProvider);
// Fetch telemetry for route
final telemetry = await (db.select(db.telemetrySamples)
..where((t) => t.rideId.equals(widget.rideId!))
..orderBy([(t) => OrderingTerm(expression: t.timestamp)])
).get();
// Fetch events for markers
final events = await (db.select(db.rideEvents)
..where((e) => e.rideId.equals(widget.rideId!))
).get();
if (telemetry.isNotEmpty) {
final List<LatLng> points = telemetry
.where((t) => t.latitude != null && t.longitude != null)
.map((t) => LatLng(t.latitude!, t.longitude!))
.toList();
if (points.isNotEmpty) {
_initialCameraPosition = points.first;
_polylines.add(
Polyline(
polylineId: const PolylineId('route'),
points: points,
color: Colors.cyanAccent,
width: 5,
)
);
}
}
for (final event in events) {
if (event.latitude != null && event.longitude != null) {
BitmapDescriptor icon = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed);
if (event.eventType == 'Extreme Lean') icon = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange);
if (event.eventType == 'Hard Acceleration') icon = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen);
if (event.eventType == 'High Speed') icon = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet);
_markers.add(
Marker(
markerId: MarkerId(event.id.toString()),
position: LatLng(event.latitude!, event.longitude!),
icon: icon,
infoWindow: InfoWindow(
title: event.eventType,
snippet: 'Value: ${event.value.toStringAsFixed(2)}',
),
)
);
}
}
setState(() {
_isLoadingHistory = false;
});
// Adjust camera to fit route if map is ready
_fitRoute();
}
void _fitRoute() {
if (_mapController == null || _polylines.isEmpty) return;
final points = _polylines.first.points;
if (points.isEmpty) return;
double minLat = points.first.latitude;
double maxLat = points.first.latitude;
double minLng = points.first.longitude;
double maxLng = points.first.longitude;
for (final p in points) {
if (p.latitude < minLat) minLat = p.latitude;
if (p.latitude > maxLat) maxLat = p.latitude;
if (p.longitude < minLng) minLng = p.longitude;
if (p.longitude > maxLng) maxLng = p.longitude;
}
_mapController!.animateCamera(
CameraUpdate.newLatLngBounds(
LatLngBounds(
southwest: LatLng(minLat, minLng),
northeast: LatLng(maxLat, maxLng),
),
50.0, // padding
)
);
}
@override
Widget build(BuildContext context) {
if (_isLoadingHistory) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
}
Widget mapContent;
if (widget.rideId != null) {
// Historical Ride View
mapContent = GoogleMap(
initialCameraPosition: CameraPosition(
target: _initialCameraPosition ?? const LatLng(0, 0),
zoom: 15.0,
),
mapType: _currentMapType,
polylines: _polylines,
markers: _markers,
myLocationEnabled: false,
onMapCreated: (controller) {
_mapController = controller;
_fitRoute();
},
);
} else {
// Live Tracking View
final locationAsync = ref.watch(locationStreamProvider);
mapContent = locationAsync.when(
data: (position) {
final currentLatLng = LatLng(position.latitude, position.longitude);
if (_mapController != null) {
_mapController!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: currentLatLng,
zoom: 17.0,
bearing: position.heading,
),
),
);
}
return GoogleMap(
initialCameraPosition: CameraPosition(
target: currentLatLng,
zoom: 17.0,
),
mapType: _currentMapType,
myLocationEnabled: true,
myLocationButtonEnabled: true,
compassEnabled: true,
onMapCreated: (controller) {
_mapController = controller;
_mapController?.setMapStyle(darkMapStyle);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
);
}
return Scaffold(
appBar: AppBar(
title: Text(widget.rideId != null ? 'Ride ${widget.rideId} Map' : 'Map'),
actions: [
IconButton(
icon: Icon(
_currentMapType == MapType.normal
? Icons.satellite
: Icons.map,
),
onPressed: () {
setState(() {
_currentMapType = _currentMapType == MapType.normal
? MapType.satellite
: MapType.normal;
});
},
),
],
),
body: mapContent,
);
}
}