import 'dart:async'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:async/async.dart'; import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/core/model/pharmacies/order_model.dart'; import 'package:diplomaticquarterapp/core/service/parmacyModule/order-preview-service.dart'; import 'package:diplomaticquarterapp/locator.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_animarker/lat_lng_interpolation.dart'; import 'package:flutter_animarker/models/lat_lng_delta.dart'; import 'package:flutter_animarker/models/lat_lng_info.dart'; import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:location/location.dart'; import 'package:flutter_animarker/streams/lat_lng_stream.dart'; class TrackDriver extends StatefulWidget { final OrderModel order; TrackDriver({this.order}); @override State createState() => _TrackDriverState(); } class _TrackDriverState extends State { OrderPreviewService _orderServices = locator(); OrderModel _order; Completer _controller = Completer(); double CAMERA_ZOOM = 14; double CAMERA_TILT = 0; double CAMERA_BEARING = 30; LatLng SOURCE_LOCATION = null; LatLng DEST_LOCATION = null; // for my drawn routes on the map Set _polylines = Set(); List polylineCoordinates = []; PolylinePoints polylinePoints; Set _markers = Set(); BitmapDescriptor sourceIcon; // for my custom marker pins BitmapDescriptor destinationIcon; // for my custom marker pins Location location;// wrapper around the location API int locationUpdateFreq = 2; LatLngInterpolationStream _latLngStream; StreamGroup subscriptions; @override void initState() { _order = widget.order; DEST_LOCATION = _order.shippingAddress.getLocation(); location = new Location(); polylinePoints = PolylinePoints(); setSourceAndDestinationIcons(); initMarkerUpdateStream(); startUpdatingDriverLocation(); super.initState(); } @override void dispose() { super.dispose(); subscriptions.close(); _latLngStream.cancel(); stopUpdatingDriverLocation(); } initMarkerUpdateStream(){ _latLngStream = LatLngInterpolationStream(movementDuration: Duration(seconds: locationUpdateFreq+1)); subscriptions = StreamGroup(); subscriptions.add(_latLngStream.getAnimatedPosition('sourcePin')); subscriptions.stream.listen((LatLngDelta delta) { //Update the marker with animation setState(() { //Get the marker Id for this animation var markerId = MarkerId(delta.markerId); Marker sourceMarker = Marker( markerId: markerId, // rotation: delta.rotation, icon: sourceIcon, position: LatLng( delta.from.latitude, delta.from.longitude, ), ); _markers.removeWhere((m) => m.markerId.value == 'sourcePin'); _markers.add(sourceMarker); }); }); } @override Widget build(BuildContext context) { return AppScaffold( appBarTitle: TranslationBase.of(context).deliveryDriverTrack, isShowAppBar: true, isPharmacy: true, showPharmacyCart: false, showHomeAppBarIcon: false, body: GoogleMap( myLocationEnabled: true, compassEnabled: true, markers: _markers, polylines: _polylines, mapType: MapType.normal, initialCameraPosition: _orderDeliveryLocationCamera(), onMapCreated: (GoogleMapController controller) { _controller.complete(controller); showPinsOnMap(); }, ), // floatingActionButton: FloatingActionButton.extended( // onPressed: _goToDriver, // label: Text('To the lake!'), // icon: Icon(Icons.directions_boat), // ), ); } void setSourceAndDestinationIcons() async { final Uint8List srcMarkerBytes = await getBytesFromAsset('assets/images/map_markers/source_map_marker.png', getMarkerIconSize()); final Uint8List destMarkerBytes = await getBytesFromAsset('assets/images/map_markers/destination_map_marker.png', getMarkerIconSize()); sourceIcon = await BitmapDescriptor.fromBytes(srcMarkerBytes); destinationIcon = await BitmapDescriptor.fromBytes(destMarkerBytes); } CameraPosition _orderDeliveryLocationCamera(){ final CameraPosition orderDeliveryLocCamera = CameraPosition( bearing: CAMERA_BEARING, target: DEST_LOCATION, tilt: CAMERA_TILT, zoom: CAMERA_ZOOM); return orderDeliveryLocCamera; } CameraPosition _driverLocationCamera(){ final CameraPosition driverLocCamera = CameraPosition( bearing: CAMERA_BEARING, target: SOURCE_LOCATION, tilt: CAMERA_TILT, zoom: CAMERA_ZOOM); return driverLocCamera; } Future _goToOrderDeliveryLocation() async { final GoogleMapController controller = await _controller.future; final CameraPosition orderDeliveryLocCamera = _orderDeliveryLocationCamera(); controller.animateCamera(CameraUpdate.newCameraPosition(orderDeliveryLocCamera)); } Future _goToDriver() async { final GoogleMapController controller = await _controller.future; final CameraPosition driverLocCamera = _driverLocationCamera(); controller.animateCamera(CameraUpdate.newCameraPosition(driverLocCamera)); } Future _fitCameraBetweenBothPoints() async { final GoogleMapController controller = await _controller.future; final CameraPosition driverLocCamera = CameraPosition( bearing: CAMERA_BEARING, target: SOURCE_LOCATION, tilt: CAMERA_TILT, zoom: CAMERA_ZOOM); controller.animateCamera(CameraUpdate.newCameraPosition(driverLocCamera)); } void showPinsOnMap() { // source pin if(SOURCE_LOCATION != null){ setState(() { var pinPosition = SOURCE_LOCATION; _markers.removeWhere((m) => m.markerId.value == 'sourcePin'); _markers.add(Marker( markerId: MarkerId('sourcePin'), position: pinPosition, icon: sourceIcon )); }); } // destination pin if(DEST_LOCATION != null){ setState(() { var destPosition = DEST_LOCATION; _markers.removeWhere((m) => m.markerId.value == 'destPin'); _markers.add(Marker( markerId: MarkerId('destPin'), position: destPosition, icon: destinationIcon )); }); } // set the route lines on the map from source to destination // for more info follow this tutorial // drawRoute(); } void updatePinOnMap() async { // create a new CameraPosition instance // every time the location changes, so the camera // follows the pin as it moves with an animation CameraPosition cPosition = CameraPosition( zoom: CAMERA_ZOOM, tilt: CAMERA_TILT, bearing: CAMERA_BEARING, target: SOURCE_LOCATION, ); final GoogleMapController controller = await _controller.future; controller.animateCamera(CameraUpdate.newCameraPosition(cPosition)); _latLngStream.addLatLng(LatLngInfo(SOURCE_LOCATION.latitude, SOURCE_LOCATION.longitude, "sourcePin")); // do this inside the setState() so Flutter gets notified // that a widget update is due // setState(() { // // updated position // var pinPosition = SOURCE_LOCATION; // // // the trick is to remove the marker (by id) // // and add it again at the updated location // _markers.removeWhere((m) => m.markerId.value == 'sourcePin'); // _markers.add(Marker( // markerId: MarkerId('sourcePin'), // position: pinPosition, // updated position // icon: sourceIcon // )); // }); drawRoute(); } void drawRoute() async { return; // Ignore draw Route List result = await polylinePoints.getRouteBetweenCoordinates( GOOGLE_API_KEY, SOURCE_LOCATION.latitude, SOURCE_LOCATION.longitude, DEST_LOCATION.latitude, DEST_LOCATION.longitude); if(result.isNotEmpty){ result.forEach((PointLatLng point){ polylineCoordinates.add( LatLng(point.latitude,point.longitude) ); }); setState(() { _polylines.add(Polyline( width: 5, // set the width of the polylines polylineId: PolylineId('poly'), color: Color.fromARGB(255, 40, 122, 198), points: polylineCoordinates )); }); } } bool isLocationUpdating = false; startUpdatingDriverLocation({int frequencyInSeconds = 2}) async{ isLocationUpdating = true; int driverId = int.tryParse(_order.driverID); Future.doWhile(() async{ await Future.delayed(Duration(seconds: frequencyInSeconds)); if(isLocationUpdating){ LatLng driverLocation = (await _orderServices.getDriverLocation(driverId)); if(driverLocation != null){ if(SOURCE_LOCATION == null || DEST_LOCATION == null){ SOURCE_LOCATION = driverLocation; DEST_LOCATION = _order.shippingAddress.getLocation(); showPinsOnMap(); } SOURCE_LOCATION = driverLocation; updatePinOnMap(); } } return isLocationUpdating; }); } stopUpdatingDriverLocation(){ isLocationUpdating = false; } Future getBytesFromAsset(String path, int width) async { ByteData data = await rootBundle.load(path); ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width); ui.FrameInfo fi = await codec.getNextFrame(); return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List(); } int getMarkerIconSize(){ return 140; } }