Skip to content
46 changes: 44 additions & 2 deletions example/lib/pages/markers.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_example/misc/tile_providers.dart';
Expand Down Expand Up @@ -117,8 +119,11 @@ class _MarkerPageState extends State<MarkerPage> {
Flexible(
child: FlutterMap(
options: MapOptions(
initialCenter: const LatLng(51.5, -0.09),
initialZoom: 5,
initialCenter: const LatLng(
51.51868093513547,
-0.12835376940892318,
),
initialZoom: 15,
onTap: (_, p) => setState(() => customMarkers.add(buildPin(p))),
interactionOptions: const InteractionOptions(
flags: ~InteractiveFlag.doubleTapZoom,
Expand Down Expand Up @@ -167,6 +172,43 @@ class _MarkerPageState extends State<MarkerPage> {
rotate: counterRotate,
alignment: selectedAlignment,
),
MarkerLayer(
markers: [
Marker(
point: const LatLng(
51.51868093513547,
-0.12835376940892318,
),
height: 20,
width: 20,
boxConstraintsUsingMetersInPixels: const BoxConstraints(
maxHeight: 200,
maxWidth: 200,
),
child: LayoutBuilder(
builder: (context, constraints) {
final minDimension = min(
constraints.maxHeight,
constraints.maxWidth,
);

return Transform.scale(
scale: minDimension / 30,
child: const SizedBox(
width: 30,
height: 30,
child: Icon(
Icons.map,
color: Colors.amber,
),
),
);
},
),
useSizeInMeters: true,
),
],
),
],
),
),
Expand Down
13 changes: 13 additions & 0 deletions lib/src/layer/marker_layer/marker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ class Marker {
/// marker. Use a widget inside [child] to perform this.
final bool? rotate;

/// Parameter to enable or not the feature to use markers dimensions in meters.
///
/// A good way to use that feature is using a LayoutBuilder and building according the
/// maxHeight and minWidth values.
final bool useSizeInMeters;

/// Parameter to control the box size when the parameter [useSizeInMeters] is enabled.
/// That BoxConstraints is optional and when exists control the minimal and maximal size
/// in pixels of that region created by the map visible region in meters.
final BoxConstraints? boxConstraintsUsingMetersInPixels;

/// Creates a container for a [child] widget located at a geographic coordinate
/// [point]
///
Expand All @@ -61,6 +72,8 @@ class Marker {
this.height = 30,
this.alignment,
this.rotate,
this.useSizeInMeters = false,
this.boxConstraintsUsingMetersInPixels,
});

/// Returns the alignment of a [width]x[height] rectangle by [left]x[top] pixels.
Expand Down
52 changes: 43 additions & 9 deletions lib/src/layer/marker_layer/marker_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,53 @@ class MarkerLayer extends StatelessWidget {
child: Stack(
children: (List<Marker> markers) sync* {
for (final m in markers) {
// Resolve real alignment
// TODO: maybe just using Size, Offset, and Rect?
final left = 0.5 * m.width * ((m.alignment ?? alignment).x + 1);
final top = 0.5 * m.height * ((m.alignment ?? alignment).y + 1);
final right = m.width - left;
final bottom = m.height - top;

// Perform projection
final pxPoint = map.projectAtZoom(m.point);

Positioned? getPositioned(double worldShift) {
final shiftedX = pxPoint.dx + worldShift;

double height = m.height;
double width = m.width;

if (m.useSizeInMeters) {
final basePoint = m.point;
final baseOffset = map.getOffsetFromOrigin(basePoint);
final rHeight =
const Distance().offset(basePoint, height / 2, 0);
final rWidth = const Distance().offset(basePoint, width / 2, 0);

height =
(baseOffset - map.getOffsetFromOrigin(rHeight)).distance *
2;
width =
(baseOffset - map.getOffsetFromOrigin(rWidth)).distance * 2;

final boxConstraintsUsingMetersInPixels =
m.boxConstraintsUsingMetersInPixels;
if (boxConstraintsUsingMetersInPixels != null) {
if (height > boxConstraintsUsingMetersInPixels.maxHeight) {
height = boxConstraintsUsingMetersInPixels.maxHeight;
}
if (width > boxConstraintsUsingMetersInPixels.maxWidth) {
width = boxConstraintsUsingMetersInPixels.maxWidth;
}
if (height < boxConstraintsUsingMetersInPixels.minHeight) {
height = boxConstraintsUsingMetersInPixels.minHeight;
}
if (width < boxConstraintsUsingMetersInPixels.minWidth) {
width = boxConstraintsUsingMetersInPixels.minWidth;
}
}
}

// Resolve real alignment
// TODO: maybe just using Size, Offset, and Rect?
final left = 0.5 * width * ((m.alignment ?? alignment).x + 1);
final top = 0.5 * height * ((m.alignment ?? alignment).y + 1);
final right = width - left;
final bottom = height - top;

// Cull if out of bounds
if (!map.pixelBounds.overlaps(
Rect.fromPoints(
Expand All @@ -77,8 +111,8 @@ class MarkerLayer extends StatelessWidget {

return Positioned(
key: m.key,
width: m.width,
height: m.height,
width: width,
height: height,
left: shiftedLocalPoint.dx - right,
top: shiftedLocalPoint.dy - bottom,
child: (m.rotate ?? rotate)
Expand Down