diff --git a/example/lib/pages/markers.dart b/example/lib/pages/markers.dart index 2f652096b..22dad83a1 100644 --- a/example/lib/pages/markers.dart +++ b/example/lib/pages/markers.dart @@ -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'; @@ -117,8 +119,11 @@ class _MarkerPageState extends State { 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, @@ -167,6 +172,43 @@ class _MarkerPageState extends State { 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, + ), + ], + ), ], ), ), diff --git a/lib/src/layer/marker_layer/marker.dart b/lib/src/layer/marker_layer/marker.dart index 764770342..f66a8c227 100644 --- a/lib/src/layer/marker_layer/marker.dart +++ b/lib/src/layer/marker_layer/marker.dart @@ -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] /// @@ -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. diff --git a/lib/src/layer/marker_layer/marker_layer.dart b/lib/src/layer/marker_layer/marker_layer.dart index fe4693d46..6489829e0 100644 --- a/lib/src/layer/marker_layer/marker_layer.dart +++ b/lib/src/layer/marker_layer/marker_layer.dart @@ -47,19 +47,53 @@ class MarkerLayer extends StatelessWidget { child: Stack( children: (List 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( @@ -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)