mirror of
https://github.com/Iconica-Development/flutter_google_track_and_trace.git
synced 2025-05-19 05:03:45 +02:00
working track&trace demo
This commit is contained in:
parent
65262045f8
commit
a39de81555
5 changed files with 252 additions and 126 deletions
|
@ -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<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
|
||||
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<TrackTraceDemo> {
|
|||
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()));
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -1,24 +1,48 @@
|
|||
part of google_track_trace;
|
||||
|
||||
class TrackTraceController {
|
||||
late final Completer<GoogleMapController> _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<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;
|
||||
}
|
||||
|
||||
Completer<GoogleMapController> getController() {
|
||||
return _mapController;
|
||||
}
|
||||
GoogleMapController get mapController => _mapController;
|
||||
}
|
||||
|
|
|
@ -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<PointLatLng> 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(
|
||||
|
|
|
@ -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<GoogleTrackTraceMap> {
|
||||
final Completer<TrackTraceController> _controller =
|
||||
Completer<TrackTraceController>();
|
||||
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;
|
||||
// });
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue