Current File : /home/tradevaly/new.tradevaly.com.bd/fresh/lib/view/screens/order/widget/tracking_map_widget.dart
import 'dart:collection';
import 'dart:typed_data';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_grocery/data/model/response/config_model.dart';
import 'package:flutter_grocery/data/model/response/delivery_man_model.dart';
import 'package:flutter_grocery/data/model/response/order_model.dart';
import 'package:flutter_grocery/helper/responsive_helper.dart';
import 'package:flutter_grocery/localization/language_constrants.dart';
import 'package:flutter_grocery/provider/order_provider.dart';
import 'package:flutter_grocery/provider/splash_provider.dart';
import 'package:flutter_grocery/utill/dimensions.dart';
import 'package:flutter_grocery/utill/images.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

class TrackingMapWidget extends StatefulWidget {
  final DeliveryManModel deliveryManModel;
  final String orderID;
  final int branchID;
  final DeliveryAddress addressModel;
  TrackingMapWidget({@required this.deliveryManModel, @required this.orderID, @required this.addressModel, @required this.branchID});

  @override
  _TrackingMapWidgetState createState() => _TrackingMapWidgetState();
}

class _TrackingMapWidgetState extends State<TrackingMapWidget> {
  GoogleMapController _controller;
  bool _isLoading = true;
  Set<Marker> _markers = HashSet<Marker>();
  LatLng _deliveryBoyLatLng;
  LatLng _addressLatLng;
  LatLng _restaurantLatLng;

  @override
  void initState() {
    super.initState();

    LatLng _branch;
    for(Branches branch in Provider.of<SplashProvider>(context, listen: false).configModel.branches) {
      if(branch.id == widget.branchID) {
        _branch = LatLng(double.parse(branch.latitude), double.parse(branch.longitude));
        break;
      }
    }
    _deliveryBoyLatLng = widget.deliveryManModel != null
        ? LatLng(double.parse(widget.deliveryManModel.latitude ?? '0'), double.parse(widget.deliveryManModel.longitude ?? '0')) : LatLng(0, 0);
    _addressLatLng = widget.addressModel != null ? LatLng(double.parse(widget.addressModel.latitude), double.parse(widget.addressModel.longitude)) : LatLng(0,0);
    _restaurantLatLng = _branch;
  }

  @override
  void dispose() {
    super.dispose();

    _controller?.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final _width = MediaQuery.of(context).size.width;
    return Container(
      height: 200, width: _width - 100,
      margin: EdgeInsets.all(20),
      padding: EdgeInsets.all(Dimensions.PADDING_SIZE_EXTRA_SMALL),
      alignment: Alignment.center,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10),
      ),
      child: widget.deliveryManModel.latitude != null ? Stack(
        children: [
          GoogleMap(
            minMaxZoomPreference: MinMaxZoomPreference(0, 16),
            mapType: MapType.normal,
            initialCameraPosition: CameraPosition(target: _addressLatLng, zoom: 18),
            zoomControlsEnabled: true,
            markers: _markers,
            onMapCreated: (GoogleMapController controller) {
              _controller = controller;
              _isLoading = false;
              setMarker();
            },
            onTap: (latLng) async {
              await Provider.of<OrderProvider>(context, listen: false).getDeliveryManData(widget.orderID, context);
              String url ='https://www.google.com/maps/dir/?api=1&origin=${widget.deliveryManModel.latitude},${widget.deliveryManModel.longitude}'
                  '&destination=${_addressLatLng.latitude},${_addressLatLng.longitude}&mode=d';
              if (await canLaunchUrl(Uri.parse(url))) {
                await launchUrl(Uri.parse(url));
              } else {
                throw 'Could not launch $url';
              }
            },
          ),

          _isLoading ? Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).primaryColor))) : SizedBox(),
        ],
      ) : Text(getTranslated('no_delivery_man_data_found', context)),
    );
  }

  void setMarker() async {
    Uint8List restaurantImageData = await convertAssetToUnit8List(Images.restaurant_marker, width: 50);
    Uint8List deliveryBoyImageData = await convertAssetToUnit8List(Images.delivery_boy_marker, width: 50);
    Uint8List destinationImageData = await convertAssetToUnit8List(Images.destination_marker, width: 50);

    // Animate to coordinate
    LatLngBounds bounds;
    double _rotation = 0;
    if(_controller != null) {
      if (_addressLatLng.latitude < _restaurantLatLng.latitude) {
        bounds = LatLngBounds(southwest: _addressLatLng, northeast: _restaurantLatLng);
        _rotation = 0;
      }else {
        bounds = LatLngBounds(southwest: _restaurantLatLng, northeast: _addressLatLng);
        _rotation = 180;
      }
    }
    LatLng centerBounds = LatLng(
        (bounds.northeast.latitude + bounds.southwest.latitude)/2,
        (bounds.northeast.longitude + bounds.southwest.longitude)/2
    );

    _controller.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(target: centerBounds, zoom: 17)));
    if(ResponsiveHelper.isMobilePhone()) {
      zoomToFit(_controller, bounds, centerBounds);
    }

    // Marker
    _markers = HashSet<Marker>();
    _markers.add(Marker(
      markerId: MarkerId('destination'),
      position: _addressLatLng,
      infoWindow: InfoWindow(
        title: 'Destination',
        snippet: '${_addressLatLng.latitude}, ${_addressLatLng.longitude}',
      ),
      icon: BitmapDescriptor.fromBytes(destinationImageData),
    ));

    _markers.add(Marker(
      markerId: MarkerId('restaurant'),
      position: _restaurantLatLng,
      infoWindow: InfoWindow(
        title: 'Restaurant',
        snippet: '${_restaurantLatLng.latitude}, ${_restaurantLatLng.longitude}',
      ),
      icon: BitmapDescriptor.fromBytes(restaurantImageData),
    ));
    widget.deliveryManModel.latitude != null ? _markers.add(Marker(
      markerId: MarkerId('delivery_boy'),
      position: _deliveryBoyLatLng,
      infoWindow: InfoWindow(
        title: 'Delivery Man',
        snippet: '${_deliveryBoyLatLng.latitude}, ${_deliveryBoyLatLng.longitude}',
      ),
      rotation: _rotation,
      icon: BitmapDescriptor.fromBytes(deliveryBoyImageData),
    )) : SizedBox();

    setState(() {});
  }

  Future<void> zoomToFit(GoogleMapController controller, LatLngBounds bounds, LatLng centerBounds) async {
    bool keepZoomingOut = true;

    while(keepZoomingOut) {
      final LatLngBounds screenBounds = await controller.getVisibleRegion();
      if(fits(bounds, screenBounds)){
        keepZoomingOut = false;
        final double zoomLevel = await controller.getZoomLevel() - 0.5;
        controller.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(
          target: centerBounds,
          zoom: zoomLevel,
        )));
        break;
      }
      else {
        // Zooming out by 0.1 zoom level per iteration
        final double zoomLevel = await controller.getZoomLevel() - 0.1;
        controller.moveCamera(CameraUpdate.newCameraPosition(CameraPosition(
          target: centerBounds,
          zoom: zoomLevel,
        )));
      }
    }
  }

  bool fits(LatLngBounds fitBounds, LatLngBounds screenBounds) {
    final bool northEastLatitudeCheck = screenBounds.northeast.latitude >= fitBounds.northeast.latitude;
    final bool northEastLongitudeCheck = screenBounds.northeast.longitude >= fitBounds.northeast.longitude;

    final bool southWestLatitudeCheck = screenBounds.southwest.latitude <= fitBounds.southwest.latitude;
    final bool southWestLongitudeCheck = screenBounds.southwest.longitude <= fitBounds.southwest.longitude;

    return northEastLatitudeCheck && northEastLongitudeCheck && southWestLatitudeCheck && southWestLongitudeCheck;
  }

  Future<Uint8List> convertAssetToUnit8List(String imagePath, {int width = 50}) async {
    ByteData data = await rootBundle.load(imagePath);
    Codec codec = await instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
    FrameInfo fi = await codec.getNextFrame();
    return (await fi.image.toByteData(format: ImageByteFormat.png)).buffer.asUint8List();
  }
}