Search code examples
fluttergoogle-mapspolylinegoogle-drive-realtime-apigoogle-polyline

Update polyline route with user's live location flutter


When i update the polyline code, on location change method, It gives me additional straight line. I want to update my polyline when user move from one source to destination. I am able to create a polyline route but i want that route become shorter with the location of the user. So i am replacing the source location with user's current location. And if user change takes different turn than the polyline should update as per it happens in google map apps.

below is the code.

import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'package:flutter/material.dart';
import 'package:routelines/components/map_pin_pill.dart';
import 'dart:async';
import 'package:routelines/models/pin_pill_info.dart';

const double CAMERA_ZOOM = 16;
const double CAMERA_TILT = 80;
const double CAMERA_BEARING = 30;

const LatLng SOURCE_LOCATION = LatLng(22.993966, 72.498510);
const LatLng DEST_LOCATION = LatLng(23.014672, 72.530558);

class DirectionPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => DirectionPageState();
}

class DirectionPageState extends State<DirectionPage> {
  Completer<GoogleMapController> _controller = Completer();
  Set<Marker> _markers = Set<Marker>();
  Set<Polyline> _polylines = Set<Polyline>();
  List<LatLng> polylineCoordinates = [];
  PolylinePoints polylinePoints;
  String googleAPIKey = "<YourKey>";
  BitmapDescriptor sourceIcon;
  BitmapDescriptor destinationIcon;
  LocationData currentLocation;
  LocationData destinationLocation;
  Location location;
  double pinPillPosition = -100;
  int polyId = 1;
  PinInformation currentlySelectedPin = PinInformation(
      pinPath: '',
      avatarPath: '',
      location: LatLng(0, 0),
      locationName: '',
      labelColor: Colors.grey);
  PinInformation sourcePinInfo;
  PinInformation destinationPinInfo;

  @override
  void initState() {
    super.initState();
    location = new Location();
    setInitialLocation();
    polylinePoints = PolylinePoints();
    location.onLocationChanged().listen((LocationData cLoc) {
      currentLocation = cLoc;
      updatePinOnMap();
      setPolylines();
    });
    setSourceAndDestinationIcons();
  }

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

  void setSourceAndDestinationIcons() async {
    BitmapDescriptor.fromAssetImage(
            ImageConfiguration(devicePixelRatio: 2.0), 'assets/driving_pin.png')
        .then((onValue) {
      sourceIcon = onValue;
    });

    BitmapDescriptor.fromAssetImage(ImageConfiguration(devicePixelRatio: 2.0),
            'assets/destination_map_marker.png')
        .then((onValue) {
      destinationIcon = onValue;
    });
  }

  void setInitialLocation() async {
    currentLocation = await location.getLocation();
    destinationLocation = LocationData.fromMap({
      "latitude": DEST_LOCATION.latitude,
      "longitude": DEST_LOCATION.longitude
    });
  }

  @override
  Widget build(BuildContext context) {
    CameraPosition initialCameraPosition = CameraPosition(
        zoom: CAMERA_ZOOM,
        tilt: CAMERA_TILT,
        bearing: CAMERA_BEARING,
        target: SOURCE_LOCATION);
    if (currentLocation != null) {
      initialCameraPosition = CameraPosition(
          target: LatLng(currentLocation.latitude, currentLocation.longitude),
          zoom: CAMERA_ZOOM,
          tilt: CAMERA_TILT,
          bearing: CAMERA_BEARING);
    }
    return Scaffold(
      body: Stack(
        children: <Widget>[
          GoogleMap(
              myLocationEnabled: true,
              compassEnabled: true,
              tiltGesturesEnabled: false,
              markers: _markers,
              polylines: _polylines,
              mapType: MapType.normal,
              initialCameraPosition: initialCameraPosition,
              onTap: (LatLng loc) {
              },
              onMapCreated: (GoogleMapController controller) {
                controller.setMapStyle(Utils.mapStyles);
                _controller.complete(controller);
                print("map opened");
                showPinsOnMap();
              }),
          MapPinPillComponent(
              pinPillPosition: pinPillPosition,
              currentlySelectedPin: currentlySelectedPin)
        ],
      ),
    );
  }

  void showPinsOnMap() {
    if (currentLocation != null && destinationLocation != null) {
      var pinPosition =
          LatLng(currentLocation.latitude, currentLocation.longitude);
      // print("pinPosition >> $pinPosition");
      var destPosition =
          LatLng(destinationLocation.latitude, destinationLocation.longitude);

      sourcePinInfo = PinInformation(
          locationName: "Start Location",
          location: SOURCE_LOCATION,
          pinPath: "assets/driving_pin.png",
          avatarPath: "assets/square_pin.png",
          labelColor: Colors.blueAccent);

      destinationPinInfo = PinInformation(
          locationName: "End Location",
          location: DEST_LOCATION,
          pinPath: "assets/destination_map_marker.png",
          avatarPath: "assets/square_pin.png",
          labelColor: Colors.purple);

      _markers.add(Marker(
          markerId: MarkerId('sourcePin'),
          position: pinPosition,
          onTap: () {
            setState(() {
              currentlySelectedPin = sourcePinInfo;
              pinPillPosition = 0;
            });
          },
          icon: sourceIcon));
      // destination pin
      _markers.add(Marker(
          markerId: MarkerId('destPin'),
          position: destPosition,
          onTap: () {
            setState(() {
              currentlySelectedPin = destinationPinInfo;
              pinPillPosition = 0;
            });
          },
          icon: destinationIcon));
      setPolylines();
    }
  }

  void setPolylines() async {
    List<PointLatLng> result = await polylinePoints.getRouteBetweenCoordinates(
        googleAPIKey,
        currentLocation.latitude,
        currentLocation.longitude,
        destinationLocation.latitude,
        destinationLocation.longitude);

    if (result.isNotEmpty) {
      result.forEach((PointLatLng point) {
        polylineCoordinates.add(LatLng(point.latitude, point.longitude));
      });
      final String polylineIdVal = 'polyline_id_$polyId';
      polyId++;
      final PolylineId polylineId = PolylineId(polylineIdVal);
      _polylines = new Set<Polyline>();
      setState(() {
        _polylines.add( Polyline(
            width: 6,
            // set the width of the polylines
            polylineId: polylineId,
            geodesic: true,
            consumeTapEvents: true,
            color: Colors.blue,
            points: polylineCoordinates));
      });
    }
  }

  void updatePinOnMap() async {
    if (currentLocation != null && destinationLocation != null) {
      var pinPosition =
          LatLng(currentLocation.latitude, currentLocation.longitude);
      // print("pinPosition >> $pinPosition");
      var destPosition =
          LatLng(destinationLocation.latitude, destinationLocation.longitude);

      sourcePinInfo = PinInformation(
          locationName: "Start Location",
          location: SOURCE_LOCATION,
          pinPath: "assets/driving_pin.png",
          avatarPath: "assets/square_pin.png",
          labelColor: Colors.blueAccent);

      destinationPinInfo = PinInformation(
          locationName: "End Location",
          location: DEST_LOCATION,
          pinPath: "assets/destination_map_marker.png",
          avatarPath: "assets/square_pin.png",
          labelColor: Colors.purple);

      _markers.add(Marker(
          markerId: MarkerId('sourcePin'),
          position: pinPosition,
          onTap: () {
            setState(() {
              currentlySelectedPin = sourcePinInfo;
              pinPillPosition = 0;
            });
          },
          icon: sourceIcon));
      // destination pin
      _markers.add(Marker(
          markerId: MarkerId('destPin'),
          position: destPosition,
          onTap: () {
            setState(() {
              currentlySelectedPin = destinationPinInfo;
              pinPillPosition = 0;
            });
          },
          icon: destinationIcon));

      CameraPosition cPosition = CameraPosition(
        zoom: CAMERA_ZOOM,
        tilt: CAMERA_TILT,
        bearing: CAMERA_BEARING,
        target: LatLng(currentLocation.latitude, currentLocation.longitude),
      );
      final GoogleMapController controller = await _controller.future;
      controller.animateCamera(CameraUpdate.newCameraPosition(cPosition));
      setState(() {
        // print("pinPosition lat " + currentLocation.latitude.toString()+"  long "+ currentLocation.longitude.toString());
        var pinPosition =
            LatLng(currentLocation.latitude, currentLocation.longitude);
        sourcePinInfo.location = pinPosition;
        _markers.removeWhere((m) => m.markerId.value == 'sourcePin');
        _markers.add(Marker(
            markerId: MarkerId('sourcePin'),
            onTap: () {
              setState(() {
                currentlySelectedPin = sourcePinInfo;
                pinPillPosition = 0;
              });
            },
            position: pinPosition, // updated position
            icon: sourceIcon));
      });
    }
  }
}

enter image description here

Any help would be appreciated!


Solution

  • Solved the issue by clear the polylineCoordinates before adding the polyline.