diff --git a/lib/config/config.dart b/lib/config/config.dart index be5dd440..cf1836ac 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -26,6 +26,10 @@ const GET_PROJECT = 'Services/Lists.svc/REST/GetProject'; ///Geofencing const GET_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_GetAllPoints'; const LOG_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_InsertPatientFileInfo'; + +// Delivery Driver +const DRIVER_LOCATION = 'Services/Patients.svc/REST/PatientER_GetDriverLocation'; + //weather const WEATHER_INDICATOR = 'Services/Weather.svc/REST/GetCityInfo'; diff --git a/lib/core/model/pharmacies/order_model.dart b/lib/core/model/pharmacies/order_model.dart index 8872aaf8..776c21ec 100644 --- a/lib/core/model/pharmacies/order_model.dart +++ b/lib/core/model/pharmacies/order_model.dart @@ -73,6 +73,8 @@ class OrderModel { this.preferDeliveryDate, this.preferDeliveryTime, this.preferDeliveryTimen, + this.driverID, + this.driverOTP, }); String id; @@ -138,6 +140,8 @@ class OrderModel { DateTime preferDeliveryDate; PreferDeliveryTime preferDeliveryTime; PreferDeliveryTimen preferDeliveryTimen; + String driverOTP; + String driverID; factory OrderModel.fromJson(Map json) => OrderModel( id: json["id"], @@ -203,6 +207,11 @@ class OrderModel { preferDeliveryDate: json["prefer_delivery_date"] == null ? null : DateTime.parse(json["prefer_delivery_date"]), preferDeliveryTime: json["prefer_delivery_time"] == null ? null : preferDeliveryTimeValues.map[json["prefer_delivery_time"]], preferDeliveryTimen: json["prefer_delivery_timen"] == null ? null : preferDeliveryTimenValues.map[json["prefer_delivery_timen"]], + + // Driver Detail + driverID: json["DriverID"], + driverOTP: json["DriverOTP"], + ); Map toJson() => { @@ -1364,7 +1373,7 @@ final titleValues = EnumValues({ "ممتاز": Title.TITLE }); -enum OrderStatus { ORDER_SUBMITTED, PENDING, ORDER_IN_PROGRESS, ORDER_COMPLETED, CANCELLED, PROCESSING, ORDER_REFUNDED, COMPLETE } +enum OrderStatus { ORDER_SUBMITTED, PENDING, ORDER_IN_PROGRESS,ORDER_SENT_FOR_DELIVERY, ORDER_COMPLETED, CANCELLED, PROCESSING, ORDER_REFUNDED, COMPLETE } final orderStatusValues = EnumValues({ "Cancelled": OrderStatus.CANCELLED, @@ -1374,7 +1383,8 @@ final orderStatusValues = EnumValues({ "OrderRefunded": OrderStatus.ORDER_REFUNDED, "OrderSubmitted": OrderStatus.ORDER_SUBMITTED, "Pending": OrderStatus.PENDING, - "Processing": OrderStatus.PROCESSING + "Processing": OrderStatus.PROCESSING, + "orderSentForDelivery": OrderStatus.ORDER_SENT_FOR_DELIVERY }); enum OrderStatusn { ORDER_SUBMITTED, EMPTY, ORDER_IN_PROGRESS, ORDER_COMPLETED, ORDER_STATUSN, PURPLE, FLUFFY, TENTACLED } @@ -1387,6 +1397,7 @@ final orderStatusnValues = EnumValues({ "ملغي": OrderStatusn.ORDER_STATUSN, "Order Submitted": OrderStatusn.ORDER_SUBMITTED, "قيد التنفيذ": OrderStatusn.PURPLE, + "مكتمل": OrderStatusn.TENTACLED, "مكتمل": OrderStatusn.TENTACLED }); diff --git a/lib/core/service/parmacyModule/order-preview-service.dart b/lib/core/service/parmacyModule/order-preview-service.dart index 18cfa87e..8bfda569 100644 --- a/lib/core/service/parmacyModule/order-preview-service.dart +++ b/lib/core/service/parmacyModule/order-preview-service.dart @@ -6,6 +6,8 @@ import 'package:diplomaticquarterapp/core/model/pharmacies/ShoppingCart.dart'; import 'package:diplomaticquarterapp/core/model/pharmacies/payment-checkout-data.dart'; import 'package:diplomaticquarterapp/core/service/base_service.dart'; import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/OrderPreviewViewModel.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; class OrderPreviewService extends BaseService { bool isFinished = true; @@ -245,4 +247,26 @@ class OrderPreviewService extends BaseService { return ""; } } + + Future getDriverLocation(dynamic driverId) async{ + Map jsonBody = Map(); + jsonBody['DriverID'] = driverId; + + LatLng coordinates; + await baseAppClient.post(DRIVER_LOCATION, + onSuccess: (response, statusCode) async { + if(statusCode == 200){ + dynamic locationObject = response['PatientER_GetDriverLocationList'][0]; + double lat = locationObject['Latitude']; + double lon = locationObject['Longitude']; + if(lat != null && lon != null){ + coordinates = LatLng(lat,lon); + } + } + }, onFailure: (String error, int statusCode) { + + }, body: jsonBody); + + return coordinates; + } } diff --git a/lib/core/viewModels/pharmacyModule/order_model_view_model.dart b/lib/core/viewModels/pharmacyModule/order_model_view_model.dart index f434db54..90881d7e 100644 --- a/lib/core/viewModels/pharmacyModule/order_model_view_model.dart +++ b/lib/core/viewModels/pharmacyModule/order_model_view_model.dart @@ -50,7 +50,7 @@ class OrderModelViewModel extends BaseViewModel { } } - Future getOrderDetails(OrderId) async { + Future getOrderDetails(OrderId) async { setState(ViewState.Busy); await _orderDetailsService.getOrderDetails(OrderId); if (_orderDetailsService.hasError) { @@ -59,6 +59,7 @@ class OrderModelViewModel extends BaseViewModel { } else { setState(ViewState.Idle); } + return _orderDetailsService.orderList.first; } Future getProductReview() async { diff --git a/lib/pages/pharmacy/order/OrderDetails.dart b/lib/pages/pharmacy/order/OrderDetails.dart index 670cb951..f3ee5447 100644 --- a/lib/pages/pharmacy/order/OrderDetails.dart +++ b/lib/pages/pharmacy/order/OrderDetails.dart @@ -48,7 +48,7 @@ class _OrderDetailsPageState extends State { var model; var isCancel = false; var isRefund = false; - var isActiveDelivery = true; + var isActiveDelivery = false; var dataIsCancel; var dataIsRefund; @@ -66,7 +66,13 @@ class _OrderDetailsPageState extends State { @override Widget build(BuildContext context) { return BaseView( - onModelReady: (model) => model.getOrderDetails(widget.orderModel.id), + onModelReady: (model){ + model.getOrderDetails(widget.orderModel.id).then((value){ + setState(() { + isActiveDelivery = (value.orderStatusId == 995 && (value.driverID != null && value.driverID.isNotEmpty)); + }); + }); + }, builder: (_, model, wi) => AppScaffold( appBarTitle: TranslationBase.of(context).orderDetail, isShowAppBar: true, @@ -596,10 +602,10 @@ class _OrderDetailsPageState extends State { isActiveDelivery ? InkWell( onTap: () { - // Navigator.push( - // context, - // MaterialPageRoute(builder: (context) => TrackDriver(order: widget.orderModel), - // )); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => TrackDriver(order: model.orderListModel.first), + )); }, child: Container( height: 50.0, diff --git a/lib/pages/pharmacy/order/TrackDriver.dart b/lib/pages/pharmacy/order/TrackDriver.dart index b9a8056f..5a2e20c8 100644 --- a/lib/pages/pharmacy/order/TrackDriver.dart +++ b/lib/pages/pharmacy/order/TrackDriver.dart @@ -1,12 +1,19 @@ import 'dart:async'; +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:flutter/material.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; @@ -17,12 +24,13 @@ class TrackDriver extends StatefulWidget { } class _TrackDriverState extends State { + OrderPreviewService _orderServices = locator(); OrderModel _order; Completer _controller = Completer(); - double CAMERA_ZOOM = 16; + double CAMERA_ZOOM = 14; double CAMERA_TILT = 0; double CAMERA_BEARING = 30; LatLng SOURCE_LOCATION = null; @@ -39,17 +47,63 @@ class _TrackDriverState extends State { 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 new Scaffold( body: GoogleMap( myLocationEnabled: true, @@ -63,11 +117,11 @@ class _TrackDriverState extends State { showPinsOnMap(); }, ), - floatingActionButton: FloatingActionButton.extended( - onPressed: _goToDriver, - label: Text('To the lake!'), - icon: Icon(Icons.directions_boat), - ), + // floatingActionButton: FloatingActionButton.extended( + // onPressed: _goToDriver, + // label: Text('To the lake!'), + // icon: Icon(Icons.directions_boat), + // ), ); } @@ -130,6 +184,7 @@ class _TrackDriverState extends State { if(SOURCE_LOCATION != null){ setState(() { var pinPosition = SOURCE_LOCATION; + _markers.removeWhere((m) => m.markerId.value == 'sourcePin'); _markers.add(Marker( markerId: MarkerId('sourcePin'), position: pinPosition, @@ -142,6 +197,7 @@ class _TrackDriverState extends State { if(DEST_LOCATION != null){ setState(() { var destPosition = DEST_LOCATION; + _markers.removeWhere((m) => m.markerId.value == 'destPin'); _markers.add(Marker( markerId: MarkerId('destPin'), position: destPosition, @@ -166,21 +222,26 @@ class _TrackDriverState extends State { ); 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 - )); - }); + // 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 { @@ -208,4 +269,33 @@ class _TrackDriverState extends State { }); } } + + 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; + } } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 2c07d0c9..3b44f01f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: # http client http: ^0.12.1 connectivity: ^0.4.9+3 + async: ^2.4.2 # State Management provider: ^4.3.2+2 @@ -170,6 +171,9 @@ dependencies: # Dep by Zohaib shimmer: ^1.1.2 + # Marker Animation + flutter_animarker: ^1.0.0 + dev_dependencies: flutter_test: sdk: flutter