diff --git a/example/lib/main.dart b/example/lib/main.dart index 6351ec3..ffca333 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,6 @@ +import 'dart:async'; +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_track_trace/google_track_trace.dart'; @@ -10,11 +13,30 @@ class TrackTraceDemo extends StatefulWidget { } class _TrackTraceDemoState extends State { - 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 Widget build(BuildContext context) { 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( startPosition: const Marker( markerId: MarkerId('Start locatie'), @@ -24,14 +46,62 @@ class _TrackTraceDemoState extends State { markerId: MarkerId('Eind locatie'), position: LatLng(51.958996, 6.296520), ), - travelMode: TravelMode.bicycling, - onMapCreated: (ctr) => {controller = ctr}, + googleAPIKey: 'AIzaSyDaxZX8TeQeVf5tW-D6A66WLl20arbWV6c', + 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() { - runApp(const MaterialApp(home: TrackTraceDemo( - ))); + runApp(const MaterialApp(home: TrackTraceDemo())); } diff --git a/lib/google_track_trace.dart b/lib/google_track_trace.dart index a6a2679..c3658ed 100644 --- a/lib/google_track_trace.dart +++ b/lib/google_track_trace.dart @@ -3,6 +3,7 @@ library google_track_trace; import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 5d985db..e3299b3 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -1,24 +1,48 @@ part of google_track_trace; -class TrackTraceController { - late final Completer _mapController; +class TrackTraceController extends ChangeNotifier { + 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 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; } - Completer getController() { - return _mapController; - } + GoogleMapController get mapController => _mapController; } diff --git a/lib/src/directions_controller.dart b/lib/src/directions_controller.dart index 43f5a32..33cfe48 100644 --- a/lib/src/directions_controller.dart +++ b/lib/src/directions_controller.dart @@ -30,7 +30,6 @@ class DirectionsRepository { HttpHeaders.contentTypeHeader: 'application/json', }); if (response.statusCode == 200) { - print(response.body); return Directions.fromMap(jsonDecode(response.body)); } } on HttpException catch (e) { @@ -43,8 +42,8 @@ class DirectionsRepository { class Directions { final LatLngBounds bounds; final List polylinePoints; - final String totalDistance; - final String totalDuration; + final int totalDistance; + final int totalDuration; const Directions({ required this.bounds, @@ -67,12 +66,12 @@ class Directions { northeast: LatLng(northeast['lat'], northeast['lng']), ); - String distance = ''; - String duration = ''; + int distance = 0; + int duration = 0; if ((data['legs'] as List).isNotEmpty) { final leg = data['legs'][0]; - distance = leg['distance']['text']; - duration = leg['duration']['text']; + distance = leg['distance']['value']; + duration = leg['duration']['value']; } return Directions( diff --git a/lib/src/google_map.dart b/lib/src/google_map.dart index 6315d47..6ba3679 100644 --- a/lib/src/google_map.dart +++ b/lib/src/google_map.dart @@ -1,149 +1,181 @@ part of google_track_trace; -typedef void MapCreatedCallback(TrackTraceController controller); +// typedef void MapCreatedCallback(TrackTraceController controller); +enum TimePrecision { updateOnly, everySecond, everyMinute } class GoogleTrackTraceMap extends StatefulWidget { GoogleTrackTraceMap({ Key? key, - this.initialCameraPosition = const CameraPosition( - target: LatLng(51.965578, 6.293439), - zoom: 15.0), // doetinchem default initialCamera - this.onMapCreated, + required this.onMapCreated, required this.startPosition, required this.destinationPosition, - this.currentPosition, + required this.googleAPIKey, + required this.routeUpdateInterval, + this.timerPrecision = TimePrecision.everyMinute, 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), super(key: key); - CameraPosition initialCameraPosition; - /// 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] - final MapCreatedCallback? onMapCreated; + final void Function(TrackTraceController) onMapCreated; final TravelMode travelMode; - Marker? startPosition; - Marker? destinationPosition; - Marker? currentPosition; + + final String routeLabel; + + 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 State createState() => _GoogleTrackTraceMapState(); } class _GoogleTrackTraceMapState extends State { - final Completer _controller = - Completer(); - late final GoogleMapController _mapController; - bool mapLoading = true; + late final TrackTraceController trackTraceController; - 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) { if (mounted) { - _mapController = controller; + trackTraceController.mapController = controller; controller.setMapStyle( - '[{"featureType": "poi","stylers": [{"visibility": "off"}]}]'); + '[{"featureType": "poi","stylers": [{"visibility": "off"}]}]'); // move to dart json file } } CameraPosition calculateCameraPosition(LatLng pointA, LatLng pointB) { LatLng target = LatLng((pointA.latitude + pointB.latitude) / 2, (pointA.longitude + pointB.longitude) / 2); - double calculatedZoom = 16.0; + double calculatedZoom = 13.0; // TODO calculate this zoom + return CameraPosition( target: target, zoom: calculatedZoom, tilt: 0.0, bearing: 0.0); } - @override - Widget build(BuildContext context) { - return (!mapLoading) - ? 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) { + void startRouteUpdateTimer() { + calculateRoute(); // run at the start + Timer.periodic(Duration(seconds: widget.routeUpdateInterval), (timer) { 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 { - print('calculating route'); - if (widget.startPosition != null && widget.destinationPosition != null) { - DirectionsRepository() - .getDirections( - origin: widget.startPosition!.position, - destination: widget.destinationPosition!.position, - mode: widget.travelMode, - key: 'AIzaSyDaxZX8TeQeVf5tW-D6A66WLl20arbWV6c', - ) - .then((value) => setState(() { + DirectionsRepository() + .getDirections( + origin: trackTraceController.start.position, + destination: trackTraceController.end.position, + mode: widget.travelMode, + key: widget.googleAPIKey, + ) + .then((value) => { + trackTraceController.duration = value.totalDuration, + trackTraceController.mapController.moveCamera(CameraUpdate.newCameraPosition(calculateCameraPosition(trackTraceController.start.position, trackTraceController.end.position))), + setState(() { + lastRouteUpdate = DateTime.now(); route = value; - mapLoading = false; - })); - // setState(() { - // route = directions; - // mapLoading = true; - // }); - } + }) + }); } }