2021-09-28 08:18:14 +02:00
|
|
|
part of google_track_trace;
|
|
|
|
|
2021-09-29 10:07:56 +02:00
|
|
|
// typedef void MapCreatedCallback(TrackTraceController controller);
|
|
|
|
enum TimePrecision { updateOnly, everySecond, everyMinute }
|
2021-09-28 08:18:14 +02:00
|
|
|
|
|
|
|
class GoogleTrackTraceMap extends StatefulWidget {
|
|
|
|
GoogleTrackTraceMap({
|
|
|
|
Key? key,
|
2021-09-29 10:07:56 +02:00
|
|
|
required this.onMapCreated,
|
2021-09-28 08:18:14 +02:00
|
|
|
required this.startPosition,
|
|
|
|
required this.destinationPosition,
|
2021-09-29 10:07:56 +02:00
|
|
|
required this.googleAPIKey,
|
|
|
|
required this.routeUpdateInterval,
|
|
|
|
this.timerPrecision = TimePrecision.everyMinute,
|
2021-09-28 10:37:48 +02:00
|
|
|
this.travelMode = TravelMode.driving,
|
2021-09-29 10:07:56 +02:00
|
|
|
this.compassEnabled = false,
|
|
|
|
this.zoomControlsEnabled = false,
|
|
|
|
this.zoomGesturesEnabled = false,
|
|
|
|
this.mapToolbarEnabled = false,
|
|
|
|
this.mapType = MapType.normal,
|
|
|
|
this.buildingsEnabled = false,
|
2021-09-29 12:18:53 +02:00
|
|
|
this.mapMarkations =
|
|
|
|
'[{"featureType": "poi","stylers": [{"visibility": "off"}]}]',
|
|
|
|
this.line,
|
2021-09-28 08:18:14 +02:00
|
|
|
}) : assert(true),
|
|
|
|
super(key: key);
|
|
|
|
|
|
|
|
/// Callback method for when the map is ready to be used.
|
|
|
|
///
|
|
|
|
/// Used to receive a [TrackTraceController] for this [GoogleTrackTraceMap].
|
|
|
|
/// this [TrackTraceController] also contains the [GoogleMapController]
|
2021-09-29 10:07:56 +02:00
|
|
|
final void Function(TrackTraceController) onMapCreated;
|
2021-09-28 08:18:14 +02:00
|
|
|
|
|
|
|
final TravelMode travelMode;
|
2021-09-29 10:07:56 +02:00
|
|
|
|
|
|
|
final int routeUpdateInterval;
|
|
|
|
|
|
|
|
final TimePrecision timerPrecision;
|
|
|
|
|
|
|
|
final Marker startPosition;
|
|
|
|
final Marker destinationPosition;
|
|
|
|
|
2021-09-29 12:18:53 +02:00
|
|
|
Polyline? line;
|
|
|
|
|
2021-09-29 10:07:56 +02:00
|
|
|
final bool compassEnabled;
|
|
|
|
final bool zoomControlsEnabled;
|
|
|
|
final bool zoomGesturesEnabled;
|
|
|
|
final bool mapToolbarEnabled;
|
|
|
|
final bool buildingsEnabled;
|
|
|
|
final MapType mapType;
|
|
|
|
|
2021-09-29 12:18:53 +02:00
|
|
|
final String mapMarkations;
|
|
|
|
|
2021-09-29 10:07:56 +02:00
|
|
|
CameraPosition initialCameraPosition = const CameraPosition(
|
|
|
|
// doetinchem default initialCamera
|
|
|
|
target: LatLng(51.965578, 6.293439),
|
|
|
|
zoom: 12.0);
|
|
|
|
|
|
|
|
final String googleAPIKey;
|
2021-09-28 08:18:14 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
State createState() => _GoogleTrackTraceMapState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
|
2021-09-29 12:18:53 +02:00
|
|
|
late final TrackTraceController controller;
|
2021-09-29 10:07:56 +02:00
|
|
|
|
|
|
|
DateTime lastRouteUpdate = DateTime.now();
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
2021-09-29 12:18:53 +02:00
|
|
|
controller =
|
2021-09-29 10:07:56 +02:00
|
|
|
TrackTraceController(widget.startPosition, widget.destinationPosition);
|
2021-09-29 12:18:53 +02:00
|
|
|
controller.addListener(_onChange);
|
|
|
|
widget.onMapCreated(controller);
|
2021-09-29 10:07:56 +02:00
|
|
|
startRouteUpdateTimer();
|
|
|
|
startMarkerUpdateTimer();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
2021-09-29 12:18:53 +02:00
|
|
|
controller.dispose();
|
2021-09-29 10:07:56 +02:00
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return GoogleMap(
|
|
|
|
initialCameraPosition: calculateCameraPosition(
|
2021-09-29 12:18:53 +02:00
|
|
|
controller.start.position, controller.end.position),
|
2021-09-29 10:07:56 +02:00
|
|
|
onMapCreated: _onMapCreated,
|
|
|
|
compassEnabled: widget.compassEnabled,
|
|
|
|
zoomControlsEnabled: widget.zoomControlsEnabled,
|
|
|
|
zoomGesturesEnabled: widget.zoomGesturesEnabled,
|
|
|
|
mapToolbarEnabled: widget.mapToolbarEnabled,
|
|
|
|
mapType: widget.mapType,
|
|
|
|
buildingsEnabled: widget.buildingsEnabled,
|
|
|
|
markers: {
|
2021-09-29 12:18:53 +02:00
|
|
|
controller.start,
|
|
|
|
controller.end,
|
2021-09-29 10:07:56 +02:00
|
|
|
},
|
|
|
|
polylines: {
|
2021-09-29 12:18:53 +02:00
|
|
|
if (controller.route != null)
|
|
|
|
(widget.line != null)
|
|
|
|
? widget.line!.copyWith(
|
|
|
|
pointsParam: controller.route!.line
|
|
|
|
.map((e) => LatLng(e.latitude, e.longitude))
|
|
|
|
.toList())
|
|
|
|
: Polyline(
|
|
|
|
// default PolyLine if none is provided
|
|
|
|
polylineId: const PolylineId('track&trace route'),
|
|
|
|
color: Theme.of(context).primaryColor,
|
|
|
|
width: 4,
|
|
|
|
points: controller.route!.line
|
|
|
|
.map((e) => LatLng(e.latitude, e.longitude))
|
|
|
|
.toList(),
|
|
|
|
),
|
2021-09-29 10:07:56 +02:00
|
|
|
});
|
|
|
|
}
|
2021-09-28 08:18:14 +02:00
|
|
|
|
2021-09-29 10:07:56 +02:00
|
|
|
void _onChange() {
|
|
|
|
setState(() {});
|
|
|
|
}
|
2021-09-28 08:18:14 +02:00
|
|
|
|
2021-09-29 12:18:53 +02:00
|
|
|
void _onMapCreated(GoogleMapController ctr) {
|
2021-09-28 08:18:14 +02:00
|
|
|
if (mounted) {
|
2021-09-29 12:18:53 +02:00
|
|
|
controller.mapController = ctr;
|
|
|
|
ctr.setMapStyle(widget.mapMarkations); // move to dart json file
|
2021-09-28 08:18:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CameraPosition calculateCameraPosition(LatLng pointA, LatLng pointB) {
|
|
|
|
LatLng target = LatLng((pointA.latitude + pointB.latitude) / 2,
|
|
|
|
(pointA.longitude + pointB.longitude) / 2);
|
2021-09-29 10:07:56 +02:00
|
|
|
double calculatedZoom = 13.0; // TODO calculate this zoom
|
2021-09-29 12:18:53 +02:00
|
|
|
|
2021-09-28 08:18:14 +02:00
|
|
|
return CameraPosition(
|
|
|
|
target: target, zoom: calculatedZoom, tilt: 0.0, bearing: 0.0);
|
|
|
|
}
|
|
|
|
|
2021-09-29 12:18:53 +02:00
|
|
|
CameraUpdate moveCameraToCenter(LatLng pointA, LatLng pointB) {
|
|
|
|
return CameraUpdate.newLatLngBounds(
|
|
|
|
LatLngBounds(
|
|
|
|
southwest: LatLng(
|
|
|
|
min(pointA.latitude, pointB.latitude),
|
|
|
|
min(pointA.longitude, pointB.longitude),
|
|
|
|
),
|
|
|
|
northeast: LatLng(
|
|
|
|
max(pointA.latitude, pointB.latitude),
|
|
|
|
max(pointA.longitude, pointB.longitude),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
50);
|
|
|
|
}
|
|
|
|
|
2021-09-29 10:07:56 +02:00
|
|
|
void startRouteUpdateTimer() {
|
|
|
|
calculateRoute(); // run at the start
|
|
|
|
Timer.periodic(Duration(seconds: widget.routeUpdateInterval), (timer) {
|
|
|
|
calculateRoute();
|
|
|
|
});
|
2021-09-28 08:18:14 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 10:07:56 +02:00
|
|
|
void startMarkerUpdateTimer() {
|
|
|
|
if (widget.timerPrecision != TimePrecision.updateOnly) {
|
2021-09-29 12:18:53 +02:00
|
|
|
int updateInterval =
|
|
|
|
(widget.timerPrecision == TimePrecision.everyMinute) ? 60 : 1;
|
|
|
|
Timer.periodic(Duration(seconds: updateInterval), (timer) {
|
|
|
|
if (controller.route != null) {
|
|
|
|
controller.route!.duration =
|
|
|
|
controller.route!.duration - updateInterval;
|
|
|
|
}
|
2021-09-29 10:07:56 +02:00
|
|
|
});
|
|
|
|
}
|
2021-09-28 08:18:14 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 12:18:53 +02:00
|
|
|
void calculateRoute() async {
|
|
|
|
DirectionsRepository() //TODO refactor this away
|
2021-09-29 10:07:56 +02:00
|
|
|
.getDirections(
|
2021-09-29 12:18:53 +02:00
|
|
|
origin: controller.start.position,
|
|
|
|
destination: controller.end.position,
|
2021-09-29 10:07:56 +02:00
|
|
|
mode: widget.travelMode,
|
|
|
|
key: widget.googleAPIKey,
|
|
|
|
)
|
|
|
|
.then((value) => {
|
2021-09-29 12:18:53 +02:00
|
|
|
controller.route = TrackTraceRoute(value.totalDuration,
|
|
|
|
value.totalDistance, value.polylinePoints),
|
|
|
|
if (controller.mapController != null)
|
|
|
|
{
|
|
|
|
controller.mapController!.moveCamera(moveCameraToCenter(
|
|
|
|
controller.start.position, controller.end.position)),
|
|
|
|
},
|
2021-09-29 10:07:56 +02:00
|
|
|
setState(() {
|
|
|
|
lastRouteUpdate = DateTime.now();
|
|
|
|
})
|
|
|
|
});
|
2021-09-28 08:18:14 +02:00
|
|
|
}
|
|
|
|
}
|