working track&trace demo

This commit is contained in:
Freek van de Ven 2021-09-29 10:07:56 +02:00
parent 65262045f8
commit a39de81555
5 changed files with 252 additions and 126 deletions

View file

@ -1,3 +1,6 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_track_trace/google_track_trace.dart'; import 'package:google_track_trace/google_track_trace.dart';
@ -10,11 +13,30 @@ class TrackTraceDemo extends StatefulWidget {
} }
class _TrackTraceDemoState extends State<TrackTraceDemo> { class _TrackTraceDemoState extends State<TrackTraceDemo> {
late final TrackTraceController controller; TrackTraceController? controller;
@override
void initState() {
// TODO: implement initState
Timer.periodic(const Duration(seconds: 10), (_) {
print('updating marker');
getRandomPointOnMap();
});
Timer.periodic(const Duration(seconds: 60), (_) {
print('updating route');
getRandomRoute();
});
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('TrackTrace example')), appBar: AppBar(
title: (controller == null)
? const Text('TrackTrace example')
: Text(controller!.duration.toString() + ' seconds')),
body: GoogleTrackTraceMap( body: GoogleTrackTraceMap(
startPosition: const Marker( startPosition: const Marker(
markerId: MarkerId('Start locatie'), markerId: MarkerId('Start locatie'),
@ -24,14 +46,62 @@ class _TrackTraceDemoState extends State<TrackTraceDemo> {
markerId: MarkerId('Eind locatie'), markerId: MarkerId('Eind locatie'),
position: LatLng(51.958996, 6.296520), position: LatLng(51.958996, 6.296520),
), ),
travelMode: TravelMode.bicycling, googleAPIKey: 'AIzaSyDaxZX8TeQeVf5tW-D6A66WLl20arbWV6c',
onMapCreated: (ctr) => {controller = ctr}, travelMode: TravelMode.walking,
routeUpdateInterval: 60,
routeLabel: 'Test route',
timerPrecision: TimePrecision.everySecond,
zoomGesturesEnabled: true,
onMapCreated: (ctr) => {
controller = ctr,
ctr.addListener(() {
setState(() {});
}),
},
), ),
); );
} }
void updateMap() {
controller!.current = const Marker(
markerId: MarkerId('Huidige locatie'),
position: LatLng(51.962578, 6.294439),
);
}
void getRandomPointOnMap() {
// 51.989909, 6.234950
// 51.939909, 6.314950
if (controller != null) {
controller!.current = Marker(
markerId: MarkerId('Huidige Locatie'),
position: LatLng(51.93 + Random().nextDouble() * 0.06,
6.23 + Random().nextDouble() * 0.08));
}
}
void getRandomRoute() {
// if (route != null) {
// print('removing point');
// PointLatLng point = route!.polylinePoints[1];
// trackTraceController.startMarker = Marker(
// markerId: MarkerId('Start locatie'),
// position: LatLng(point.latitude, point.longitude));
// }
if (controller != null) {
controller!.start = Marker(
markerId: MarkerId('Start Locatie'),
position: LatLng(51.93 + Random().nextDouble() * 0.06,
6.23 + Random().nextDouble() * 0.08));
controller!.end = Marker(
markerId: MarkerId('Bestemming Locatie'),
position: LatLng(51.93 + Random().nextDouble() * 0.06,
6.23 + Random().nextDouble() * 0.08));
}
}
} }
void main() { void main() {
runApp(const MaterialApp(home: TrackTraceDemo( runApp(const MaterialApp(home: TrackTraceDemo()));
)));
} }

View file

@ -3,6 +3,7 @@ library google_track_trace;
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -1,24 +1,48 @@
part of google_track_trace; part of google_track_trace;
class TrackTraceController { class TrackTraceController extends ChangeNotifier {
late final Completer<GoogleMapController> _mapController; late final GoogleMapController _mapController;
Marker startPosition;
Marker destinationPosition;
Marker? currentPosition;
// get the duration int durationInSeconds = 0;
// get the distance TrackTraceController(Marker start, Marker destination)
: startPosition = start,
destinationPosition = destination;
// listen to updates on the source marker set start(Marker start) {
startPosition = start;
notifyListeners();
}
// listen to updates on the route set current(Marker? current) {
currentPosition = current;
notifyListeners();
}
set end(Marker end) {
destinationPosition = end;
notifyListeners();
}
void dispose() {} Marker get start => startPosition;
void setController(Completer<GoogleMapController> controller) { Marker? get current => currentPosition;
Marker get end => destinationPosition;
set duration(int duration) {
durationInSeconds = duration;
notifyListeners();
}
int get duration => durationInSeconds;
set mapController(GoogleMapController controller) {
_mapController = controller; _mapController = controller;
} }
Completer<GoogleMapController> getController() { GoogleMapController get mapController => _mapController;
return _mapController;
}
} }

View file

@ -30,7 +30,6 @@ class DirectionsRepository {
HttpHeaders.contentTypeHeader: 'application/json', HttpHeaders.contentTypeHeader: 'application/json',
}); });
if (response.statusCode == 200) { if (response.statusCode == 200) {
print(response.body);
return Directions.fromMap(jsonDecode(response.body)); return Directions.fromMap(jsonDecode(response.body));
} }
} on HttpException catch (e) { } on HttpException catch (e) {
@ -43,8 +42,8 @@ class DirectionsRepository {
class Directions { class Directions {
final LatLngBounds bounds; final LatLngBounds bounds;
final List<PointLatLng> polylinePoints; final List<PointLatLng> polylinePoints;
final String totalDistance; final int totalDistance;
final String totalDuration; final int totalDuration;
const Directions({ const Directions({
required this.bounds, required this.bounds,
@ -67,12 +66,12 @@ class Directions {
northeast: LatLng(northeast['lat'], northeast['lng']), northeast: LatLng(northeast['lat'], northeast['lng']),
); );
String distance = ''; int distance = 0;
String duration = ''; int duration = 0;
if ((data['legs'] as List).isNotEmpty) { if ((data['legs'] as List).isNotEmpty) {
final leg = data['legs'][0]; final leg = data['legs'][0];
distance = leg['distance']['text']; distance = leg['distance']['value'];
duration = leg['duration']['text']; duration = leg['duration']['value'];
} }
return Directions( return Directions(

View file

@ -1,149 +1,181 @@
part of google_track_trace; part of google_track_trace;
typedef void MapCreatedCallback(TrackTraceController controller); // typedef void MapCreatedCallback(TrackTraceController controller);
enum TimePrecision { updateOnly, everySecond, everyMinute }
class GoogleTrackTraceMap extends StatefulWidget { class GoogleTrackTraceMap extends StatefulWidget {
GoogleTrackTraceMap({ GoogleTrackTraceMap({
Key? key, Key? key,
this.initialCameraPosition = const CameraPosition( required this.onMapCreated,
target: LatLng(51.965578, 6.293439),
zoom: 15.0), // doetinchem default initialCamera
this.onMapCreated,
required this.startPosition, required this.startPosition,
required this.destinationPosition, required this.destinationPosition,
this.currentPosition, required this.googleAPIKey,
required this.routeUpdateInterval,
this.timerPrecision = TimePrecision.everyMinute,
this.travelMode = TravelMode.driving, this.travelMode = TravelMode.driving,
this.routeLabel = '',
this.compassEnabled = false,
this.zoomControlsEnabled = false,
this.zoomGesturesEnabled = false,
this.mapToolbarEnabled = false,
this.mapType = MapType.normal,
this.buildingsEnabled = false,
}) : assert(true), }) : assert(true),
super(key: key); super(key: key);
CameraPosition initialCameraPosition;
/// Callback method for when the map is ready to be used. /// Callback method for when the map is ready to be used.
/// ///
/// Used to receive a [TrackTraceController] for this [GoogleTrackTraceMap]. /// Used to receive a [TrackTraceController] for this [GoogleTrackTraceMap].
/// this [TrackTraceController] also contains the [GoogleMapController] /// this [TrackTraceController] also contains the [GoogleMapController]
final MapCreatedCallback? onMapCreated; final void Function(TrackTraceController) onMapCreated;
final TravelMode travelMode; final TravelMode travelMode;
Marker? startPosition;
Marker? destinationPosition; final String routeLabel;
Marker? currentPosition;
final int routeUpdateInterval;
final TimePrecision timerPrecision;
final Marker startPosition;
final Marker destinationPosition;
final bool compassEnabled;
final bool zoomControlsEnabled;
final bool zoomGesturesEnabled;
final bool mapToolbarEnabled;
final bool buildingsEnabled;
final MapType mapType;
CameraPosition initialCameraPosition = const CameraPosition(
// doetinchem default initialCamera
target: LatLng(51.965578, 6.293439),
zoom: 12.0);
final String googleAPIKey;
@override @override
State createState() => _GoogleTrackTraceMapState(); State createState() => _GoogleTrackTraceMapState();
} }
class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> { class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
final Completer<TrackTraceController> _controller = late final TrackTraceController trackTraceController;
Completer<TrackTraceController>();
late final GoogleMapController _mapController;
bool mapLoading = true;
Directions? route; // this needs to be in the controller Directions? route;
DateTime lastRouteUpdate = DateTime.now();
@override
void initState() {
super.initState();
trackTraceController =
TrackTraceController(widget.startPosition, widget.destinationPosition);
trackTraceController.addListener(_onChange);
widget.onMapCreated(trackTraceController);
startRouteUpdateTimer();
startMarkerUpdateTimer();
}
@override
void dispose() {
trackTraceController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GoogleMap(
initialCameraPosition: calculateCameraPosition(
trackTraceController.start.position,
trackTraceController.end.position),
onMapCreated: _onMapCreated,
compassEnabled: widget.compassEnabled,
zoomControlsEnabled: widget.zoomControlsEnabled,
zoomGesturesEnabled: widget.zoomGesturesEnabled,
mapToolbarEnabled: widget.mapToolbarEnabled,
mapType: widget.mapType,
buildingsEnabled: widget.buildingsEnabled,
markers: {
// style the markers
trackTraceController.start,
trackTraceController.end,
if (trackTraceController.current != null)
trackTraceController.current!,
},
polylines: {
if (route != null)
Polyline(
polylineId: PolylineId(widget.routeLabel),
color: Theme.of(context).primaryColor,
width: 4,
points: route!.polylinePoints
.map((e) => LatLng(e.latitude, e.longitude))
.toList(),
),
});
}
void _onChange() {
setState(() {});
}
void _onMapCreated(GoogleMapController controller) { void _onMapCreated(GoogleMapController controller) {
if (mounted) { if (mounted) {
_mapController = controller; trackTraceController.mapController = controller;
controller.setMapStyle( controller.setMapStyle(
'[{"featureType": "poi","stylers": [{"visibility": "off"}]}]'); '[{"featureType": "poi","stylers": [{"visibility": "off"}]}]'); // move to dart json file
} }
} }
CameraPosition calculateCameraPosition(LatLng pointA, LatLng pointB) { CameraPosition calculateCameraPosition(LatLng pointA, LatLng pointB) {
LatLng target = LatLng((pointA.latitude + pointB.latitude) / 2, LatLng target = LatLng((pointA.latitude + pointB.latitude) / 2,
(pointA.longitude + pointB.longitude) / 2); (pointA.longitude + pointB.longitude) / 2);
double calculatedZoom = 16.0; double calculatedZoom = 13.0; // TODO calculate this zoom
return CameraPosition( return CameraPosition(
target: target, zoom: calculatedZoom, tilt: 0.0, bearing: 0.0); target: target, zoom: calculatedZoom, tilt: 0.0, bearing: 0.0);
} }
@override void startRouteUpdateTimer() {
Widget build(BuildContext context) { calculateRoute(); // run at the start
return (!mapLoading) Timer.periodic(Duration(seconds: widget.routeUpdateInterval), (timer) {
? GoogleMap(
initialCameraPosition: calculateCameraPosition(
widget.startPosition!.position,
widget.destinationPosition!.position),
onMapCreated: _onMapCreated,
compassEnabled: false,
zoomControlsEnabled: false,
mapToolbarEnabled: false,
mapType: MapType.normal,
buildingsEnabled: false,
markers: {
// style the markers
if (widget.startPosition != null) widget.startPosition!,
if (widget.destinationPosition != null)
widget.destinationPosition!,
if (widget.currentPosition != null) widget.currentPosition!,
},
polylines: {
if (route != null)
Polyline(
polylineId: const PolylineId('Route van verzorger'),
color: const Color(0xFFFF7884),
width: 3,
points: route!.polylinePoints
.map((e) => LatLng(e.latitude, e.longitude))
.toList(),
),
})
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
CircularProgressIndicator(),
Text('Map is Loading'),
],
);
}
@override
void initState() {
super.initState();
updateMaps();
}
@override
void dispose() async {
super.dispose();
TrackTraceController controller = await _controller.future;
controller.dispose();
}
void updateMaps() async {
if (route == null) {
calculateRoute(); calculateRoute();
});
}
void startMarkerUpdateTimer() {
if (widget.timerPrecision != TimePrecision.updateOnly) {
Timer.periodic(
Duration(
seconds: (widget.timerPrecision == TimePrecision.everyMinute)
? 60
: 1), (timer) {
updateDurationTimer();
});
}
}
void updateDurationTimer() {
if (route != null) {
trackTraceController.duration = route!.totalDuration -
DateTime.now().difference(lastRouteUpdate).inSeconds;
} }
// get the current GPS data from the user
// getLocationMarker().then((value) => {
// setState(() {
// _current = Marker(
// markerId: MarkerId('Huidige locatie'),
// position: LatLng(value.latitude, value.longitude),
// );
// })
// });
} }
void calculateRoute() async { void calculateRoute() async {
print('calculating route'); DirectionsRepository()
if (widget.startPosition != null && widget.destinationPosition != null) { .getDirections(
DirectionsRepository() origin: trackTraceController.start.position,
.getDirections( destination: trackTraceController.end.position,
origin: widget.startPosition!.position, mode: widget.travelMode,
destination: widget.destinationPosition!.position, key: widget.googleAPIKey,
mode: widget.travelMode, )
key: 'AIzaSyDaxZX8TeQeVf5tW-D6A66WLl20arbWV6c', .then((value) => {
) trackTraceController.duration = value.totalDuration,
.then((value) => setState(() { trackTraceController.mapController.moveCamera(CameraUpdate.newCameraPosition(calculateCameraPosition(trackTraceController.start.position, trackTraceController.end.position))),
setState(() {
lastRouteUpdate = DateTime.now();
route = value; route = value;
mapLoading = false; })
})); });
// setState(() {
// route = directions;
// mapLoading = true;
// });
}
} }
} }