Skip to content

Constraining only latitude with unconstrained horizontal scroll #2071

@mbenoukaiss

Description

@mbenoukaiss

This issue is actually a feature request : it is labelled as a bug because it originated from a bug and I originally planned on only creating this issue, however after trying to understand what causes this behaviour, I created issue #2072 which is the underlying bug. They are two separate issues and solving #2072 will not "solve" this feature request

What is the bug?

If cameraConstraint is set from -90;-180 to 90;180, panning to the horizontal edges of the map will be inconsistent and there is no way to have unconstrained horizontal scroll with latitude boundaries of -90 to 90. If you don't pan with enough speed it will stop at the border and if you pan with enough speed it will jump to the other side with some lag and it will be quite jerky.

Seems to be the same issue as #2005 don't know why he marked it as completed though ? Is there already a way to fix it I missed ?

How can we reproduce it?

Create a FlutterMap with the following property set in MapOptions : cameraConstraint: CameraConstraint.contain(LatLngBounds(const LatLng(-90, -180), const LatLng(90, 180))),

Do you have a potential solution?

I see two ways to fix the problem :

  • Ignore the longitude constraints when they're 180 or -180, but it would make it impossible for someone to actually constrain a map to these longitudes if they want to
  • Create a camera constraint that only constrains latitude

To work around it I opted for the second solution and made the following camera constraint class inspired from the other classes, it only constrains the camera on the latitude.

@immutable
class ContainCameraVertically extends CameraConstraint {
  const ContainCameraVertically(this.a, this.b);

  final double a;
  final double b;

  @override
  MapCamera? constrain(MapCamera camera) {
    final double testZoom = camera.zoom;
    final LatLng testCenter = camera.center;

    final Offset aPixel = camera.projectAtZoom(LatLng(a, 0), testZoom);
    final Offset bPixel = camera.projectAtZoom(LatLng(b, 0), testZoom);

    final Size halfSize = camera.size / 2;

    // Find the limits for the map center which would keep the camera within the
    // [a] and [b] bounds.
    final double topOkCenter = math.min(aPixel.dy, bPixel.dy) + halfSize.height;
    final double botOkCenter = math.max(aPixel.dy, bPixel.dy) - halfSize.height;

    // Stop if we are zoomed out so far that the camera cannot be translated to
    // stay within the [a] and [b] bounds.
    if (topOkCenter > botOkCenter) {
      return null;
    }

    final Offset centerPix = camera.projectAtZoom(testCenter, testZoom);
    final newCenterPix = Offset(centerPix.dx, centerPix.dy.clamp(topOkCenter, botOkCenter));

    if (newCenterPix == centerPix) {
      return camera;
    }

    return camera.withPosition(center: camera.unprojectAtZoom(newCenterPix, testZoom));
  }

  @override
  bool operator ==(Object other) {
    return other is ContainCameraVertically && other.a == a && other.b == b;
  }

  @override
  int get hashCode => a.hashCode ^ b.hashCode;
}

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions