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_detail.dart'; import 'package:diplomaticquarterapp/core/service/parmacyModule/order-preview-service.dart'; import 'package:diplomaticquarterapp/locator.dart'; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.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:google_maps_flutter/google_maps_flutter.dart'; import 'package:location/location.dart'; class TrackDriver extends StatefulWidget { final OrderDetailModel order; TrackDriver({this.order}); @override State createState() => _TrackDriverState(); } class _TrackDriverState extends State { OrderPreviewService _orderServices = locator(); OrderDetailModel _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() { super.initState(); _order = widget.order; DEST_LOCATION = _order.shippingAddress.getLocation(); location = new Location(); polylinePoints = PolylinePoints(); setSourceAndDestinationIcons(); initMarkerUpdateStream(); startUpdatingDriverLocation(); } @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, ), onTap: onSourceMarkerTap ); _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: CameraPosition(target: DEST_LOCATION, zoom: 4), 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(){ if(DEST_LOCATION != null){ final CameraPosition orderDeliveryLocCamera = CameraPosition( bearing: CAMERA_BEARING, target: DEST_LOCATION, tilt: CAMERA_TILT, zoom: CAMERA_ZOOM); return orderDeliveryLocCamera; } return null; } CameraPosition _driverLocationCamera(){ if(DEST_LOCATION != null) { final CameraPosition driverLocCamera = CameraPosition( bearing: CAMERA_BEARING, target: SOURCE_LOCATION, tilt: CAMERA_TILT, zoom: CAMERA_ZOOM); return driverLocCamera; } return null; } 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)); } 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, infoWindow: InfoWindow(title: TranslationBase.of(context).driver), onTap: onSourceMarkerTap )); }); } // 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, infoWindow: InfoWindow(title: TranslationBase.of(context).deliveryLocation), onTap: onDestinationMarkerTap )); }); } // set the route lines on the map from source to destination // for more info follow this tutorial // drawRoute(); } void updatePinOnMap() async { _latLngStream.addLatLng(LatLngInfo(SOURCE_LOCATION.latitude, SOURCE_LOCATION.longitude, "sourcePin")); 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{ if(isLocationUpdating){ await Future.delayed(Duration(seconds: frequencyInSeconds)); showLoading(); LatLng driverLocation = (await _orderServices.getDriverLocation(driverId)); hideLoading(); if(driverLocation != null){ if(SOURCE_LOCATION == null || DEST_LOCATION == null){ SOURCE_LOCATION = driverLocation; DEST_LOCATION = _order.shippingAddress.getLocation(); showPinsOnMap(); } SOURCE_LOCATION = driverLocation; updatePinOnMap(); updateMapCamera(); }else{ GifLoaderDialogUtils.hideDialog(context); } } return isLocationUpdating; }); } showLoading(){ if(SOURCE_LOCATION == null){ GifLoaderDialogUtils.showMyDialog(context); } } hideLoading(){ if(SOURCE_LOCATION == null){ GifLoaderDialogUtils.hideDialog(context); } } 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; } updateMapCamera() async{ if(SOURCE_LOCATION != null && DEST_LOCATION != null){ // 'package:google_maps_flutter_platform_interface/src/types/location.dart': Failed assertion: line 72 pos 16: 'southwest.latitude <= northeast.latitude': is not true. LatLngBounds bound; if(SOURCE_LOCATION.latitude <= DEST_LOCATION.latitude){ bound = LatLngBounds(southwest: SOURCE_LOCATION, northeast: DEST_LOCATION); }else{ bound = LatLngBounds(southwest: DEST_LOCATION, northeast: SOURCE_LOCATION); } if(bound == null) return; CameraUpdate camera = CameraUpdate.newLatLngBounds(bound, 50); final GoogleMapController controller = await _controller.future; controller.animateCamera(camera); } } bool showSrcMarkerTitle = false; onSourceMarkerTap() async{ // showSrcMarkerTitle = !showSrcMarkerTitle; } bool showDestMarkerTitle = false; onDestinationMarkerTap() async{ // showDestMarkerTitle = !showDestMarkerTitle; // Marker m = _markers.firstWhere((m) => m.markerId.value == 'destPin'); // if(showDestMarkerTitle){ // } } }