You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
11 KiB
Dart
345 lines
11 KiB
Dart
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/model/pharmacies/order_model.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: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 OrderDetailModel order;
|
|
TrackDriver({this.order});
|
|
|
|
@override
|
|
State<TrackDriver> createState() => _TrackDriverState();
|
|
}
|
|
|
|
class _TrackDriverState extends State<TrackDriver> {
|
|
OrderPreviewService _orderServices = locator<OrderPreviewService>();
|
|
OrderDetailModel _order;
|
|
|
|
Completer<GoogleMapController> _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<Polyline> _polylines = Set<Polyline>();
|
|
List<LatLng> polylineCoordinates = [];
|
|
PolylinePoints polylinePoints;
|
|
|
|
Set<Marker> _markers = Set<Marker>();
|
|
|
|
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<LatLngDelta> 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<LatLngDelta>();
|
|
|
|
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<void> _goToOrderDeliveryLocation() async {
|
|
final GoogleMapController controller = await _controller.future;
|
|
final CameraPosition orderDeliveryLocCamera = _orderDeliveryLocationCamera();
|
|
controller.animateCamera(CameraUpdate.newCameraPosition(orderDeliveryLocCamera));
|
|
}
|
|
|
|
Future<void> _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<PointLatLng> 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<Uint8List> 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){
|
|
// }
|
|
}
|
|
}
|