flutter_map_marker_popup icon indicating copy to clipboard operation
flutter_map_marker_popup copied to clipboard

Feature Request: Show popups inside current window

Open pento opened this issue 2 years ago • 4 comments

I have a map view that isn't scrollable (that is, the interactiveFlags map option is set to InteractiveFlag.none). I'm also using a PopupLayer with popupSnap set to PopupSnap.markerTop. When these popups are close to the edge of the map, it'd be super useful if there was an option to force them to move over slight (left, right, or down), to ensure the popup fits inside the map.

I'm happy to help by writing or testing a PR if you could give me some pointers for how you'd suggest implementing this. 🙂

Current Behaviour

Suggested Behaviour

pento avatar Feb 19 '23 07:02 pento

Hi @pento . I can definitely see the benefit of this feature. I'm going to have a think about how this should be implemented in a clean way.

rorystephenson avatar Feb 24 '23 07:02 rorystephenson

@pento I haven't forgotten this but I just wanted to let you know I'm not likely to get to this very soon as I'm busy with other packages/projects. It's not a trivial feature otherwise I'd jump in and implement it. For when I (or someone else with a PR) do eventually get to it here is a thought on implementation:

I can see it having benefit for non-static maps as well although I'd probably limit the visibility to only when the marker itself is visible both because it will be confusing otherwise and because, due to implementation details, it would be tricky to keep it visible when the marker is off the map.

rorystephenson avatar Apr 04 '23 16:04 rorystephenson

@pento Unfortunately I don't have much time for this right now as I'm searching for work. If you need this for a commercial project with a budget I would be available to make this happen fast on a freelancing basis, otherwise I hope to get to it eventually but I don't know when that will be.

rorystephenson avatar Jul 06 '23 07:07 rorystephenson

I finally got back to playing around with this - it's just a little hobby project, nothing commercial, so I really only look at it when I'm feeling inspired.

I managed to hack something resembling this behaviour together, if anyone wants to implement it in their own app.

First, PopupLayer creates a custom widget for the popup:

PopupLayer(
  popupDisplayOptions: PopupDisplayOptions(
    builder: (BuildContext context, Marker marker) {
      return MyCustomPopup(
        marker: marker,
      );
    },
    snap: PopupSnap.markerCenter,
  ),
),

Then my custom popup figures out if the marker is too close to the edge of the map, and adds some extra padding, depending on which edge it's too close to. It's not ideal that it uses magic numbers for this (these are just the numbers that work for my use, you'll probably need to tweak them), but it works. e😄

class MyCustomPopup extends StatelessWidget {
  /// The [Marker] that was tapped.
  final Marker marker;

  /// Constructor
  const MyCustomPopup({
    required this.marker,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    // Default position is centered above the marker.
    double left = 0;
    double top = 0;
    double right = 0;
    double bottom = 105;

    final Point<double> markerPoint =
        MapCamera.of(context).latLngToScreenPoint(marker.point);

    // If we're too close to the top, move the popup below the marker.
    if (markerPoint.y < 110) {
      top = 105;
      bottom = 0;
    }

    // If we're too close to the left, move the popup to the right.
    if (markerPoint.x < 130) {
      left = 135 - markerPoint.x;
    }

    // If we're too close to the right, move the popup to the left.
    if (markerPoint.x > MediaQuery.of(context).size.width - 130) {
      right = 135 - (MediaQuery.of(context).size.width - markerPoint.x);
    }

    return SizedBox(
      child: Card(
        margin: EdgeInsets.only(
          left: left,
          right: right,
          bottom: bottom,
          top: top,
        ),
        child: Padding(
          padding: const EdgeInsets.all(8),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Text('Here is some content'),
            ],
          ),
        ),
      ),
    );
  }
}

pento avatar Jan 27 '24 04:01 pento