Files
fforte/lib/screens/excursion/widgets/tracking.dart
2025-06-06 19:46:29 +02:00

240 lines
7.5 KiB
Dart

import 'dart:async';
import 'package:fforte/l10n/app_localizations.dart';
import 'package:fforte/screens/addCam/services/geolocator_service.dart';
import 'package:fforte/screens/helper/add_entries_dialog_helper.dart';
import 'package:fforte/screens/helper/snack_bar_helper.dart';
import 'package:fforte/services/tracking_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
class Tracking extends StatefulWidget {
final Position startPosition;
final TextEditingController weg;
const Tracking({super.key, required this.startPosition, required this.weg});
@override
State<Tracking> createState() => _TrackingState();
}
class _TrackingState extends State<Tracking> {
final TrackingService _trackingService = TrackingService();
Position? currentPosition;
MapController mapController = MapController();
StreamSubscription? _positionSubscription;
StreamSubscription? _statsSubscription;
TrackingStats? _currentStats;
@override
void initState() {
super.initState();
if (widget.weg.text.isNotEmpty) {
for (var element in widget.weg.text.split(";")) {
List<String> posSplit = element.split(",");
try {
posSplit[0] = posSplit[0].substring(0, 9);
posSplit[1] = posSplit[1].substring(0, 9);
} on RangeError {
// ignore because the double is short enough then
}
_trackingService.pathList.add(
LatLng(double.parse(posSplit.first), double.parse(posSplit[1])),
);
}
}
currentPosition = widget.startPosition;
// Initialisiere die Statistiken sofort
setState(() {
_currentStats = _trackingService.currentStats;
});
_trackingService.requestStatsUpdate();
_positionSubscription = _trackingService.positionStream$.listen((position) {
setState(() {
currentPosition = position;
});
});
_statsSubscription = _trackingService.statsStream$.listen((stats) {
setState(() {
_currentStats = stats;
});
});
GeolocatorService.alwaysPositionEnabled().then((value) {
if (!value && mounted) {
Navigator.of(context).pop();
SnackBarHelper.showSnackBarMessage(context, "${AppLocalizations.of(context)!.locationForbidden} ${AppLocalizations.of(context)!.oder} ${AppLocalizations.of(context)!.locationDisabled}");
}
});
}
@override
void dispose() {
_positionSubscription?.cancel();
_statsSubscription?.cancel();
widget.weg.text = _trackingService.getPathAsString();
super.dispose();
}
String _formatDistance(double meters) {
if (meters >= 1000) {
return '${(meters / 1000).toStringAsFixed(2)} km';
}
return '${meters.toStringAsFixed(1)} m';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(AppLocalizations.of(context)!.tracking),
if (_currentStats != null)
DefaultTextStyle(
style: Theme.of(context).textTheme.bodySmall!,
child: Row(
children: [
Expanded(
child: Text(
'${AppLocalizations.of(context)!.accuracy}: ${_currentStats!.currentAccuracy.toStringAsFixed(1)}m (∅ ${_currentStats!.averageAccuracy.toStringAsFixed(1)}m)',
overflow: TextOverflow.ellipsis,
),
),
Text(
_formatDistance(_currentStats!.totalDistanceMeters),
),
],
),
),
],
),
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back_rounded)
),
actions: [
if (!_trackingService.isTracking)
IconButton(
onPressed: () async {
bool delete = await AddEntriesDialogHelper.deleteCompleteRouteDialog(context);
if (delete) {
setState(() {
_trackingService.clearPath();
});
}
},
icon: Icon(
Icons.delete,
color: Theme.of(context).colorScheme.errorContainer,
),
),
if (_trackingService.isTracking)
TextButton(
onPressed: () {
setState(() {
_trackingService.stopTracking();
});
},
child: Text(AppLocalizations.of(context)!.trackingStop),
),
TextButton(
onPressed: () {
setState(() {
if (_trackingService.isTracking) {
_trackingService.pauseTracking();
} else {
_trackingService.startTracking(context);
}
});
},
child: _trackingService.isTracking
? Text(AppLocalizations.of(context)!.trackingPause)
: Text(AppLocalizations.of(context)!.trackingStart),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
mapController.move(
LatLng(
currentPosition!.latitude,
currentPosition!.longitude,
),
16,
);
},
child: Icon(Icons.my_location),
),
body: FlutterMap(
mapController: mapController,
options: MapOptions(
interactionOptions: const InteractionOptions(
flags: InteractiveFlag.pinchZoom |
InteractiveFlag.drag |
InteractiveFlag.pinchMove,
),
initialCenter: LatLng(
widget.startPosition.latitude,
widget.startPosition.longitude,
),
initialZoom: 16.0,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'de.lupus.apps',
),
if (_trackingService.pathList.isNotEmpty)
PolylineLayer(
polylines: [
Polyline(
strokeWidth: 2.0,
points: _trackingService.pathList,
color: Colors.red
),
],
),
if (currentPosition != null)
CircleLayer(
circles: [
CircleMarker(
point: LatLng(
currentPosition!.latitude,
currentPosition!.longitude,
),
radius: currentPosition!.accuracy,
color: Colors.blue.withAlpha(2),
borderColor: Colors.blue,
borderStrokeWidth: 2,
),
CircleMarker(
point: LatLng(
currentPosition!.latitude,
currentPosition!.longitude,
),
radius: 5,
color: Colors.blue,
borderColor: Colors.white,
borderStrokeWidth: 2,
),
],
),
CurrentLocationLayer(),
],
),
);
}
}