From 88adba9ac5641617e1b4f9a3e35616b6addc2500 Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Tue, 4 Mar 2025 22:43:31 +0000 Subject: [PATCH 1/2] Added `LayerHitTestStrategy` Updated example app --- example/lib/pages/polygon.dart | 198 ++++++++++++------ lib/flutter_map.dart | 1 + lib/src/layer/circle_layer/circle_layer.dart | 7 +- lib/src/layer/circle_layer/painter.dart | 6 +- lib/src/layer/polygon_layer/painter.dart | 6 +- .../layer/polygon_layer/polygon_layer.dart | 7 +- lib/src/layer/polyline_layer/painter.dart | 6 +- .../layer/polyline_layer/polyline_layer.dart | 7 +- .../internal_hit_detectable.dart | 62 ++++-- .../layer_hit_notifier.dart | 16 +- .../layer_hit_test_strategy.dart | 39 ++++ 11 files changed, 266 insertions(+), 89 deletions(-) create mode 100644 lib/src/layer/shared/layer_interactivity/layer_hit_test_strategy.dart diff --git a/example/lib/pages/polygon.dart b/example/lib/pages/polygon.dart index 02717514e..f829032e9 100644 --- a/example/lib/pages/polygon.dart +++ b/example/lib/pages/polygon.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/misc/tile_providers.dart'; @@ -17,23 +16,23 @@ class PolygonPage extends StatefulWidget { } class _PolygonPageState extends State { + LayerHitTestStrategy _hitTestStrategy = LayerHitTestStrategy.allElements; + final LayerHitNotifier _hitNotifier = ValueNotifier(null); - List? _prevHitValues; - List>? _hoverGons; + final _hoverGons = >[]; final _polygonsRaw = >[ Polygon( points: const [ LatLng(51.5, -0.09), LatLng(53.3498, -6.2603), + LatLng(52.230366, -5.767677), LatLng(48.8566, 2.3522), ], borderColor: Colors.red, borderStrokeWidth: 4, - hitValue: ( - title: 'Basic Unfilled Polygon', - subtitle: 'Nothing really special here...', - ), + label: 'Non-interactive', + labelStyle: const TextStyle(color: Colors.black), ), Polygon( points: const [ @@ -49,6 +48,20 @@ class _PolygonPageState extends State { subtitle: 'Nothing really special here...', ), ), + Polygon( + points: const [ + LatLng(53.688428, 0.842058), + LatLng(51.962732, 1.589128), + LatLng(53.844279, 3.91823), + ], + color: Colors.purple.withAlpha(255 ~/ 2), + borderColor: Colors.orange, + borderStrokeWidth: 4, + hitValue: ( + title: 'Interactive Overlap', + subtitle: 'Both polygons appear, this should be on top', + ), + ), Polygon( points: const [ LatLng(46.35, 4.94), @@ -59,10 +72,6 @@ class _PolygonPageState extends State { borderStrokeWidth: 4, borderColor: Colors.lightBlue, color: Colors.yellow, - hitValue: ( - title: 'Polygon With Dashed Borders', - subtitle: '...', - ), ), Polygon( points: const [ @@ -74,10 +83,7 @@ class _PolygonPageState extends State { borderStrokeWidth: 4, borderColor: Colors.purple, label: 'Label!', - hitValue: ( - title: 'Polygon With Label', - subtitle: 'This is a very descriptive label!', - ), + labelStyle: const TextStyle(color: Colors.black), ), Polygon( points: const [ @@ -92,6 +98,7 @@ class _PolygonPageState extends State { label: 'Rotated!', rotateLabel: true, labelPlacement: PolygonLabelPlacement.polylabel, + labelStyle: const TextStyle(color: Colors.black), hitValue: ( title: 'Polygon With Rotated Label', subtitle: "Now you don't have to turn your head so much", @@ -138,7 +145,8 @@ class _PolygonPageState extends State { labelStyle: const TextStyle(color: Colors.black), hitValue: ( title: 'Polygon With Hole', - subtitle: 'A bit like Swiss cheese maybe?', + subtitle: 'A bit like Swiss cheese maybe? Also overlaps a ' + 'non-interactive polygon.', ), ), Polygon( @@ -185,10 +193,6 @@ class _PolygonPageState extends State { rotateLabel: true, labelPlacement: PolygonLabelPlacement.centroid, labelStyle: const TextStyle(color: Colors.black), - hitValue: ( - title: 'Polygon With Hole & Self Intersection', - subtitle: 'This one still works with performant rendering', - ), ), Polygon( points: const [ @@ -277,15 +281,13 @@ class _PolygonPageState extends State { hitTestBehavior: HitTestBehavior.deferToChild, cursor: SystemMouseCursors.click, onHover: (_) { - final hitValues = _hitNotifier.value?.hitValues.toList(); - if (hitValues == null) return; + _hoverGons.clear(); - if (listEquals(hitValues, _prevHitValues)) return; - _prevHitValues = hitValues; + final hitValues = _hitNotifier.value?.hitValues.toList(); + if (hitValues == null) return setState(() {}); - final hoverLines = hitValues.map((v) { + _hoverGons.addAll(hitValues.map((v) { final original = _polygons[v]!; - return Polygon( points: original.points, holePointsList: original.holePointsList, @@ -294,33 +296,28 @@ class _PolygonPageState extends State { borderColor: Colors.green, disableHolesBorder: original.disableHolesBorder, ); - }).toList(); - setState(() => _hoverGons = hoverLines); - }, - onExit: (_) { - _prevHitValues = null; - setState(() => _hoverGons = null); + })); + setState(() {}); }, + onExit: (_) => setState(_hoverGons.clear), child: GestureDetector( onTap: () => _openTouchedGonsModal( 'Tapped', - _hitNotifier.value!.hitValues, - _hitNotifier.value!.coordinate, + _hitNotifier.value, ), onLongPress: () => _openTouchedGonsModal( 'Long pressed', - _hitNotifier.value!.hitValues, - _hitNotifier.value!.coordinate, + _hitNotifier.value, ), onSecondaryTap: () => _openTouchedGonsModal( 'Secondary tapped', - _hitNotifier.value!.hitValues, - _hitNotifier.value!.coordinate, + _hitNotifier.value, ), child: PolygonLayer( hitNotifier: _hitNotifier, simplificationTolerance: 0, - polygons: [..._polygonsRaw, ...?_hoverGons], + hitTestStrategy: _hitTestStrategy, + polygons: [..._polygonsRaw, ..._hoverGons], ), ), ), @@ -429,6 +426,52 @@ class _PolygonPageState extends State { ), ], ), + Positioned( + top: 16, + right: 16, + child: DecoratedBox( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(32), + ), + child: Padding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 4, + bottom: 4, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 16, + children: [ + const Tooltip( + message: 'Adjust Hit Test Strategy', + child: Icon(Icons.ads_click), + ), + DropdownButton( + value: _hitTestStrategy, + items: const [ + DropdownMenuItem( + value: LayerHitTestStrategy.allElements, + child: Text('All Elements'), + ), + DropdownMenuItem( + value: LayerHitTestStrategy.onlyInteractiveElements, + child: Text('Only Interactive Elements'), + ), + DropdownMenuItem( + value: LayerHitTestStrategy.inverted, + child: Text('Inverted'), + ), + ], + onChanged: (v) => setState(() => _hitTestStrategy = v!), + ), + ], + ), + ), + ), + ), ], ), ); @@ -436,9 +479,17 @@ class _PolygonPageState extends State { void _openTouchedGonsModal( String eventType, - List tappedLines, - LatLng coords, + LayerHitResult? hitResult, ) { + if (hitResult == null) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + const SnackBar(content: Text('Hit detected outside of polygons')), + ); + return; + } + showModalBottomSheet( context: context, builder: (context) => Padding( @@ -447,31 +498,58 @@ class _PolygonPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( - 'Tapped Polygon(s)', + 'Hit Polygon(s)', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), Text( - '$eventType at point: (${coords.latitude.toStringAsFixed(6)}, ${coords.longitude.toStringAsFixed(6)})', + '$eventType at coords: (' + '${hitResult.coordinate.latitude.toStringAsFixed(4)}, ' + '${hitResult.coordinate.longitude.toStringAsFixed(4)})', ), const SizedBox(height: 8), - Expanded( - child: ListView.builder( - itemBuilder: (context, index) { - final tappedLineData = tappedLines[index]; - return ListTile( - leading: index == 0 - ? const Icon(Icons.vertical_align_top) - : index == tappedLines.length - 1 - ? const Icon(Icons.vertical_align_bottom) - : const SizedBox.shrink(), - title: Text(tappedLineData.title), - subtitle: Text(tappedLineData.subtitle), - dense: true, - ); - }, - itemCount: tappedLines.length, + if (hitResult.hitValues.isNotEmpty) + Expanded( + child: ListView.builder( + itemBuilder: (context, index) { + if (index == hitResult.hitValues.length) { + return const ListTile( + leading: Icon(Icons.highlight_alt_rounded), + title: + Text('Potentially other non-`hitValue` polygons'), + dense: true, + ); + } + + final hitValue = hitResult.hitValues[index]; + return ListTile( + leading: index == 0 + ? const Icon(Icons.vertical_align_top) + : index == hitResult.hitValues.length - 1 + ? const Icon(Icons.vertical_align_bottom) + : const SizedBox.shrink(), + title: Text(hitValue.title), + subtitle: Text(hitValue.subtitle), + dense: true, + ); + }, + itemCount: hitResult.hitValues.length + 1, + ), + ) + else + const Expanded( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.highlight_alt_rounded, size: 42), + Text( + 'Polygon(s) were hit, but none had `hitValues`', + textAlign: TextAlign.center, + ), + ], + ), + ), ), - ), const SizedBox(height: 8), Align( alignment: Alignment.bottomCenter, diff --git a/lib/flutter_map.dart b/lib/flutter_map.dart index acb68995f..7180212af 100644 --- a/lib/flutter_map.dart +++ b/lib/flutter_map.dart @@ -35,6 +35,7 @@ export 'package:flutter_map/src/layer/polyline_layer/polyline_layer.dart'; export 'package:flutter_map/src/layer/scalebar/scalebar.dart'; export 'package:flutter_map/src/layer/shared/layer_interactivity/layer_hit_notifier.dart'; export 'package:flutter_map/src/layer/shared/layer_interactivity/layer_hit_result.dart'; +export 'package:flutter_map/src/layer/shared/layer_interactivity/layer_hit_test_strategy.dart'; export 'package:flutter_map/src/layer/shared/line_patterns/stroke_pattern.dart'; export 'package:flutter_map/src/layer/shared/mobile_layer_transformer.dart'; export 'package:flutter_map/src/layer/shared/translucent_pointer.dart'; diff --git a/lib/src/layer/circle_layer/circle_layer.dart b/lib/src/layer/circle_layer/circle_layer.dart index 72fdbdb58..af7d28974 100644 --- a/lib/src/layer/circle_layer/circle_layer.dart +++ b/lib/src/layer/circle_layer/circle_layer.dart @@ -16,14 +16,18 @@ class CircleLayer extends StatelessWidget { /// The list of [CircleMarker]s. final List> circles; - /// {@macro fm.lhn.layerHitNotifier.usage} + /// {@macro fm.layerHitNotifier.usage} final LayerHitNotifier? hitNotifier; + /// {@macro fm.layerHitTestStrategy.usage} + final LayerHitTestStrategy hitTestStrategy; + /// Create a new [CircleLayer] as a child for [FlutterMap] const CircleLayer({ super.key, required this.circles, this.hitNotifier, + this.hitTestStrategy = LayerHitTestStrategy.allElements, }); @override @@ -36,6 +40,7 @@ class CircleLayer extends StatelessWidget { circles: circles, camera: camera, hitNotifier: hitNotifier, + hitTestStrategy: hitTestStrategy, ), size: camera.size, isComplex: true, diff --git a/lib/src/layer/circle_layer/painter.dart b/lib/src/layer/circle_layer/painter.dart index 8fd6b2058..8c8fc3a28 100644 --- a/lib/src/layer/circle_layer/painter.dart +++ b/lib/src/layer/circle_layer/painter.dart @@ -12,12 +12,16 @@ class CirclePainter extends CustomPainter @override final LayerHitNotifier? hitNotifier; + @override + final LayerHitTestStrategy hitTestStrategy; + /// Create a [CirclePainter] instance by providing the required /// reference objects. CirclePainter({ required this.circles, required this.camera, required this.hitNotifier, + required this.hitTestStrategy, }); @override @@ -45,7 +49,7 @@ class CirclePainter extends CustomPainter } @override - Iterable> get elements => circles; + List> get elements => circles; @override void paint(Canvas canvas, Size size) { diff --git a/lib/src/layer/polygon_layer/painter.dart b/lib/src/layer/polygon_layer/painter.dart index bbfbea9a0..a580a6e3c 100644 --- a/lib/src/layer/polygon_layer/painter.dart +++ b/lib/src/layer/polygon_layer/painter.dart @@ -41,6 +41,9 @@ class _PolygonPainter extends CustomPainter @override final LayerHitNotifier? hitNotifier; + @override + final LayerHitTestStrategy hitTestStrategy; + /// Create a new [_PolygonPainter] instance. _PolygonPainter({ required this.polygons, @@ -50,6 +53,7 @@ class _PolygonPainter extends CustomPainter required this.debugAltRenderer, required this.camera, required this.hitNotifier, + required this.hitTestStrategy, }) : bounds = camera.visibleBounds; @override @@ -113,7 +117,7 @@ class _PolygonPainter extends CustomPainter } @override - Iterable<_ProjectedPolygon> get elements => polygons; + List<_ProjectedPolygon> get elements => polygons; @override void paint(Canvas canvas, Size size) { diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 6cf7172b5..51b157643 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -66,9 +66,12 @@ base class PolygonLayer /// Defaults to `false`. final bool drawLabelsLast; - /// {@macro fm.lhn.layerHitNotifier.usage} + /// {@macro fm.layerHitNotifier.usage} final LayerHitNotifier? hitNotifier; + /// {@macro fm.layerHitTestStrategy.usage} + final LayerHitTestStrategy hitTestStrategy; + /// Create a new [PolygonLayer] for the [FlutterMap] widget. const PolygonLayer({ super.key, @@ -79,6 +82,7 @@ base class PolygonLayer this.polygonLabels = true, this.drawLabelsLast = false, this.hitNotifier, + this.hitTestStrategy = LayerHitTestStrategy.allElements, super.simplificationTolerance, }) : super(); @@ -176,6 +180,7 @@ class _PolygonLayerState extends State> drawLabelsLast: widget.drawLabelsLast, debugAltRenderer: widget.debugAltRenderer, hitNotifier: widget.hitNotifier, + hitTestStrategy: widget.hitTestStrategy, ), size: camera.size, ), diff --git a/lib/src/layer/polyline_layer/painter.dart b/lib/src/layer/polyline_layer/painter.dart index 661b5afe9..c63dd1e17 100644 --- a/lib/src/layer/polyline_layer/painter.dart +++ b/lib/src/layer/polyline_layer/painter.dart @@ -14,12 +14,16 @@ class _PolylinePainter extends CustomPainter @override final LayerHitNotifier? hitNotifier; + @override + final LayerHitTestStrategy hitTestStrategy; + /// Create a new [_PolylinePainter] instance _PolylinePainter({ required this.polylines, required this.minimumHitbox, required this.camera, required this.hitNotifier, + required this.hitTestStrategy, }); @override @@ -78,7 +82,7 @@ class _PolylinePainter extends CustomPainter } @override - Iterable<_ProjectedPolyline> get elements => polylines; + List<_ProjectedPolyline> get elements => polylines; @override void paint(Canvas canvas, Size size) { diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index f46b981b4..f1403139b 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -35,9 +35,12 @@ base class PolylineLayer /// Defaults to 10. Set to `null` to disable culling. final double? cullingMargin; - /// {@macro fm.lhn.layerHitNotifier.usage} + /// {@macro fm.layerHitNotifier.usage} final LayerHitNotifier? hitNotifier; + /// {@macro fm.layerHitTestStrategy.usage} + final LayerHitTestStrategy hitTestStrategy; + /// The minimum radius of the hittable area around each [Polyline] in logical /// pixels /// @@ -53,6 +56,7 @@ base class PolylineLayer required this.polylines, this.cullingMargin = 10, this.hitNotifier, + this.hitTestStrategy = LayerHitTestStrategy.allElements, this.minimumHitbox = 10, super.simplificationTolerance, }) : super(); @@ -110,6 +114,7 @@ class _PolylineLayerState extends State> polylines: culled, camera: camera, hitNotifier: widget.hitNotifier, + hitTestStrategy: widget.hitTestStrategy, minimumHitbox: widget.minimumHitbox, ), size: camera.size, diff --git a/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart b/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart index 85e2e89c9..22cf59e93 100644 --- a/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart +++ b/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart @@ -12,6 +12,10 @@ mixin HitDetectableElement { /// Elements without a defined [hitValue] are still hit tested, but are not /// notified about. /// + /// When a [hitValue] is defined on a layer, that layer will always capture a + /// hit, and the value will always appear in the list of hits in + /// [LayerHitResult.hitValues] (if a notifier is defined). + /// /// The object should have a valid & useful equality, as it may be used /// by FM internals. R? get hitValue; @@ -22,12 +26,13 @@ mixin HitDetectablePainter> on CustomPainter { abstract final MapCamera camera; abstract final LayerHitNotifier? hitNotifier; + abstract final LayerHitTestStrategy hitTestStrategy; /// Elements that should be possibly be hit tested by [elementHitTest] /// ([hitTest]) /// /// See [elementHitTest] for more information. - Iterable get elements; + List get elements; /// Method invoked by [hitTest] for every element (each of [elements] in /// reverse order) that requires testing @@ -57,31 +62,50 @@ mixin HitDetectablePainter> @override @mustCallSuper bool? hitTest(Offset position) { - _hits.clear(); - bool hasHit = false; + final coordinate = camera.screenOffsetToLatLng(position); + bool? hitResult; - final point = position; - final coordinate = camera.screenOffsetToLatLng(point); + _hits.clear(); for (int i = elements.length - 1; i >= 0; i--) { - final element = elements.elementAt(i); - if (hasHit && element.hitValue == null) continue; - if (elementHitTest(element, point: point, coordinate: coordinate)) { - if (element.hitValue != null) _hits.add(element.hitValue!); - hasHit = true; + final element = elements[i]; + + // If we're not going to change anything even if we hit, don't bother + // testing for a hit + late final addsToHitsList = element.hitValue != null; + late final setsHitResult = + hitTestStrategy == LayerHitTestStrategy.allElements && + hitResult != true; + late final unsetsHitResult = + hitTestStrategy == LayerHitTestStrategy.inverted && hitResult == null; + + if ((addsToHitsList || setsHitResult || unsetsHitResult) && + elementHitTest(element, point: position, coordinate: coordinate)) { + if (element.hitValue != null) { + _hits.add(element.hitValue!); + hitResult = true; + continue; + } + if (hitTestStrategy == LayerHitTestStrategy.allElements) { + hitResult = true; + } + if (hitTestStrategy == LayerHitTestStrategy.inverted) { + hitResult ??= false; + } } } - if (!hasHit) { - hitNotifier?.value = null; - return false; + if (hitResult ?? false) { + hitNotifier?.value = LayerHitResult( + hitValues: _hits, + coordinate: coordinate, + point: position, + ); + return true; } - hitNotifier?.value = LayerHitResult( - hitValues: _hits, - coordinate: coordinate, - point: point, - ); - return true; + hitNotifier?.value = null; + return hitTestStrategy == LayerHitTestStrategy.inverted && + hitResult == null; } } diff --git a/lib/src/layer/shared/layer_interactivity/layer_hit_notifier.dart b/lib/src/layer/shared/layer_interactivity/layer_hit_notifier.dart index 076d118bc..9b43d7854 100644 --- a/lib/src/layer/shared/layer_interactivity/layer_hit_notifier.dart +++ b/lib/src/layer/shared/layer_interactivity/layer_hit_notifier.dart @@ -1,8 +1,8 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_map/src/layer/shared/layer_interactivity/layer_hit_result.dart'; +import 'package:flutter_map/flutter_map.dart'; -/// A [ValueNotifier] that notifies: +/// A [ValueNotifier] that emits: /// /// * a [LayerHitResult] when a hit is detected on an element in a layer /// * `null` when a hit is detected on the layer but not on an element @@ -11,9 +11,17 @@ import 'package:flutter_map/src/layer/shared/layer_interactivity/layer_hit_resul /// ```dart /// final LayerHitNotifier hitNotifier = ValueNotifier(null); /// ``` +/// +/// Note that whether or not this is defined on a layer does not affect whether +/// the layer conducts hit testing. +/// +/// A layer's hit test result, the behaviour of which is determined by +/// [LayerHitTestStrategy], is independent of the values emitted by any notifier +/// attached to the layer. The layer's hit test result can only be a boolean +/// flag, whereas the notifier allows more detail to be emitted. typedef LayerHitNotifier = ValueNotifier?>; -/// {@template fm.lhn.layerHitNotifier.usage} +/// {@template fm.layerHitNotifier.usage} /// A notifier to be notified when a hit test occurs on the layer /// /// Notified with a [LayerHitResult] if any elements are hit, otherwise @@ -25,4 +33,4 @@ typedef LayerHitNotifier = ValueNotifier?>; /// example project for an example implementation. /// {@endtemplate} // ignore: unused_element, constant_identifier_names -const _doc_fmLHNLayerHitNotiferUsage = null; +const _doc_fmLayerHitNotifierUsage = null; diff --git a/lib/src/layer/shared/layer_interactivity/layer_hit_test_strategy.dart b/lib/src/layer/shared/layer_interactivity/layer_hit_test_strategy.dart new file mode 100644 index 000000000..55e469263 --- /dev/null +++ b/lib/src/layer/shared/layer_interactivity/layer_hit_test_strategy.dart @@ -0,0 +1,39 @@ +import 'package:flutter_map/flutter_map.dart'; + +/// Controls the results of hit tests performed on hittable layers +/// +/// Elements within hittable layers are always (at least for the purpose of +/// external API usage) hit tested when the layers are hit tested. This controls +/// how the results of each element's hit test affects the layer's hit test +/// result (potentially depending on whether elements have non-null +/// `hitValue`s). +/// +/// A layer's hit test result is independent of the [LayerHitResult]s emitted by +/// any [LayerHitNotifier] attached to the layer. The layer's hit test result +/// can only be a boolean flag, whereas the notifier allows more detail to be +/// emitted. +enum LayerHitTestStrategy { + /// Positive if any element has been hit + /// + /// The default behaviour. + allElements, + + /// Positive if any element with a non-null `hitValue` has been hit + onlyInteractiveElements, + + /// Positive if no elements have been hit, or any element with a non-null + /// `hitValue` has been hit + inverted, +} + +/// {@template fm.layerHitTestStrategy.usage} +/// The strategy to use to determine the result of a hit test performed on this +/// layer +/// +/// Defaults to [LayerHitTestStrategy.allElements]. +/// +/// See online documentation for more detailed usage instructions. See the +/// example project for an example implementation. +/// {@endtemplate} +// ignore: unused_element, constant_identifier_names +const _doc_fmLayerHitTestStrategyUsage = null; From 65605e7f30def70f1e53461bb5975f94248ee32a Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Tue, 18 Mar 2025 10:13:46 +0000 Subject: [PATCH 2/2] Minor correction in example app --- example/lib/pages/polygon.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/example/lib/pages/polygon.dart b/example/lib/pages/polygon.dart index 8f7239933..d623e9bfc 100644 --- a/example/lib/pages/polygon.dart +++ b/example/lib/pages/polygon.dart @@ -431,6 +431,7 @@ class _PolygonPageState extends State { right: 16, child: Column( spacing: 8, + crossAxisAlignment: CrossAxisAlignment.end, children: [ DecoratedBox( decoration: BoxDecoration(