flutter_google_track_and_trace/lib/src/google_map.dart

290 lines
8.6 KiB
Dart
Raw Normal View History

2021-09-28 08:18:14 +02:00
part of google_track_trace;
2021-09-29 10:07:56 +02:00
enum TimePrecision { updateOnly, everySecond, everyMinute }
2021-09-28 08:18:14 +02:00
class GoogleTrackTraceMap extends StatefulWidget {
2021-09-30 10:09:57 +02:00
const GoogleTrackTraceMap({
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,
2021-09-30 10:09:57 +02:00
Key? key,
2021-10-01 15:00:23 +02:00
this.markerUpdatePrecision = 50,
2021-09-29 10:07:56 +02:00
this.timerPrecision = TimePrecision.everyMinute,
2021-09-28 10:37:48 +02:00
this.travelMode = TravelMode.driving,
2021-10-01 15:00:23 +02:00
this.cameraTargetBounds,
2021-09-29 10:07:56 +02:00
this.compassEnabled = false,
2021-10-01 15:00:23 +02:00
this.rotateGesturesEnabled = false,
this.scrollGesturesEnabled = false,
2021-09-29 10:07:56 +02:00
this.zoomControlsEnabled = false,
this.zoomGesturesEnabled = false,
2021-10-01 15:00:23 +02:00
this.liteModeEnabled = false,
this.tiltGesturesEnabled = false,
this.myLocationEnabled = false,
this.myLocationButtonEnabled = false,
2021-09-29 10:07:56 +02:00
this.mapToolbarEnabled = false,
this.mapType = MapType.normal,
this.buildingsEnabled = false,
2021-10-01 15:00:23 +02:00
this.indoorViewEnabled = false,
this.trafficEnabled = false,
2021-09-30 12:34:43 +02:00
this.mapStylingTheme,
2021-10-01 15:00:23 +02:00
this.onTap,
this.onArrived,
this.onLongPress,
this.onCameraMove,
2021-09-29 12:18:53 +02:00
this.line,
2021-09-30 12:34:43 +02:00
}) : super(key: key);
2021-09-28 08:18:14 +02:00
/// 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
2021-10-01 15:00:23 +02:00
final Duration routeUpdateInterval;
2021-09-29 10:07:56 +02:00
2021-10-01 15:00:23 +02:00
/// amount of meter the marker needs to move to update
final int markerUpdatePrecision;
2021-09-29 10:07:56 +02:00
final TimePrecision timerPrecision;
final Marker startPosition;
final Marker destinationPosition;
2021-09-30 10:09:57 +02:00
final Polyline? line;
2021-09-29 12:18:53 +02:00
2021-09-29 10:07:56 +02:00
final bool compassEnabled;
2021-10-01 15:00:23 +02:00
final bool rotateGesturesEnabled;
final bool scrollGesturesEnabled;
final bool liteModeEnabled;
final bool tiltGesturesEnabled;
final bool myLocationEnabled;
final bool myLocationButtonEnabled;
2021-09-29 10:07:56 +02:00
final bool zoomControlsEnabled;
final bool zoomGesturesEnabled;
final bool mapToolbarEnabled;
final bool buildingsEnabled;
2021-10-01 15:00:23 +02:00
final bool indoorViewEnabled;
final bool trafficEnabled;
final CameraTargetBounds? cameraTargetBounds;
2021-09-29 10:07:56 +02:00
final MapType mapType;
2021-09-30 12:34:43 +02:00
final GoogleTrackTraceMapTheme? mapStylingTheme;
2021-09-29 12:18:53 +02:00
2021-10-01 15:00:23 +02:00
final ArgumentCallback<LatLng>? onTap;
final void Function()? onArrived;
final ArgumentCallback<LatLng>? onLongPress;
final CameraPositionCallback? onCameraMove;
2021-09-29 10:07:56 +02:00
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();
2021-10-01 15:00:23 +02:00
late final Timer routeCalculateTimer;
late final Timer markerUpdateTimer;
2021-09-29 10:07:56 +02:00
@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-10-01 15:00:23 +02:00
controller.addListener(
() => setState(() {}),
);
2021-09-29 12:18:53 +02:00
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(
2021-09-30 12:34:43 +02:00
initialCameraPosition: calculateCameraPosition(
controller.start.position,
controller.end.position,
),
onMapCreated: _onMapCreated,
2021-10-01 15:00:23 +02:00
onTap: widget.onTap,
onLongPress: widget.onLongPress,
onCameraMove: widget.onCameraMove,
2021-09-30 12:34:43 +02:00
compassEnabled: widget.compassEnabled,
2021-10-01 15:00:23 +02:00
rotateGesturesEnabled: widget.rotateGesturesEnabled,
scrollGesturesEnabled: widget.scrollGesturesEnabled,
2021-09-30 12:34:43 +02:00
zoomControlsEnabled: widget.zoomControlsEnabled,
zoomGesturesEnabled: widget.zoomGesturesEnabled,
2021-10-01 15:00:23 +02:00
liteModeEnabled: widget.liteModeEnabled,
tiltGesturesEnabled: widget.tiltGesturesEnabled,
myLocationEnabled: widget.myLocationEnabled,
myLocationButtonEnabled: widget.myLocationButtonEnabled,
2021-09-30 12:34:43 +02:00
mapToolbarEnabled: widget.mapToolbarEnabled,
mapType: widget.mapType,
buildingsEnabled: widget.buildingsEnabled,
2021-10-01 15:00:23 +02:00
indoorViewEnabled: widget.indoorViewEnabled,
trafficEnabled: widget.trafficEnabled,
2021-09-30 12:34:43 +02:00
markers: <Marker>{
controller.start,
controller.end,
},
polylines: <Polyline>{
if (controller.route != null)
(widget.line != null)
? widget.line!.copyWith(
pointsParam: controller.route!.line
.map((PointLatLng 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((PointLatLng 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 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;
2021-09-30 12:34:43 +02:00
if (widget.mapStylingTheme != null) {
ctr.setMapStyle(widget.mapStylingTheme!.getJson());
} else {
// No theme provided so switching to default
ctr.setMapStyle(
'[{"featureType": "poi","stylers": [{"visibility": "off"}]}]',
);
}
2021-10-01 15:00:23 +02:00
controller.recenterCamera();
2021-09-28 08:18:14 +02:00
}
}
CameraPosition calculateCameraPosition(LatLng pointA, LatLng pointB) {
2021-09-30 10:09:57 +02:00
var target = LatLng(
(pointA.latitude + pointB.latitude) / 2,
(pointA.longitude + pointB.longitude) / 2,
);
2021-09-28 08:18:14 +02:00
return CameraPosition(
2021-09-30 10:09:57 +02:00
target: target,
zoom: 13.0,
tilt: 0.0,
bearing: 0.0,
);
2021-09-28 08:18:14 +02:00
}
2021-09-29 10:07:56 +02:00
void startRouteUpdateTimer() {
calculateRoute(); // run at the start
2021-10-01 15:00:23 +02:00
routeCalculateTimer =
Timer.periodic(widget.routeUpdateInterval, (Timer timer) {
2021-09-29 10:07:56 +02:00
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-30 10:09:57 +02:00
var updateInterval =
2021-09-29 12:18:53 +02:00
(widget.timerPrecision == TimePrecision.everyMinute) ? 60 : 1;
2021-10-01 15:00:23 +02:00
markerUpdateTimer =
Timer.periodic(Duration(seconds: updateInterval), (timer) {
2021-09-29 12:18:53 +02:00
if (controller.route != null) {
2021-10-01 15:00:23 +02:00
checkDestinationCloseBy();
2021-09-29 13:42:13 +02:00
controller.route = TrackTraceRoute(
2021-10-01 15:00:23 +02:00
(controller.route!.duration != 0)
? controller.route!.duration - updateInterval
: 0,
2021-09-30 12:34:43 +02:00
controller.route!.distance,
controller.route!.line,
);
2021-09-29 12:18:53 +02:00
}
2021-09-29 10:07:56 +02:00
});
}
2021-09-28 08:18:14 +02:00
}
2021-09-30 12:34:43 +02:00
void calculateRoute() {
2021-10-01 15:00:23 +02:00
if (controller.route == null || checkTargetMoved()) {
DirectionsRepository() // TODO(freek): refactor this away
.getDirections(
origin: controller.start.position,
destination: controller.end.position,
mode: widget.travelMode,
key: widget.googleAPIKey,
)
.then(
(value) => {
controller.route = TrackTraceRoute(
value.totalDuration,
value.totalDistance,
value.polylinePoints,
),
checkDestinationCloseBy(),
controller.recenterCamera(),
setState(() {
lastRouteUpdate = DateTime.now();
}),
},
);
}
}
void checkDestinationCloseBy() {
if (calculatePointProximity(
controller.start.position,
controller.end.position,
) <
widget.markerUpdatePrecision) {
routeCalculateTimer.cancel();
markerUpdateTimer.cancel();
if (controller.route != null) {
controller.route!.line = <PointLatLng>[controller.route!.line[1]];
controller.route!.distance = 0;
controller.route!.duration = 0;
}
widget.onArrived?.call();
}
}
bool checkTargetMoved() {
return calculatePointProximity(
controller.start.position,
LatLng(
controller.route!.line[0].latitude,
controller.route!.line[0].longitude,
),
) >=
widget.markerUpdatePrecision;
}
double calculatePointProximity(LatLng pointA, LatLng pointB) {
var p = 0.017453292519943295;
var c = cos;
var a = 0.5 -
c(
(pointA.latitude - pointB.latitude) * p,
) /
2 +
c(controller.route!.line[0].latitude * p) *
c(pointA.latitude * p) *
(1 -
c(
(pointA.longitude - pointB.longitude) * p,
)) /
2;
return 12742 * asin(sqrt(a)) * 1000;
2021-09-28 08:18:14 +02:00
}
}