AlexKost
AlexKost

Reputation: 2972

Fit markers on the screen with flutter google maps

I'm playing around with Google Maps for Flutter. I want the map view to fit all the markers on the screen. Basically, I want to do same as here for android.

I assumed that cameraTargetBounds describes the area that should fit inside screen boundaries. But in fact it doesn't fit this area entirely, focusing somewhere in between.

What I've done so far:

  @override
  Widget build(BuildContext context) {
    final bloc = Provider.of<BlocMap>(context);
    return SafeArea(
      child: Container(
        child: Stack(
          children: <Widget>[
            Positioned.fill(
              child: StreamBuilder<MapData>(
                  stream: bloc.mapData,
                  builder: (context, snap) {
                    final mapData = snap.data;
                    print("mapData: $mapData");
                    return GoogleMap(
                      padding: EdgeInsets.only(bottom: 42),
                      mapType: _mapType,
                      cameraTargetBounds: mapData?.markers == null
                          ? CameraTargetBounds.unbounded
                          : CameraTargetBounds(_bounds(mapData?.markers)),
                      initialCameraPosition: _cameraPosition,
                      myLocationEnabled: true,
                      myLocationButtonEnabled: true,
                      gestureRecognizers: _buildGestureRecognizer(),
                      onMapCreated: (GoogleMapController controller) {
                        _controller.complete(controller);
                      },
                      markers: mapData?.markers,
                      polylines: mapData?.polylines,
                      polygons: mapData?.polygons,
                      onLongPress: (latLng) {
                        print("LatLng: $latLng");
                      },
                    );
                  }),
            ),
            Positioned(
              left: 0,
              right: 0,
              bottom: 0,
              child: _buildBtnBar(),
            )
          ],
        ),
      ),
    );

    LatLngBounds _bounds(Set<Marker> markers) {
    if (markers == null || markers.isEmpty) return null;
    return _createBounds(markers.map((m) => m.position).toList());
  }


 LatLngBounds _createBounds(List<LatLng> positions) {
    final southwestLat = positions.map((p) => p.latitude).reduce((value, element) => value < element ? value : element); // smallest
    final southwestLon = positions.map((p) => p.longitude).reduce((value, element) => value < element ? value : element);
    final northeastLat = positions.map((p) => p.latitude).reduce((value, element) => value > element ? value : element); // biggest
    final northeastLon = positions.map((p) => p.longitude).reduce((value, element) => value > element ? value : element);
    return LatLngBounds(
        southwest: LatLng(southwestLat, southwestLon),
        northeast: LatLng(northeastLat, northeastLon)
    );
  }

What is the intended way of achieving this basic functionality in flutter? Any suggestions?

Upvotes: 15

Views: 10247

Answers (3)

benten
benten

Reputation: 790

first make a list of latlng and pass it to the given method

List<LatLng> latlonList = [
  LatLng(125.75,87.00),
  LatLng(165,46.76),
  LatLng(175.87,86.00),
];

final GoogleMapController googleMapController = await widget.completer.future;

googleMapController.moveCamera(CameraUpdate.newLatLngBounds(
  MapUtils.boundsFromLatLngList(latlonList),
  4.0,
));

after that your map move to all the pointer . but to show more marker decent way we use another method which give us the center point of all the markers

var centerPoint =  computeCentroid(latlonList);
var zoom = await googleMapController.getZoomLevel();
 
this.updateLocation(centerPoint.latitude, centerPoint.longitude, zoom - 2);  

Now simply copy these method..and every ting work perfectly fine

LatLng computeCentroid(Iterable<LatLng> points) {
  double latitude = 0;
  double longitude = 0;
  int n = points.length;

  for (LatLng point in points) {
    latitude += point.latitude;
    longitude += point.longitude;
  }

  return LatLng(latitude / n, longitude / n);
}

class MapUtils {
  static LatLngBounds boundsFromLatLngList(List<LatLng> list) {
    double? x0, x1, y0, y1;

    for (LatLng latLng in list) {
      if (x0 == null) {
        x0 = x1 = latLng.latitude;
        y0 = y1 = latLng.longitude;
      } else {
        if (latLng.latitude > x1!) x1 = latLng.latitude;
        if (latLng.latitude < x0) x0 = latLng.latitude;
        if (latLng.longitude > y1!) y1 = latLng.longitude;
        if (latLng.longitude < y0!) y0 = latLng.longitude;
      }
    }

    return LatLngBounds(
      northeast: LatLng(x1!, y1!),
      southwest: LatLng(x0!, y0!),
    );
  }
}

updateLocation(double latitude, double longitude, double zoom) async {
  final GoogleMapController googleMapController = await widget.completer.future;
  print("zoom: $zoom");
  googleMapController.animateCamera(CameraUpdate.newCameraPosition(
    CameraPosition(
      bearing: 0,
      target: LatLng(latitude, longitude),
      zoom: zoom,
    ),
  ));
} 

Upvotes: 3

Nick
Nick

Reputation: 1215

Try using the controller to move the camera to the new bounds, eg (from the plugin example):

mapController.moveCamera(
  CameraUpdate.newLatLngBounds(
    LatLngBounds(
      southwest: const LatLng(-38.483935, 113.248673),
      northeast: const LatLng(-8.982446, 153.823821),
    ),
    10.0,
  ),
);

Upvotes: 3

Bruno Zaranza
Bruno Zaranza

Reputation: 91

Use this code to do what you wish.

onMapCreated: (GoogleMapController controller) {
   _controller.complete(controller);

   setState(() {
      controller.animateCamera(CameraUpdate.newLatLngBounds(_bounds(markers), 50));
   });
}

Upvotes: 9

Related Questions