From 66336acd2957bd6537c773f9c2d9420d06cd5e4b Mon Sep 17 00:00:00 2001 From: nimr77 Date: Mon, 16 Dec 2024 12:50:02 +0100 Subject: [PATCH 1/7] fix issue with flutter 3.27 --- example/lib/map_click.dart | 5 +- example/lib/map_coordinates.dart | 5 +- example/lib/place_annotation.dart | 382 +++++++++++++------------- example/lib/scrolling_map.dart | 23 +- example/test_driver/test_widgets.dart | 2 +- lib/apple_maps_flutter.dart | 11 +- lib/src/annotation.dart | 336 +++++++++++----------- lib/src/annotation_updates.dart | 49 ++-- lib/src/camera.dart | 74 ++--- lib/src/circle_updates.dart | 49 ++-- lib/src/location.dart | 78 +++--- lib/src/polygon_updates.dart | 49 ++-- lib/src/polyline_updates.dart | 49 ++-- lib/src/snapshot_options.dart | 44 +-- lib/src/ui.dart | 80 +++--- pubspec.yaml | 6 +- 16 files changed, 619 insertions(+), 623 deletions(-) diff --git a/example/lib/map_click.dart b/example/lib/map_click.dart index 49510d4..6ff8eac 100644 --- a/example/lib/map_click.dart +++ b/example/lib/map_click.dart @@ -4,7 +4,6 @@ import 'package:apple_maps_flutter/apple_maps_flutter.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'page.dart'; @@ -30,11 +29,11 @@ class _MapClickBody extends StatefulWidget { } class _MapClickBodyState extends State<_MapClickBody> { - _MapClickBodyState(); - AppleMapController? mapController; + LatLng? _lastTap; LatLng? _lastLongPress; + _MapClickBodyState(); @override Widget build(BuildContext context) { diff --git a/example/lib/map_coordinates.dart b/example/lib/map_coordinates.dart index de28d54..5d63b77 100644 --- a/example/lib/map_coordinates.dart +++ b/example/lib/map_coordinates.dart @@ -4,7 +4,6 @@ import 'package:apple_maps_flutter/apple_maps_flutter.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'page.dart'; @@ -30,13 +29,13 @@ class _MapCoordinatesBody extends StatefulWidget { } class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { - _MapCoordinatesBodyState(); - AppleMapController? mapController; + LatLngBounds _visibleRegion = LatLngBounds( southwest: const LatLng(0, 0), northeast: const LatLng(0, 0), ); + _MapCoordinatesBodyState(); @override Widget build(BuildContext context) { diff --git a/example/lib/place_annotation.dart b/example/lib/place_annotation.dart index 3a3e28b..af615c7 100644 --- a/example/lib/place_annotation.dart +++ b/example/lib/place_annotation.dart @@ -4,8 +4,6 @@ import 'dart:async'; import 'dart:math'; -import 'dart:typed_data'; -import 'dart:ui'; import 'dart:ui' as ui; import 'package:apple_maps_flutter/apple_maps_flutter.dart'; @@ -14,14 +12,7 @@ import 'package:flutter/services.dart'; import 'page.dart'; -class PlaceAnnotationPage extends ExamplePage { - PlaceAnnotationPage() : super(const Icon(Icons.place), 'Place annotation'); - - @override - Widget build(BuildContext context) { - return const PlaceAnnotationBody(); - } -} +typedef Annotation AnnotationUpdateAction(Annotation annotation); class PlaceAnnotationBody extends StatefulWidget { const PlaceAnnotationBody(); @@ -30,13 +21,10 @@ class PlaceAnnotationBody extends StatefulWidget { State createState() => PlaceAnnotationBodyState(); } -typedef Annotation AnnotationUpdateAction(Annotation annotation); - class PlaceAnnotationBodyState extends State { - PlaceAnnotationBodyState(); static final LatLng center = const LatLng(-33.86711, 151.1947171); - late AppleMapController controller; + Uint8List? _imageBytes; Map annotations = {}; late AnnotationId selectedAnnotationId; @@ -44,9 +32,115 @@ class PlaceAnnotationBodyState extends State { BitmapDescriptor? _annotationIcon; late BitmapDescriptor _iconFromBytes; double _devicePixelRatio = 3.0; + PlaceAnnotationBodyState(); - void _onMapCreated(AppleMapController controller) { - this.controller = controller; + @override + Widget build(BuildContext context) { + _createAnnotationImageFromAsset(context, _devicePixelRatio); + _getBytesFromAsset('assets/creator.png', 160); + return SafeArea( + child: Column( + children: [ + Expanded( + child: AppleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11, + ), + annotations: Set.of(annotations.values), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Wrap( + alignment: WrapAlignment.spaceEvenly, + children: [ + TextButton( + child: const Text('add defaultAnnotation'), + onPressed: () => _add('pin'), + ), + TextButton( + child: const Text('add defaultWithHue'), + onPressed: () => _add('defaultAnnotationWithColor'), + ), + TextButton( + child: const Text('add markerAnnotation'), + onPressed: () => _add('marker'), + ), + TextButton( + child: const Text('add markerWithHue'), + onPressed: () => _add('markerAnnotationWithHue'), + ), + TextButton( + child: const Text('add customAnnotation'), + onPressed: () => _add('customAnnotation'), + ), + TextButton( + child: const Text('customAnnotation from bytes'), + onPressed: () => _add('customAnnotationFromBytes'), + ), + TextButton( + child: const Text('remove'), + onPressed: _remove, + ), + TextButton( + child: const Text('change info'), + onPressed: _changeInfo, + ), + TextButton( + child: const Text('infoWindow is shown?s'), + onPressed: _isInfoWindowShown, + ), + TextButton( + child: const Text('change alpha'), + onPressed: _changeAlpha, + ), + TextButton( + child: const Text('toggle draggable'), + onPressed: _toggleDraggable, + ), + TextButton( + child: const Text('change position'), + onPressed: _changePosition, + ), + TextButton( + child: const Text('toggle visible'), + onPressed: _toggleVisible, + ), + TextButton( + child: const Text('show infoWindow'), + onPressed: _showInfoWindow, + ), + TextButton( + child: const Text('hide infoWindow'), + onPressed: _hideInfoWindow, + ), + TextButton( + child: const Text('change zIndex'), + onPressed: () => _changeZIndex(selectedAnnotationId), + ), + TextButton( + child: Text('Take a snapshot'), + onPressed: () async { + final imageBytes = await this.controller.takeSnapshot(); + setState(() { + _imageBytes = imageBytes; + }); + }, + ), + Container( + decoration: BoxDecoration(color: Colors.blueGrey[50]), + height: 180, + child: + _imageBytes != null ? Image.memory(_imageBytes!) : null, + ), + ], + ), + ) + ], + ), + ); } @override @@ -54,20 +148,6 @@ class PlaceAnnotationBodyState extends State { super.dispose(); } - void _onAnnotationTapped(AnnotationId annotationId) { - final Annotation? tappedAnnotation = annotations[annotationId]; - if (tappedAnnotation != null) { - setState(() { - if (annotations.containsKey(tappedAnnotation)) { - final Annotation resetOld = - annotations[selectedAnnotationId]!.copyWith(); - annotations[selectedAnnotationId] = resetOld; - } - selectedAnnotationId = annotationId; - }); - } - } - void _add(String iconType) { final int annotationCount = annotations.length; @@ -124,36 +204,12 @@ class PlaceAnnotationBodyState extends State { }); } - void _remove() { - setState(() { - if (annotations.containsKey(selectedAnnotationId)) { - annotations.remove(selectedAnnotationId); - } - }); - } - - void _changePosition() { - final Annotation annotation = annotations[selectedAnnotationId]!; - final LatLng current = annotation.position; - final Offset offset = Offset( - center.latitude - current.latitude, - center.longitude - current.longitude, - ); - setState(() { - annotations[selectedAnnotationId] = annotation.copyWith( - positionParam: LatLng( - center.latitude + offset.dy, - center.longitude + offset.dx, - ), - ); - }); - } - - Future _toggleDraggable() async { + Future _changeAlpha() async { final Annotation annotation = annotations[selectedAnnotationId]!; + final double current = annotation.alpha; setState(() { annotations[selectedAnnotationId] = annotation.copyWith( - draggableParam: !annotation.draggable, + alphaParam: current < 0.1 ? 1.0 : current * 0.75, ); }); } @@ -171,21 +227,29 @@ class PlaceAnnotationBodyState extends State { }); } - Future _changeAlpha() async { + void _changePosition() { final Annotation annotation = annotations[selectedAnnotationId]!; - final double current = annotation.alpha; + final LatLng current = annotation.position; + final Offset offset = Offset( + center.latitude - current.latitude, + center.longitude - current.longitude, + ); setState(() { annotations[selectedAnnotationId] = annotation.copyWith( - alphaParam: current < 0.1 ? 1.0 : current * 0.75, + positionParam: LatLng( + center.latitude + offset.dy, + center.longitude + offset.dx, + ), ); }); } - Future _toggleVisible() async { - final Annotation annotation = annotations[selectedAnnotationId]!; + Future _changeZIndex(AnnotationId annotationId) async { + final Annotation annotation = annotations[annotationId]!; + final double current = annotation.zIndex; setState(() { - annotations[selectedAnnotationId] = annotation.copyWith( - visibleParam: !annotation.visible, + annotations[annotationId] = annotation.copyWith( + zIndexParam: current >= 12.0 ? 0.0 : current + 1.0, ); }); } @@ -201,15 +265,15 @@ class PlaceAnnotationBodyState extends State { } } - void _updateBitmap(BitmapDescriptor bitmap) { - setState(() { - _annotationIcon = bitmap; - }); - } - - Future _showInfoWindow() async { - final Annotation annotation = annotations[selectedAnnotationId]!; - await this.controller.showMarkerInfoWindow(annotation.annotationId); + Future _getBytesFromAsset(String path, int width) async { + ByteData data = await rootBundle.load(path); + ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), + targetWidth: width); + ui.FrameInfo fi = await codec.getNextFrame(); + _iconFromBytes = BitmapDescriptor.fromBytes( + (await fi.image.toByteData(format: ui.ImageByteFormat.png))! + .buffer + .asUint8List()); } Future _hideInfoWindow() async { @@ -226,133 +290,67 @@ class PlaceAnnotationBodyState extends State { .isMarkerInfoWindowShown(annotation.annotationId))!; } - Future _getBytesFromAsset(String path, int width) async { - ByteData data = await rootBundle.load(path); - ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), - targetWidth: width); - ui.FrameInfo fi = await codec.getNextFrame(); - _iconFromBytes = BitmapDescriptor.fromBytes( - (await fi.image.toByteData(format: ui.ImageByteFormat.png))! - .buffer - .asUint8List()); + void _onAnnotationTapped(AnnotationId annotationId) { + final Annotation? tappedAnnotation = annotations[annotationId]; + if (tappedAnnotation != null) { + setState(() { + if (annotations.containsKey(tappedAnnotation)) { + final Annotation resetOld = + annotations[selectedAnnotationId]!.copyWith(); + annotations[selectedAnnotationId] = resetOld; + } + selectedAnnotationId = annotationId; + }); + } } - Future _changeZIndex(AnnotationId annotationId) async { - final Annotation annotation = annotations[annotationId]!; - final double current = annotation.zIndex; + void _onMapCreated(AppleMapController controller) { + this.controller = controller; + } + + void _remove() { setState(() { - annotations[annotationId] = annotation.copyWith( - zIndexParam: current >= 12.0 ? 0.0 : current + 1.0, + if (annotations.containsKey(selectedAnnotationId)) { + annotations.remove(selectedAnnotationId); + } + }); + } + + Future _showInfoWindow() async { + final Annotation annotation = annotations[selectedAnnotationId]!; + await this.controller.showMarkerInfoWindow(annotation.annotationId); + } + + Future _toggleDraggable() async { + final Annotation annotation = annotations[selectedAnnotationId]!; + setState(() { + annotations[selectedAnnotationId] = annotation.copyWith( + draggableParam: !annotation.draggable, ); }); } + Future _toggleVisible() async { + final Annotation annotation = annotations[selectedAnnotationId]!; + setState(() { + annotations[selectedAnnotationId] = annotation.copyWith( + visibleParam: !annotation.visible, + ); + }); + } + + void _updateBitmap(BitmapDescriptor bitmap) { + setState(() { + _annotationIcon = bitmap; + }); + } +} + +class PlaceAnnotationPage extends ExamplePage { + PlaceAnnotationPage() : super(const Icon(Icons.place), 'Place annotation'); + @override Widget build(BuildContext context) { - _createAnnotationImageFromAsset(context, _devicePixelRatio); - _getBytesFromAsset('assets/creator.png', 160); - return SafeArea( - child: Column( - children: [ - Expanded( - child: AppleMap( - onMapCreated: _onMapCreated, - initialCameraPosition: const CameraPosition( - target: LatLng(-33.852, 151.211), - zoom: 11, - ), - annotations: Set.of(annotations.values), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Wrap( - alignment: WrapAlignment.spaceEvenly, - children: [ - TextButton( - child: const Text('add defaultAnnotation'), - onPressed: () => _add('pin'), - ), - TextButton( - child: const Text('add defaultWithHue'), - onPressed: () => _add('defaultAnnotationWithColor'), - ), - TextButton( - child: const Text('add markerAnnotation'), - onPressed: () => _add('marker'), - ), - TextButton( - child: const Text('add markerWithHue'), - onPressed: () => _add('markerAnnotationWithHue'), - ), - TextButton( - child: const Text('add customAnnotation'), - onPressed: () => _add('customAnnotation'), - ), - TextButton( - child: const Text('customAnnotation from bytes'), - onPressed: () => _add('customAnnotationFromBytes'), - ), - TextButton( - child: const Text('remove'), - onPressed: _remove, - ), - TextButton( - child: const Text('change info'), - onPressed: _changeInfo, - ), - TextButton( - child: const Text('infoWindow is shown?s'), - onPressed: _isInfoWindowShown, - ), - TextButton( - child: const Text('change alpha'), - onPressed: _changeAlpha, - ), - TextButton( - child: const Text('toggle draggable'), - onPressed: _toggleDraggable, - ), - TextButton( - child: const Text('change position'), - onPressed: _changePosition, - ), - TextButton( - child: const Text('toggle visible'), - onPressed: _toggleVisible, - ), - TextButton( - child: const Text('show infoWindow'), - onPressed: _showInfoWindow, - ), - TextButton( - child: const Text('hide infoWindow'), - onPressed: _hideInfoWindow, - ), - TextButton( - child: const Text('change zIndex'), - onPressed: () => _changeZIndex(selectedAnnotationId), - ), - TextButton( - child: Text('Take a snapshot'), - onPressed: () async { - final imageBytes = await this.controller.takeSnapshot(); - setState(() { - _imageBytes = imageBytes; - }); - }, - ), - Container( - decoration: BoxDecoration(color: Colors.blueGrey[50]), - height: 180, - child: - _imageBytes != null ? Image.memory(_imageBytes!) : null, - ), - ], - ), - ) - ], - ), - ); + return const PlaceAnnotationBody(); } } diff --git a/example/lib/scrolling_map.dart b/example/lib/scrolling_map.dart index 4b6143a..3e043c7 100644 --- a/example/lib/scrolling_map.dart +++ b/example/lib/scrolling_map.dart @@ -8,24 +8,14 @@ import 'package:apple_maps_flutter/apple_maps_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'page.dart'; -class ScrollingMapPage extends ExamplePage { - ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); - - @override - Widget build(BuildContext context) { - return const ScrollingMapBody(); - } -} - class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody(); - final LatLng center = const LatLng(32.080664, 34.9563837); + const ScrollingMapBody(); + @override Widget build(BuildContext context) { return SafeArea( @@ -113,3 +103,12 @@ class ScrollingMapBody extends StatelessWidget { ); } } + +class ScrollingMapPage extends ExamplePage { + ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); + + @override + Widget build(BuildContext context) { + return const ScrollingMapBody(); + } +} diff --git a/example/test_driver/test_widgets.dart b/example/test_driver/test_widgets.dart index d2c27fd..5656c9f 100644 --- a/example/test_driver/test_widgets.dart +++ b/example/test_driver/test_widgets.dart @@ -8,5 +8,5 @@ import 'package:flutter/widgets.dart'; Future pumpWidget(Widget widget) { runApp(widget); - return WidgetsBinding.instance!.endOfFrame; + return WidgetsBinding.instance.endOfFrame; } diff --git a/lib/apple_maps_flutter.dart b/lib/apple_maps_flutter.dart index dccdfc8..84aff66 100644 --- a/lib/apple_maps_flutter.dart +++ b/lib/apple_maps_flutter.dart @@ -5,31 +5,28 @@ library apple_maps_flutter; import 'dart:async'; -import 'dart:typed_data'; -import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; part 'src/annotation.dart'; part 'src/annotation_updates.dart'; part 'src/apple_map.dart'; part 'src/bitmap.dart'; -part 'src/cap.dart'; part 'src/callbacks.dart'; part 'src/camera.dart'; +part 'src/cap.dart'; part 'src/circle.dart'; part 'src/circle_updates.dart'; part 'src/controller.dart'; part 'src/joint_type.dart'; part 'src/location.dart'; part 'src/pattern_item.dart'; -part 'src/polyline.dart'; -part 'src/polyline_updates.dart'; part 'src/polygon.dart'; part 'src/polygon_updates.dart'; -part 'src/ui.dart'; +part 'src/polyline.dart'; +part 'src/polyline_updates.dart'; part 'src/snapshot_options.dart'; +part 'src/ui.dart'; diff --git a/lib/src/annotation.dart b/lib/src/annotation.dart index 17d61cf..5f2e48c 100644 --- a/lib/src/annotation.dart +++ b/lib/src/annotation.dart @@ -4,6 +4,16 @@ part of apple_maps_flutter; +Map _keyByAnnotationId( + Iterable? annotations) { + if (annotations == null) { + return {}; + } + return Map.fromEntries(annotations.map( + (Annotation annotation) => MapEntry( + annotation.annotationId, annotation))); +} + dynamic _offsetToJson(Offset? offset) { if (offset == null) { return null; @@ -11,117 +21,14 @@ dynamic _offsetToJson(Offset? offset) { return [offset.dx, offset.dy]; } -/// Text labels for a [Annotation] info window. -class InfoWindow { - const InfoWindow({ - this.title, - this.snippet, - this.anchor = const Offset(0.5, 0.0), - this.onTap, - }); - - /// Text labels specifying that no text is to be displayed. - static const InfoWindow noText = InfoWindow(); - - /// Text displayed in an info window when the user taps the annotation. - /// - /// A null value means no title. - final String? title; - - /// Additional text displayed below the [title]. - /// - /// A null value means no additional text. - final String? snippet; - - /// The icon image point that will be the anchor of the info window when - /// displayed. - /// - /// The image point is specified in normalized coordinates: An anchor of - /// (0.0, 0.0) means the top left corner of the image. An anchor - /// of (1.0, 1.0) means the bottom right corner of the image. - final Offset anchor; - - /// onTap callback for this [InfoWindow]. - final VoidCallback? onTap; - - /// Creates a new [InfoWindow] object whose values are the same as this instance, - /// unless overwritten by the specified parameters. - InfoWindow copyWith({ - String? titleParam, - String? snippetParam, - Offset? anchorParam, - VoidCallback? onTapParam, - }) { - return InfoWindow( - title: titleParam ?? title, - snippet: snippetParam ?? snippet, - anchor: anchorParam ?? anchor, - onTap: onTapParam ?? onTap, - ); - } - - dynamic _toJson() { - final Map json = {}; - - void addIfPresent(String fieldName, dynamic value) { - if (value != null) { - json[fieldName] = value; - } - } - - addIfPresent('title', title); - addIfPresent('snippet', snippet); - addIfPresent('anchor', _offsetToJson(anchor)); - addIfPresent('consumesTapEvents', onTap != null); - - return json; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other is! InfoWindow) return false; - final InfoWindow typedOther = other; - return title == typedOther.title && - snippet == typedOther.snippet && - anchor == typedOther.anchor && - onTap == typedOther.onTap; - } - - @override - int get hashCode => hashValues(title.hashCode, snippet, anchor); - - @override - String toString() { - return 'InfoWindow{title: $title, snippet: $snippet, anchor: $anchor, consumesTapEvents: ${onTap != null}}'; - } -} - -/// Uniquely identifies a [Annotation] among [AppleMap] annotations. -/// -/// This does not have to be globally unique, only unique among the list. -@immutable -class AnnotationId { - AnnotationId(this.value); - - /// value of the [AnnotationId]. - final String value; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other is! AnnotationId) return false; - final AnnotationId typedOther = other; - return value == typedOther.value; - } - - @override - int get hashCode => value.hashCode; - - @override - String toString() { - return 'AnnotationId{value: $value}'; +List>? _serializeAnnotationSet( + Set? annotations) { + if (annotations == null) { + return null; } + return annotations + .map>((Annotation m) => m._toJson()) + .toList(); } /// Marks a geographical location on the map. @@ -131,32 +38,6 @@ class AnnotationId { /// due to map rotations, tilting, or zooming. @immutable class Annotation { - /// Creates a set of annotation configuration options. - /// - /// Default annotation options. - /// - /// Specifies a annotation that - /// * is fully opaque; [alpha] is 1.0 - /// * has default tap handling; [consumeTapEvents] is false - /// * is stationary; [draggable] is false - /// * has a default icon; [icon] is default Pin Annotation - /// * has no info window text; [infoWindowText] is `InfoWindowText.noText` - /// * is positioned at 0, 0; [position] is `LatLng(0.0, 0.0)` - /// * is visible; [visible] is true - Annotation({ - required this.annotationId, - this.alpha = 1.0, - this.anchor = const Offset(0.5, 1.0), - this.draggable = false, - this.icon = BitmapDescriptor.defaultAnnotation, - this.infoWindow = InfoWindow.noText, - this.position = const LatLng(0.0, 0.0), - this.onTap, - this.visible = true, - this.zIndex = -1, - this.onDragEnd, - }) : assert(0.0 <= alpha && alpha <= 1.0); - /// Uniquely identifies a [Annotation]. final AnnotationId annotationId; @@ -201,6 +82,51 @@ class Annotation { /// earlier, and thus appearing to be closer to the surface of the Earth. double zIndex; + /// Creates a set of annotation configuration options. + /// + /// Default annotation options. + /// + /// Specifies a annotation that + /// * is fully opaque; [alpha] is 1.0 + /// * has default tap handling; [consumeTapEvents] is false + /// * is stationary; [draggable] is false + /// * has a default icon; [icon] is default Pin Annotation + /// * has no info window text; [infoWindowText] is `InfoWindowText.noText` + /// * is positioned at 0, 0; [position] is `LatLng(0.0, 0.0)` + /// * is visible; [visible] is true + Annotation({ + required this.annotationId, + this.alpha = 1.0, + this.anchor = const Offset(0.5, 1.0), + this.draggable = false, + this.icon = BitmapDescriptor.defaultAnnotation, + this.infoWindow = InfoWindow.noText, + this.position = const LatLng(0.0, 0.0), + this.onTap, + this.visible = true, + this.zIndex = -1, + this.onDragEnd, + }) : assert(0.0 <= alpha && alpha <= 1.0); + + @override + int get hashCode => annotationId.hashCode; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! Annotation) return false; + final Annotation typedOther = other; + return annotationId == typedOther.annotationId && + alpha == typedOther.alpha && + anchor == typedOther.anchor && + draggable == typedOther.draggable && + icon == typedOther.icon && + infoWindow == typedOther.infoWindow && + position == typedOther.position && + visible == typedOther.visible && + zIndex == typedOther.zIndex; + } + /// Creates a new [Annotation] object whose values are the same as this instance, /// unless overwritten by the specified parameters. Annotation copyWith({ @@ -231,6 +157,13 @@ class Annotation { ); } + @override + String toString() { + return 'Annotation{annotationId: $annotationId, alpha: $alpha, draggable: $draggable, ' + 'icon: $icon, infoWindow: $infoWindow, position: $position ,visible: $visible, ' + 'onTap: $onTap}, zIndex: $zIndex, onTap: $onTap}'; + } + Map _toJson() { final Map json = {}; @@ -251,50 +184,117 @@ class Annotation { addIfPresent('zIndex', zIndex); return json; } +} + +/// Uniquely identifies a [Annotation] among [AppleMap] annotations. +/// +/// This does not have to be globally unique, only unique among the list. +@immutable +class AnnotationId { + /// value of the [AnnotationId]. + final String value; + + AnnotationId(this.value); + + @override + int get hashCode => value.hashCode; @override bool operator ==(Object other) { if (identical(this, other)) return true; - if (other is! Annotation) return false; - final Annotation typedOther = other; - return annotationId == typedOther.annotationId && - alpha == typedOther.alpha && - anchor == typedOther.anchor && - draggable == typedOther.draggable && - icon == typedOther.icon && - infoWindow == typedOther.infoWindow && - position == typedOther.position && - visible == typedOther.visible && - zIndex == typedOther.zIndex; + if (other is! AnnotationId) return false; + final AnnotationId typedOther = other; + return value == typedOther.value; } - @override - int get hashCode => annotationId.hashCode; - @override String toString() { - return 'Annotation{annotationId: $annotationId, alpha: $alpha, draggable: $draggable, ' - 'icon: $icon, infoWindow: $infoWindow, position: $position ,visible: $visible, ' - 'onTap: $onTap}, zIndex: $zIndex, onTap: $onTap}'; + return 'AnnotationId{value: $value}'; } } -Map _keyByAnnotationId( - Iterable? annotations) { - if (annotations == null) { - return {}; +/// Text labels for a [Annotation] info window. +class InfoWindow { + /// Text labels specifying that no text is to be displayed. + static const InfoWindow noText = InfoWindow(); + + /// Text displayed in an info window when the user taps the annotation. + /// + /// A null value means no title. + final String? title; + + /// Additional text displayed below the [title]. + /// + /// A null value means no additional text. + final String? snippet; + + /// The icon image point that will be the anchor of the info window when + /// displayed. + /// + /// The image point is specified in normalized coordinates: An anchor of + /// (0.0, 0.0) means the top left corner of the image. An anchor + /// of (1.0, 1.0) means the bottom right corner of the image. + final Offset anchor; + + /// onTap callback for this [InfoWindow]. + final VoidCallback? onTap; + + const InfoWindow({ + this.title, + this.snippet, + this.anchor = const Offset(0.5, 0.0), + this.onTap, + }); + + @override + int get hashCode => Object.hash(title.hashCode, snippet, anchor); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! InfoWindow) return false; + final InfoWindow typedOther = other; + return title == typedOther.title && + snippet == typedOther.snippet && + anchor == typedOther.anchor && + onTap == typedOther.onTap; } - return Map.fromEntries(annotations.map( - (Annotation annotation) => MapEntry( - annotation.annotationId, annotation))); -} -List>? _serializeAnnotationSet( - Set? annotations) { - if (annotations == null) { - return null; + /// Creates a new [InfoWindow] object whose values are the same as this instance, + /// unless overwritten by the specified parameters. + InfoWindow copyWith({ + String? titleParam, + String? snippetParam, + Offset? anchorParam, + VoidCallback? onTapParam, + }) { + return InfoWindow( + title: titleParam ?? title, + snippet: snippetParam ?? snippet, + anchor: anchorParam ?? anchor, + onTap: onTapParam ?? onTap, + ); + } + + @override + String toString() { + return 'InfoWindow{title: $title, snippet: $snippet, anchor: $anchor, consumesTapEvents: ${onTap != null}}'; + } + + dynamic _toJson() { + final Map json = {}; + + void addIfPresent(String fieldName, dynamic value) { + if (value != null) { + json[fieldName] = value; + } + } + + addIfPresent('title', title); + addIfPresent('snippet', snippet); + addIfPresent('anchor', _offsetToJson(anchor)); + addIfPresent('consumesTapEvents', onTap != null); + + return json; } - return annotations - .map>((Annotation m) => m._toJson()) - .toList(); } diff --git a/lib/src/annotation_updates.dart b/lib/src/annotation_updates.dart index be0f13a..6f3eec9 100644 --- a/lib/src/annotation_updates.dart +++ b/lib/src/annotation_updates.dart @@ -8,6 +8,11 @@ part of apple_maps_flutter; /// /// Used in [AppleMapController] when the map is updated. class _AnnotationUpdates { + late Set annotationsToAdd; + + late Set annotationIdsToRemove; + late Set annotationsToChange; + /// Computes [_AnnotationUpdates] given previous and current [Annotation]s. _AnnotationUpdates.from(Set? previous, Set? current) { if (previous == null) { @@ -50,9 +55,26 @@ class _AnnotationUpdates { annotationsToChange = _annotationsToChange; } - late Set annotationsToAdd; - late Set annotationIdsToRemove; - late Set annotationsToChange; + @override + int get hashCode => + Object.hash(annotationsToAdd, annotationIdsToRemove, annotationsToChange); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! _AnnotationUpdates) return false; + final _AnnotationUpdates typedOther = other; + return setEquals(annotationsToAdd, typedOther.annotationsToAdd) && + setEquals(annotationIdsToRemove, typedOther.annotationIdsToRemove) && + setEquals(annotationsToChange, typedOther.annotationsToChange); + } + + @override + String toString() { + return '_AnnotationUpdates{annotationsToAdd: $annotationsToAdd, ' + 'annotationIdsToRemove: $annotationIdsToRemove, ' + 'annotationsToChange: $annotationsToChange}'; + } Map _toMap() { final Map updateMap = {}; @@ -74,25 +96,4 @@ class _AnnotationUpdates { return updateMap; } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other is! _AnnotationUpdates) return false; - final _AnnotationUpdates typedOther = other; - return setEquals(annotationsToAdd, typedOther.annotationsToAdd) && - setEquals(annotationIdsToRemove, typedOther.annotationIdsToRemove) && - setEquals(annotationsToChange, typedOther.annotationsToChange); - } - - @override - int get hashCode => - hashValues(annotationsToAdd, annotationIdsToRemove, annotationsToChange); - - @override - String toString() { - return '_AnnotationUpdates{annotationsToAdd: $annotationsToAdd, ' - 'annotationIdsToRemove: $annotationIdsToRemove, ' - 'annotationsToChange: $annotationsToChange}'; - } } diff --git a/lib/src/camera.dart b/lib/src/camera.dart index 09bc24f..8dd827e 100644 --- a/lib/src/camera.dart +++ b/lib/src/camera.dart @@ -8,13 +8,6 @@ part of apple_maps_flutter; /// shown in the map view. Aggregates the camera's [target] geographical /// location, its [zoom] level, [pitch] angle, and [heading]. class CameraPosition { - const CameraPosition({ - required this.target, - this.heading = 0.0, - this.pitch = 0.0, - this.zoom = 0, - }); - /// The camera's bearing in degrees, measured clockwise from north. /// /// A bearing of 0.0, the default, means the camera points north. @@ -41,6 +34,31 @@ class CameraPosition { /// will be silently clamped to the supported range. final double zoom; + const CameraPosition({ + required this.target, + this.heading = 0.0, + this.pitch = 0.0, + this.zoom = 0, + }); + + @override + int get hashCode => Object.hash(heading, target, pitch, zoom); + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) return true; + if (runtimeType != other.runtimeType) return false; + final CameraPosition typedOther = other; + return heading == typedOther.heading && + target == typedOther.target && + pitch == typedOther.pitch && + zoom == typedOther.zoom; + } + + @override + String toString() => + 'CameraPosition(bearing: $heading, target: $target, tilt: $pitch, zoom: $zoom)'; + dynamic _toMap() => { 'target': target._toJson(), 'heading': heading, @@ -60,31 +78,17 @@ class CameraPosition { zoom: json['zoom'], ); } - - @override - bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (runtimeType != other.runtimeType) return false; - final CameraPosition typedOther = other; - return heading == typedOther.heading && - target == typedOther.target && - pitch == typedOther.pitch && - zoom == typedOther.zoom; - } - - @override - int get hashCode => hashValues(heading, target, pitch, zoom); - - @override - String toString() => - 'CameraPosition(bearing: $heading, target: $target, tilt: $pitch, zoom: $zoom)'; } /// Defines a camera move, supporting absolute moves as well as moves relative /// the current position. class CameraUpdate { + final dynamic _json; + CameraUpdate._(this._json); + dynamic _toJson() => _json; + /// Returns a camera update that moves the camera to the specified position. static CameraUpdate newCameraPosition(CameraPosition cameraPosition) { return CameraUpdate._( @@ -98,14 +102,6 @@ class CameraUpdate { return CameraUpdate._(['newLatLng', latLng._toJson()]); } - /// Returns a camera update that moves the camera target to the specified - /// geographical location and zoom level. - static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { - return CameraUpdate._( - ['newLatLngZoom', latLng._toJson(), zoom], - ); - } - /// Returns a camera update that transforms the camera so that the specified /// geographical bounding box is centered in the map view at the greatest /// possible zoom level. A non-zero [padding] insets the bounding box from the @@ -118,6 +114,14 @@ class CameraUpdate { ]); } + /// Returns a camera update that moves the camera target to the specified + /// geographical location and zoom level. + static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { + return CameraUpdate._( + ['newLatLngZoom', latLng._toJson(), zoom], + ); + } + /// Returns a camera update that modifies the camera zoom level by the /// specified amount. The optional [focus] is a screen point whose underlying /// geographical location should be invariant, if possible, by the movement. @@ -153,8 +157,4 @@ class CameraUpdate { static CameraUpdate zoomTo(double zoom) { return CameraUpdate._(['zoomTo', zoom]); } - - final dynamic _json; - - dynamic _toJson() => _json; } diff --git a/lib/src/circle_updates.dart b/lib/src/circle_updates.dart index a7282cd..4e00a9c 100644 --- a/lib/src/circle_updates.dart +++ b/lib/src/circle_updates.dart @@ -8,6 +8,11 @@ part of apple_maps_flutter; /// /// Used in [AppleMapController] when the map is updated. class _CircleUpdates { + late Set circlesToAdd; + + late Set circleIdsToRemove; + late Set circlesToChange; + /// Computes [_CircleUpdates] given previous and current [Circle]s. _CircleUpdates.from(Set? previous, Set? current) { if (previous == null) { @@ -54,26 +59,9 @@ class _CircleUpdates { circlesToChange = _circlesToChange; } - late Set circlesToAdd; - late Set circleIdsToRemove; - late Set circlesToChange; - - Map _toMap() { - final Map updateMap = {}; - - void addIfNonNull(String fieldName, dynamic value) { - if (value != null) { - updateMap[fieldName] = value; - } - } - - addIfNonNull('circlesToAdd', _serializeCircleSet(circlesToAdd)); - addIfNonNull('circlesToChange', _serializeCircleSet(circlesToChange)); - addIfNonNull('circleIdsToRemove', - circleIdsToRemove.map((CircleId m) => m.value).toList()); - - return updateMap; - } + @override + int get hashCode => + Object.hash(circlesToAdd, circleIdsToRemove, circlesToChange); @override bool operator ==(Object other) { @@ -85,14 +73,27 @@ class _CircleUpdates { setEquals(circlesToChange, typedOther.circlesToChange); } - @override - int get hashCode => - hashValues(circlesToAdd, circleIdsToRemove, circlesToChange); - @override String toString() { return '_CircleUpdates{circlesToAdd: $circlesToAdd, ' 'circleIdsToRemove: $circleIdsToRemove, ' 'circlesToChange: $circlesToChange}'; } + + Map _toMap() { + final Map updateMap = {}; + + void addIfNonNull(String fieldName, dynamic value) { + if (value != null) { + updateMap[fieldName] = value; + } + } + + addIfNonNull('circlesToAdd', _serializeCircleSet(circlesToAdd)); + addIfNonNull('circlesToChange', _serializeCircleSet(circlesToChange)); + addIfNonNull('circleIdsToRemove', + circleIdsToRemove.map((CircleId m) => m.value).toList()); + + return updateMap; + } } diff --git a/lib/src/location.dart b/lib/src/location.dart index f8360d0..2e25c0f 100644 --- a/lib/src/location.dart +++ b/lib/src/location.dart @@ -6,6 +6,12 @@ part of apple_maps_flutter; /// A pair of latitude and longitude coordinates, stored as degrees. class LatLng { + /// The latitude in degrees between -90.0 and 90.0, both inclusive. + final double latitude; + + /// The longitude in degrees between -180.0 (inclusive) and 180.0 (exclusive). + final double longitude; + /// Creates a geographical location specified in degrees [latitude] and /// [longitude]. /// @@ -18,11 +24,16 @@ class LatLng { (latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude)), longitude = (longitude + 180.0) % 360.0 - 180.0; - /// The latitude in degrees between -90.0 and 90.0, both inclusive. - final double latitude; + @override + int get hashCode => Object.hash(latitude, longitude); - /// The longitude in degrees between -180.0 (inclusive) and 180.0 (exclusive). - final double longitude; + @override + bool operator ==(Object o) { + return o is LatLng && o.latitude == latitude && o.longitude == longitude; + } + + @override + String toString() => '$runtimeType($latitude, $longitude)'; dynamic _toJson() { return [latitude, longitude]; @@ -34,17 +45,6 @@ class LatLng { } return LatLng(json[0], json[1]); } - - @override - String toString() => '$runtimeType($latitude, $longitude)'; - - @override - bool operator ==(Object o) { - return o is LatLng && o.latitude == latitude && o.longitude == longitude; - } - - @override - int get hashCode => hashValues(latitude, longitude); } /// A latitude/longitude aligned rectangle. @@ -56,6 +56,12 @@ class LatLng { /// * lng ∈ [-180, `northeast.longitude`] ∪ [`southwest.longitude`, 180[, /// if `northeast.longitude` < `southwest.longitude` class LatLngBounds { + /// The southwest corner of the rectangle. + final LatLng southwest; + + /// The northeast corner of the rectangle. + final LatLng northeast; + /// Creates geographical bounding box with the specified corners. /// /// The latitude of the southwest corner cannot be larger than the @@ -63,11 +69,15 @@ class LatLngBounds { LatLngBounds({required this.southwest, required this.northeast}) : assert(southwest.latitude <= northeast.latitude); - /// The southwest corner of the rectangle. - final LatLng southwest; + @override + int get hashCode => Object.hash(southwest, northeast); - /// The northeast corner of the rectangle. - final LatLng northeast; + @override + bool operator ==(Object o) { + return o is LatLngBounds && + o.southwest == southwest && + o.northeast == northeast; + } /// Returns whether this rectangle contains the given [LatLng]. bool contains(LatLng point) { @@ -75,6 +85,11 @@ class LatLngBounds { _containsLongitude(point.longitude); } + @override + String toString() { + return '$runtimeType($southwest, $northeast)'; + } + bool _containsLatitude(double lat) { return (southwest.latitude <= lat) && (lat <= northeast.latitude); } @@ -87,6 +102,11 @@ class LatLngBounds { } } + /// Converts this object to something serializable in JSON. + dynamic _toJson() { + return [southwest._toJson(), northeast._toJson()]; + } + @visibleForTesting static LatLngBounds? fromList(dynamic json) { if (json == null) { @@ -97,24 +117,4 @@ class LatLngBounds { northeast: LatLng._fromJson(json[1])!, ); } - - @override - String toString() { - return '$runtimeType($southwest, $northeast)'; - } - - @override - bool operator ==(Object o) { - return o is LatLngBounds && - o.southwest == southwest && - o.northeast == northeast; - } - - /// Converts this object to something serializable in JSON. - dynamic _toJson() { - return [southwest._toJson(), northeast._toJson()]; - } - - @override - int get hashCode => hashValues(southwest, northeast); } diff --git a/lib/src/polygon_updates.dart b/lib/src/polygon_updates.dart index 76eea3f..3c4b985 100644 --- a/lib/src/polygon_updates.dart +++ b/lib/src/polygon_updates.dart @@ -8,6 +8,11 @@ part of apple_maps_flutter; /// /// Used in [AppleMapController] when the map is updated. class _PolygonUpdates { + late Set polygonsToAdd; + + late Set polygonIdsToRemove; + late Set polygonsToChange; + /// Computes [_PolygonUpdates] given previous and current [Polygon]s. _PolygonUpdates.from(Set? previous, Set? current) { if (previous == null) { @@ -54,26 +59,9 @@ class _PolygonUpdates { polygonsToChange = _polygonsToChange; } - late Set polygonsToAdd; - late Set polygonIdsToRemove; - late Set polygonsToChange; - - Map _toMap() { - final Map updateMap = {}; - - void addIfNonNull(String fieldName, dynamic value) { - if (value != null) { - updateMap[fieldName] = value; - } - } - - addIfNonNull('polygonsToAdd', _serializePolygonSet(polygonsToAdd)); - addIfNonNull('polygonsToChange', _serializePolygonSet(polygonsToChange)); - addIfNonNull('polygonIdsToRemove', - polygonIdsToRemove.map((PolygonId m) => m.value).toList()); - - return updateMap; - } + @override + int get hashCode => + Object.hash(polygonsToAdd, polygonIdsToRemove, polygonsToChange); @override bool operator ==(Object other) { @@ -85,14 +73,27 @@ class _PolygonUpdates { setEquals(polygonsToChange, typedOther.polygonsToChange); } - @override - int get hashCode => - hashValues(polygonsToAdd, polygonIdsToRemove, polygonsToChange); - @override String toString() { return '_PolygonUpdates{polygonsToAdd: $polygonsToAdd, ' 'polygonIdsToRemove: $polygonIdsToRemove, ' 'polygonsToChange: $polygonsToChange}'; } + + Map _toMap() { + final Map updateMap = {}; + + void addIfNonNull(String fieldName, dynamic value) { + if (value != null) { + updateMap[fieldName] = value; + } + } + + addIfNonNull('polygonsToAdd', _serializePolygonSet(polygonsToAdd)); + addIfNonNull('polygonsToChange', _serializePolygonSet(polygonsToChange)); + addIfNonNull('polygonIdsToRemove', + polygonIdsToRemove.map((PolygonId m) => m.value).toList()); + + return updateMap; + } } diff --git a/lib/src/polyline_updates.dart b/lib/src/polyline_updates.dart index b22ba3e..baf813a 100644 --- a/lib/src/polyline_updates.dart +++ b/lib/src/polyline_updates.dart @@ -8,6 +8,11 @@ part of apple_maps_flutter; /// /// Used in [AppleMapController] when the map is updated. class _PolylineUpdates { + late Set polylinesToAdd; + + late Set polylineIdsToRemove; + late Set polylinesToChange; + /// Computes [_PolylineUpdates] given previous and current [Polyline]s. _PolylineUpdates.from(Set? previous, Set? current) { if (previous == null) { @@ -48,26 +53,9 @@ class _PolylineUpdates { polylinesToChange = _polylinesToChange; } - late Set polylinesToAdd; - late Set polylineIdsToRemove; - late Set polylinesToChange; - - Map _toMap() { - final Map updateMap = {}; - - void addIfNonNull(String fieldName, dynamic value) { - if (value != null) { - updateMap[fieldName] = value; - } - } - - addIfNonNull('polylinesToAdd', _serializePolylineSet(polylinesToAdd)); - addIfNonNull('polylinesToChange', _serializePolylineSet(polylinesToChange)); - addIfNonNull('polylineIdsToRemove', - polylineIdsToRemove.map((PolylineId m) => m.value).toList()); - - return updateMap; - } + @override + int get hashCode => + Object.hash(polylinesToAdd, polylineIdsToRemove, polylinesToChange); @override bool operator ==(Object other) { @@ -79,14 +67,27 @@ class _PolylineUpdates { setEquals(polylinesToChange, typedOther.polylinesToChange); } - @override - int get hashCode => - hashValues(polylinesToAdd, polylineIdsToRemove, polylinesToChange); - @override String toString() { return '_PolylineUpdates{polylinesToAdd: $polylinesToAdd, ' 'polylineIdsToRemove: $polylineIdsToRemove, ' 'polylinesToChange: $polylinesToChange}'; } + + Map _toMap() { + final Map updateMap = {}; + + void addIfNonNull(String fieldName, dynamic value) { + if (value != null) { + updateMap[fieldName] = value; + } + } + + addIfNonNull('polylinesToAdd', _serializePolylineSet(polylinesToAdd)); + addIfNonNull('polylinesToChange', _serializePolylineSet(polylinesToChange)); + addIfNonNull('polylineIdsToRemove', + polylineIdsToRemove.map((PolylineId m) => m.value).toList()); + + return updateMap; + } } diff --git a/lib/src/snapshot_options.dart b/lib/src/snapshot_options.dart index 222c46d..b7a8eb4 100644 --- a/lib/src/snapshot_options.dart +++ b/lib/src/snapshot_options.dart @@ -1,6 +1,11 @@ part of apple_maps_flutter; class SnapshotOptions { + final bool showBuildings; + + final bool showPointsOfInterest; + final bool showAnnotations; + final bool showOverlays; const SnapshotOptions({ this.showBuildings = true, this.showPointsOfInterest = true, @@ -8,10 +13,23 @@ class SnapshotOptions { this.showOverlays = true, }); - final bool showBuildings; - final bool showPointsOfInterest; - final bool showAnnotations; - final bool showOverlays; + @override + int get hashCode => Object.hash(showBuildings, showPointsOfInterest); + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) return true; + if (runtimeType != other.runtimeType) return false; + final SnapshotOptions typedOther = other; + return showBuildings == typedOther.showBuildings && + showPointsOfInterest == typedOther.showPointsOfInterest && + showAnnotations == typedOther.showAnnotations && + showOverlays == typedOther.showOverlays; + } + + @override + String toString() => + 'SnapshotOptions(showBuildings: $showBuildings, showPointsOfInterest: $showPointsOfInterest, showAnnotations: $showAnnotations, showOverlays: $showOverlays)'; dynamic _toMap() => { 'showBuildings': showBuildings, @@ -32,22 +50,4 @@ class SnapshotOptions { showOverlays: json['showOverlays'], ); } - - @override - bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (runtimeType != other.runtimeType) return false; - final SnapshotOptions typedOther = other; - return showBuildings == typedOther.showBuildings && - showPointsOfInterest == typedOther.showPointsOfInterest && - showAnnotations == typedOther.showAnnotations && - showOverlays == typedOther.showOverlays; - } - - @override - int get hashCode => hashValues(showBuildings, showPointsOfInterest); - - @override - String toString() => - 'SnapshotOptions(showBuildings: $showBuildings, showPointsOfInterest: $showPointsOfInterest, showAnnotations: $showAnnotations, showOverlays: $showOverlays)'; } diff --git a/lib/src/ui.dart b/lib/src/ui.dart index d650a2a..1dc577f 100644 --- a/lib/src/ui.dart +++ b/lib/src/ui.dart @@ -4,45 +4,25 @@ part of apple_maps_flutter; -/// Type of map tiles to display. -enum MapType { - /// Normal tiles (traffic and labels, subtle terrain information). - standard, - - /// Satellite imaging tiles (aerial photos) - satellite, - - /// Hybrid tiles (satellite images with some labels/overlays) - hybrid, -} - -enum TrackingMode { - // the user's location is not followed - none, - - // the map follows the user's location - follow, - - // the map follows the user's location and heading - followWithHeading, -} - /// Bounds for the map camera target. // Used with [AppleMapOptions] to wrap a [LatLngBounds] value. This allows // distinguishing between specifying an unbounded target (null `LatLngBounds`) // from not specifying anything (null `CameraTargetBounds`). class CameraTargetBounds { - /// Creates a camera target bounds with the specified bounding box, or null - /// to indicate that the camera target is not bounded. - const CameraTargetBounds(this.bounds); + /// Unbounded camera target. + static const CameraTargetBounds unbounded = CameraTargetBounds(null); /// The geographical bounding box for the map camera target. /// /// A null value means the camera target is unbounded. final LatLngBounds? bounds; - /// Unbounded camera target. - static const CameraTargetBounds unbounded = CameraTargetBounds(null); + /// Creates a camera target bounds with the specified bounding box, or null + /// to indicate that the camera target is not bounded. + const CameraTargetBounds(this.bounds); + + @override + int get hashCode => bounds.hashCode; @override bool operator ==(dynamic other) { @@ -52,18 +32,28 @@ class CameraTargetBounds { return bounds == typedOther.bounds; } - @override - int get hashCode => bounds.hashCode; - @override String toString() { return 'CameraTargetBounds(bounds: $bounds)'; } } +/// Type of map tiles to display. +enum MapType { + /// Normal tiles (traffic and labels, subtle terrain information). + standard, + + /// Satellite imaging tiles (aerial photos) + satellite, + + /// Hybrid tiles (satellite images with some labels/overlays) + hybrid, +} + class MinMaxZoomPreference { - const MinMaxZoomPreference(this.minZoom, this.maxZoom) - : assert(minZoom == null || maxZoom == null || minZoom <= maxZoom); + /// Unbounded zooming. + static const MinMaxZoomPreference unbounded = + MinMaxZoomPreference(null, null); /// The preferred minimum zoom level or null, if unbounded from below. final double? minZoom; @@ -71,11 +61,11 @@ class MinMaxZoomPreference { /// The preferred maximum zoom level or null, if unbounded from above. final double? maxZoom; - /// Unbounded zooming. - static const MinMaxZoomPreference unbounded = - MinMaxZoomPreference(null, null); + const MinMaxZoomPreference(this.minZoom, this.maxZoom) + : assert(minZoom == null || maxZoom == null || minZoom <= maxZoom); - dynamic _toJson() => [minZoom, maxZoom]; + @override + int get hashCode => Object.hash(minZoom, maxZoom); @override bool operator ==(dynamic other) { @@ -85,11 +75,21 @@ class MinMaxZoomPreference { return minZoom == typedOther.minZoom && maxZoom == typedOther.maxZoom; } - @override - int get hashCode => hashValues(minZoom, maxZoom); - @override String toString() { return 'MinMaxZoomPreference(minZoom: $minZoom, maxZoom: $maxZoom)'; } + + dynamic _toJson() => [minZoom, maxZoom]; +} + +enum TrackingMode { + // the user's location is not followed + none, + + // the map follows the user's location + follow, + + // the map follows the user's location and heading + followWithHeading, } diff --git a/pubspec.yaml b/pubspec.yaml index ff7ca9e..a0a6e5b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,13 +1,13 @@ name: apple_maps_flutter description: This plugin uses the Flutter platform view to display an Apple Maps widget. -version: 1.3.0 +version: 1.4.0 homepage: https://luisthein.de repository: https://github.com/LuisThein/apple_maps_flutter issue_tracker: https://github.com/LuisThein/apple_maps_flutter/issues environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.17.0" + sdk: '>=2.17.0 <4.0.0' + flutter: ">=3.7.0" dependencies: flutter: From 0177e78a165ab5d858481116ec4c4a3d134861ea Mon Sep 17 00:00:00 2001 From: nimr77 Date: Tue, 20 Oct 2026 14:00:36 +0000 Subject: [PATCH 2/7] feat: Implement overlays for polygons, polylines, and circles in Apple Maps Flutter plugin - Added FlutterOverlay protocol for shared overlay functionality. - Created FlutterPolygon class to represent polygons with customizable properties. - Implemented PolygonController for managing polygon overlays, including adding, changing, and removing polygons. - Introduced FlutterPolyline class for polylines with customizable styles and properties. - Developed PolylineController for managing polyline overlays. - Added FlutterCircle class for circle overlays with customizable properties. - Implemented CircleController for managing circle overlays. - Created utility classes for JSON conversion and touch handling. - Updated plugin registration to use SwiftAppleMapsFlutterPlugin. - Enhanced pubspec.yaml to support Flutter 3.4.0 and Dart 3.0.0. --- example/ios/Flutter/AppFrameworkInfo.plist | 2 - example/ios/Podfile | 2 +- example/ios/Podfile.lock | 12 +-- example/ios/Runner.xcodeproj/project.pbxproj | 74 ++++++++++++------- .../xcshareddata/xcschemes/Runner.xcscheme | 23 +++++- example/ios/Runner/AppDelegate.swift | 11 ++- example/ios/Runner/Info.plist | 27 ++++++- ios/.gitignore | 2 + ios/Assets/.gitkeep | 0 ios/Classes/AppleMapsFlutterPlugin.h | 4 - ios/Classes/AppleMapsFlutterPlugin.m | 8 -- ios/apple_maps_flutter.podspec | 5 +- .../Annotations/AnnotationController.swift | 1 + .../Annotations/AnnotationDelegate.swift | 1 + .../Annotations/AnnotationIcon.swift | 1 + .../Annotations/FlutterAnnotation.swift | 1 + .../Annotations/FlutterAnnotationView.swift | 1 + .../MapView/AppleMapController.swift | 1 + .../MapView/AppleMapsViewFactory.swift | 1 + .../MapView/FlutterMapView.swift | 1 + .../MapView/MapViewExtension.swift | 0 .../Overlays/FlutterOverlay.swift | 1 + .../Overlays/Polygons/FlutterPolygon.swift | 1 + .../Overlays/Polygons/PolygonController.swift | 1 + .../Overlays/Polygons/PolygonDelegate.swift | 0 .../Overlays/Polylines/FlutterPolyline.swift | 1 + .../Polylines/PolylineController.swift | 1 + .../Overlays/Polylines/PolylineDelegate.swift | 0 .../Overlays/circles/CircleController.swift | 1 + .../Overlays/circles/CircleDelegate.swift | 0 .../Overlays/circles/FlutterCircle.swift | 1 + .../SwiftAppleMapsFlutterPlugin.swift | 0 .../Utils/JsonConversion.swift | 0 .../Utils/TouchHandler.swift | 1 + .../apple_maps_flutter}/Utils/Utils.swift | 0 .../models/SnapshotOptions.swift | 0 pubspec.yaml | 6 +- 37 files changed, 129 insertions(+), 63 deletions(-) delete mode 100644 ios/Assets/.gitkeep delete mode 100644 ios/Classes/AppleMapsFlutterPlugin.h delete mode 100644 ios/Classes/AppleMapsFlutterPlugin.m rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Annotations/AnnotationController.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Annotations/AnnotationDelegate.swift (97%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Annotations/AnnotationIcon.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Annotations/FlutterAnnotation.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Annotations/FlutterAnnotationView.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/MapView/AppleMapController.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/MapView/AppleMapsViewFactory.swift (98%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/MapView/FlutterMapView.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/MapView/MapViewExtension.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/FlutterOverlay.swift (94%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/Polygons/FlutterPolygon.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/Polygons/PolygonController.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/Polygons/PolygonDelegate.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/Polylines/FlutterPolyline.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/Polylines/PolylineController.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/Polylines/PolylineDelegate.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/circles/CircleController.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/circles/CircleDelegate.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Overlays/circles/FlutterCircle.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/SwiftAppleMapsFlutterPlugin.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Utils/JsonConversion.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Utils/TouchHandler.swift (99%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/Utils/Utils.swift (100%) rename ios/{Classes => apple_maps_flutter/Sources/apple_maps_flutter}/models/SnapshotOptions.swift (100%) diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..391a902 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 9.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 1e8c3c9..17ed986 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index eff7602..d62e931 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,22 +1,16 @@ PODS: - - apple_maps_flutter (0.0.1): - - Flutter - Flutter (1.0.0) DEPENDENCIES: - - apple_maps_flutter (from `.symlinks/plugins/apple_maps_flutter/ios`) - Flutter (from `Flutter`) EXTERNAL SOURCES: - apple_maps_flutter: - :path: ".symlinks/plugins/apple_maps_flutter/ios" Flutter: :path: Flutter SPEC CHECKSUMS: - apple_maps_flutter: c59725efea39e13e703cde52a1d2b14866ad68a8 - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + Flutter: 71a624a5bc0c04062bf19101d501e466baf2fb47 -PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c +PODFILE CHECKSUM: a938cb79d452854fa9f13c14ded5e356e38f0fc7 -COCOAPODS: 1.11.3 +COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index b74440f..1125509 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,13 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 7E3ED54974DCE25524E09722 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5AB21804E11E2CF6CAB18B54 /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -36,6 +37,9 @@ 5AB21804E11E2CF6CAB18B54 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* apple_maps_flutter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = apple_maps_flutter; path = ../../ios/apple_maps_flutter; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -54,6 +58,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 7E3ED54974DCE25524E09722 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -75,6 +80,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* apple_maps_flutter */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -139,13 +147,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - A76139E19E24CB98ED0622B3 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -156,7 +166,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -174,6 +184,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -200,10 +213,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -236,6 +251,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -248,23 +264,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - A76139E19E24CB98ED0622B3 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -340,7 +339,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -358,7 +357,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = de.luisthein.appleMapsFlutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -414,7 +416,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -463,7 +465,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -482,7 +484,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = de.luisthein.appleMapsFlutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -501,7 +506,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = de.luisthein.appleMapsFlutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -534,6 +542,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6..0bd6d42 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + @@ -45,11 +64,13 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 70693e4..c30b367 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,13 +1,16 @@ -import UIKit import Flutter +import UIKit -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } } diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 6f300aa..7d9e657 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -24,6 +26,29 @@ NSLocationWhenInUseUsageDescription This App needs permission to access your location in order to show it on the map. + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -43,7 +68,5 @@ UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - diff --git a/ios/.gitignore b/ios/.gitignore index 710ec6c..e4f8f29 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -9,6 +9,8 @@ profile DerivedData/ build/ +.build/ +.swiftpm/ GeneratedPluginRegistrant.h GeneratedPluginRegistrant.m diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ios/Classes/AppleMapsFlutterPlugin.h b/ios/Classes/AppleMapsFlutterPlugin.h deleted file mode 100644 index 7ca9bfb..0000000 --- a/ios/Classes/AppleMapsFlutterPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface AppleMapsFlutterPlugin : NSObject -@end diff --git a/ios/Classes/AppleMapsFlutterPlugin.m b/ios/Classes/AppleMapsFlutterPlugin.m deleted file mode 100644 index 1316409..0000000 --- a/ios/Classes/AppleMapsFlutterPlugin.m +++ /dev/null @@ -1,8 +0,0 @@ -#import "AppleMapsFlutterPlugin.h" -#import - -@implementation AppleMapsFlutterPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftAppleMapsFlutterPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/ios/apple_maps_flutter.podspec b/ios/apple_maps_flutter.podspec index 1bad179..2eb7387 100644 --- a/ios/apple_maps_flutter.podspec +++ b/ios/apple_maps_flutter.podspec @@ -12,10 +12,9 @@ A new flutter plugin project. s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' + s.source_files = 'apple_maps_flutter/Sources/apple_maps_flutter/**/*.swift' s.dependency 'Flutter' - s.ios.deployment_target = '9.0' + s.ios.deployment_target = '13.0' s.swift_version = '5.0' end diff --git a/ios/Classes/Annotations/AnnotationController.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationController.swift similarity index 99% rename from ios/Classes/Annotations/AnnotationController.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationController.swift index 09bf6b2..9aadb75 100644 --- a/ios/Classes/Annotations/AnnotationController.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationController.swift @@ -1,3 +1,4 @@ +import Flutter // // AnnotationController.swift // apple_maps_flutter diff --git a/ios/Classes/Annotations/AnnotationDelegate.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationDelegate.swift similarity index 97% rename from ios/Classes/Annotations/AnnotationDelegate.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationDelegate.swift index 83f6798..37fc23e 100644 --- a/ios/Classes/Annotations/AnnotationDelegate.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationDelegate.swift @@ -1,3 +1,4 @@ +import Flutter // // AnnotationDelegate.swift // apple_maps_flutter diff --git a/ios/Classes/Annotations/AnnotationIcon.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationIcon.swift similarity index 99% rename from ios/Classes/Annotations/AnnotationIcon.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationIcon.swift index 85b04f7..91f6c1a 100644 --- a/ios/Classes/Annotations/AnnotationIcon.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/AnnotationIcon.swift @@ -1,3 +1,4 @@ +import Flutter // // AnnotationIcon.swift // apple_maps_flutter diff --git a/ios/Classes/Annotations/FlutterAnnotation.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/FlutterAnnotation.swift similarity index 99% rename from ios/Classes/Annotations/FlutterAnnotation.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/FlutterAnnotation.swift index 5e29e79..db25ac5 100644 --- a/ios/Classes/Annotations/FlutterAnnotation.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/FlutterAnnotation.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterAnnotation.swift // apple_maps_flutter diff --git a/ios/Classes/Annotations/FlutterAnnotationView.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/FlutterAnnotationView.swift similarity index 99% rename from ios/Classes/Annotations/FlutterAnnotationView.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/FlutterAnnotationView.swift index 70e4b26..d8fcb8c 100644 --- a/ios/Classes/Annotations/FlutterAnnotationView.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Annotations/FlutterAnnotationView.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterAnnotationView.swift // apple_maps_flutter diff --git a/ios/Classes/MapView/AppleMapController.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift similarity index 99% rename from ios/Classes/MapView/AppleMapController.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift index 85e7a98..1345dcf 100644 --- a/ios/Classes/MapView/AppleMapController.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift @@ -1,3 +1,4 @@ +import Flutter // // AppleMapController.swift // apple_maps_flutter diff --git a/ios/Classes/MapView/AppleMapsViewFactory.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapsViewFactory.swift similarity index 98% rename from ios/Classes/MapView/AppleMapsViewFactory.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapsViewFactory.swift index 2d01ad6..97ee0fa 100644 --- a/ios/Classes/MapView/AppleMapsViewFactory.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapsViewFactory.swift @@ -1,3 +1,4 @@ +import Flutter // // AppleMapsViewFactory.swift // apple_maps_flutter diff --git a/ios/Classes/MapView/FlutterMapView.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/FlutterMapView.swift similarity index 99% rename from ios/Classes/MapView/FlutterMapView.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/FlutterMapView.swift index 1e733ed..93ffc1c 100644 --- a/ios/Classes/MapView/FlutterMapView.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/FlutterMapView.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterAppleMap.swift // apple_maps_flutter diff --git a/ios/Classes/MapView/MapViewExtension.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/MapViewExtension.swift similarity index 100% rename from ios/Classes/MapView/MapViewExtension.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/MapViewExtension.swift diff --git a/ios/Classes/Overlays/FlutterOverlay.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/FlutterOverlay.swift similarity index 94% rename from ios/Classes/Overlays/FlutterOverlay.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/FlutterOverlay.swift index 1258b3d..c2789e8 100644 --- a/ios/Classes/Overlays/FlutterOverlay.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/FlutterOverlay.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterOverlay.swift // apple_maps_flutter diff --git a/ios/Classes/Overlays/Polygons/FlutterPolygon.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/FlutterPolygon.swift similarity index 99% rename from ios/Classes/Overlays/Polygons/FlutterPolygon.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/FlutterPolygon.swift index 190b080..a346fc6 100644 --- a/ios/Classes/Overlays/Polygons/FlutterPolygon.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/FlutterPolygon.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterPolygon.swift // apple_maps_flutter diff --git a/ios/Classes/Overlays/Polygons/PolygonController.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/PolygonController.swift similarity index 99% rename from ios/Classes/Overlays/Polygons/PolygonController.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/PolygonController.swift index 87c5113..08b0b29 100644 --- a/ios/Classes/Overlays/Polygons/PolygonController.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/PolygonController.swift @@ -1,3 +1,4 @@ +import Flutter // // PolygonController.swift // apple_maps_flutter diff --git a/ios/Classes/Overlays/Polygons/PolygonDelegate.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/PolygonDelegate.swift similarity index 100% rename from ios/Classes/Overlays/Polygons/PolygonDelegate.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polygons/PolygonDelegate.swift diff --git a/ios/Classes/Overlays/Polylines/FlutterPolyline.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/FlutterPolyline.swift similarity index 99% rename from ios/Classes/Overlays/Polylines/FlutterPolyline.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/FlutterPolyline.swift index 8b583db..79c2454 100644 --- a/ios/Classes/Overlays/Polylines/FlutterPolyline.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/FlutterPolyline.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterPolyline.swift // apple_maps_flutter diff --git a/ios/Classes/Overlays/Polylines/PolylineController.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/PolylineController.swift similarity index 99% rename from ios/Classes/Overlays/Polylines/PolylineController.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/PolylineController.swift index 8e29757..9412ac2 100644 --- a/ios/Classes/Overlays/Polylines/PolylineController.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/PolylineController.swift @@ -1,3 +1,4 @@ +import Flutter // // PolylineController.swift // apple_maps_flutter diff --git a/ios/Classes/Overlays/Polylines/PolylineDelegate.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/PolylineDelegate.swift similarity index 100% rename from ios/Classes/Overlays/Polylines/PolylineDelegate.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/Polylines/PolylineDelegate.swift diff --git a/ios/Classes/Overlays/circles/CircleController.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/CircleController.swift similarity index 99% rename from ios/Classes/Overlays/circles/CircleController.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/CircleController.swift index bf89771..37ab71a 100644 --- a/ios/Classes/Overlays/circles/CircleController.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/CircleController.swift @@ -1,3 +1,4 @@ +import Flutter // // CircleController.swift // apple_maps_flutter diff --git a/ios/Classes/Overlays/circles/CircleDelegate.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/CircleDelegate.swift similarity index 100% rename from ios/Classes/Overlays/circles/CircleDelegate.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/CircleDelegate.swift diff --git a/ios/Classes/Overlays/circles/FlutterCircle.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/FlutterCircle.swift similarity index 99% rename from ios/Classes/Overlays/circles/FlutterCircle.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/FlutterCircle.swift index 7371b32..07dd98c 100644 --- a/ios/Classes/Overlays/circles/FlutterCircle.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Overlays/circles/FlutterCircle.swift @@ -1,3 +1,4 @@ +import Flutter // // FlutterCircle.swift // apple_maps_flutter diff --git a/ios/Classes/SwiftAppleMapsFlutterPlugin.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/SwiftAppleMapsFlutterPlugin.swift similarity index 100% rename from ios/Classes/SwiftAppleMapsFlutterPlugin.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/SwiftAppleMapsFlutterPlugin.swift diff --git a/ios/Classes/Utils/JsonConversion.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/JsonConversion.swift similarity index 100% rename from ios/Classes/Utils/JsonConversion.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/JsonConversion.swift diff --git a/ios/Classes/Utils/TouchHandler.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/TouchHandler.swift similarity index 99% rename from ios/Classes/Utils/TouchHandler.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/TouchHandler.swift index e14644f..8241377 100644 --- a/ios/Classes/Utils/TouchHandler.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/TouchHandler.swift @@ -1,3 +1,4 @@ +import Flutter // // TouchHandler.swift // apple_maps_flutter diff --git a/ios/Classes/Utils/Utils.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/Utils.swift similarity index 100% rename from ios/Classes/Utils/Utils.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/Utils/Utils.swift diff --git a/ios/Classes/models/SnapshotOptions.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/models/SnapshotOptions.swift similarity index 100% rename from ios/Classes/models/SnapshotOptions.swift rename to ios/apple_maps_flutter/Sources/apple_maps_flutter/models/SnapshotOptions.swift diff --git a/pubspec.yaml b/pubspec.yaml index a0a6e5b..f9ec716 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,8 +6,8 @@ repository: https://github.com/LuisThein/apple_maps_flutter issue_tracker: https://github.com/LuisThein/apple_maps_flutter/issues environment: - sdk: '>=2.17.0 <4.0.0' - flutter: ">=3.7.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.44.0" dependencies: flutter: @@ -29,7 +29,7 @@ flutter: plugin: platforms: ios: - pluginClass: AppleMapsFlutterPlugin + pluginClass: SwiftAppleMapsFlutterPlugin # To add assets to your plugin package, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg From 3fc5c94ead97e3a143891f8ca959ab9922b76f84 Mon Sep 17 00:00:00 2001 From: nimr77 Date: Tue, 20 Oct 2026 14:00:40 +0000 Subject: [PATCH 3/7] feat: Add Swift package support and generated files for Flutter integration --- example/ios/Flutter/ephemeral/.swift_pm.lock | 0 .../Package.swift | 30 +++++++++++++++++ .../FlutterGeneratedPluginSwiftPackage.swift | 3 ++ .../Flutter/ephemeral/flutter_lldb_helper.py | 32 +++++++++++++++++++ .../ios/Flutter/ephemeral/flutter_lldbinit | 5 +++ .../ephemeral/flutter_native_integration.env | 13 ++++++++ ios/apple_maps_flutter/Package.swift | 24 ++++++++++++++ 7 files changed, 107 insertions(+) create mode 100644 example/ios/Flutter/ephemeral/.swift_pm.lock create mode 100644 example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift create mode 100644 example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift create mode 100644 example/ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 example/ios/Flutter/ephemeral/flutter_lldbinit create mode 100644 example/ios/Flutter/ephemeral/flutter_native_integration.env create mode 100644 ios/apple_maps_flutter/Package.swift diff --git a/example/ios/Flutter/ephemeral/.swift_pm.lock b/example/ios/Flutter/ephemeral/.swift_pm.lock new file mode 100644 index 0000000..e69de29 diff --git a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift new file mode 100644 index 0000000..83bf8c8 --- /dev/null +++ b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. +// +// Generated file. Do not edit. +// + +import PackageDescription + +let package = Package( + name: "FlutterGeneratedPluginSwiftPackage", + platforms: [ + .iOS("15.0") + ], + products: [ + .library(name: "FlutterGeneratedPluginSwiftPackage", type: .static, targets: ["FlutterGeneratedPluginSwiftPackage"]) + ], + dependencies: [ + .package(name: "apple_maps_flutter", path: "../.packages/apple_maps_flutter"), + .package(name: "FlutterFramework", path: "../.packages/FlutterFramework") + ], + targets: [ + .target( + name: "FlutterGeneratedPluginSwiftPackage", + dependencies: [ + .product(name: "apple-maps-flutter", package: "apple_maps_flutter"), + .product(name: "FlutterFramework", package: "FlutterFramework") + ] + ) + ] +) diff --git a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift new file mode 100644 index 0000000..a30d95a --- /dev/null +++ b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift @@ -0,0 +1,3 @@ +// +// Generated file. Do not edit. +// diff --git a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000..a88caf9 --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/example/ios/Flutter/ephemeral/flutter_lldbinit b/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000..e3ba6fb --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/example/ios/Flutter/ephemeral/flutter_native_integration.env b/example/ios/Flutter/ephemeral/flutter_native_integration.env new file mode 100644 index 0000000..03d45f8 --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_native_integration.env @@ -0,0 +1,13 @@ +FLUTTER_ROOT=/Users/admin/Development/flutter +FLUTTER_APPLICATION_PATH=/Users/admin/Projects/apple_maps_flutter/example +FLUTTER_FRAMEWORK_SWIFT_PACKAGE_PATH=/Users/admin/Projects/apple_maps_flutter/example/ios/Flutter/ephemeral/Packages/.packages/FlutterFramework +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib/main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +DART_DEFINES=RkxVVFRFUl9CVUlMRF9OQU1FPTEuMC4w,RkxVVFRFUl9CVUlMRF9OVU1CRVI9MQ==,RkxVVFRFUl9WRVJTSU9OPTMuNDcuNw==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049YWJhZjljNTIzNw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049ZGViMjg3NDgxZQ==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMy41 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=false +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=/Users/admin/Projects/apple_maps_flutter/example/.dart_tool/package_config.json diff --git a/ios/apple_maps_flutter/Package.swift b/ios/apple_maps_flutter/Package.swift new file mode 100644 index 0000000..6b154d0 --- /dev/null +++ b/ios/apple_maps_flutter/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 5.9 + +import PackageDescription + +let package = Package( + name: "apple_maps_flutter", + platforms: [ + .iOS("13.0") + ], + products: [ + .library(name: "apple-maps-flutter", targets: ["apple_maps_flutter"]) + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework") + ], + targets: [ + .target( + name: "apple_maps_flutter", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework") + ] + ) + ] +) From f4e0bcba75cf049e67262328688e902a00a10cca Mon Sep 17 00:00:00 2001 From: nimr77 Date: Tue, 20 Oct 2026 14:00:52 +0000 Subject: [PATCH 4/7] fix: update filter ID and regex for filtered resources in Android project configuration --- example/android/.project | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/android/.project b/example/android/.project index f181704..994fbc4 100644 --- a/example/android/.project +++ b/example/android/.project @@ -16,12 +16,12 @@ - 1601924530843 + 1792504847059 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ From b960a1a78dee601f1c2a4f0cafd11d006390b2c0 Mon Sep 17 00:00:00 2001 From: nimr77 Date: Tue, 20 Oct 2026 14:05:58 +0000 Subject: [PATCH 5/7] feat: update version to 1.5.0 and add changelog for Swift Package Manager support --- CHANGELOG.md | 4 ++++ .../FlutterGeneratedPluginSwiftPackage/Package.swift | 9 ++------- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f793020..de2179b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.5.0 + +* Swift Package Manager support (requires Flutter 3.44+); CocoaPods podspec kept in sync + ## 1.4.0 * Flutter 3.27.1 compatibility, replace `ui.hash*` with `Object.hash* diff --git a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift index 83bf8c8..ef4bd1b 100644 --- a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift +++ b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift @@ -15,16 +15,11 @@ let package = Package( .library(name: "FlutterGeneratedPluginSwiftPackage", type: .static, targets: ["FlutterGeneratedPluginSwiftPackage"]) ], dependencies: [ - .package(name: "apple_maps_flutter", path: "../.packages/apple_maps_flutter"), - .package(name: "FlutterFramework", path: "../.packages/FlutterFramework") + ], targets: [ .target( - name: "FlutterGeneratedPluginSwiftPackage", - dependencies: [ - .product(name: "apple-maps-flutter", package: "apple_maps_flutter"), - .product(name: "FlutterFramework", package: "FlutterFramework") - ] + name: "FlutterGeneratedPluginSwiftPackage" ) ] ) diff --git a/pubspec.yaml b/pubspec.yaml index f9ec716..27f2232 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: apple_maps_flutter description: This plugin uses the Flutter platform view to display an Apple Maps widget. -version: 1.4.0 +version: 1.5.0 homepage: https://luisthein.de repository: https://github.com/LuisThein/apple_maps_flutter issue_tracker: https://github.com/LuisThein/apple_maps_flutter/issues From 3111d93f19489b862dd2ae39712435a682d87713 Mon Sep 17 00:00:00 2001 From: nimr77 Date: Tue, 20 Oct 2026 14:06:05 +0000 Subject: [PATCH 6/7] fix: correct formatting in CHANGELOG.md for consistency --- CHANGELOG.md | 104 ++++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de2179b..705ba36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,150 +2,152 @@ ## 1.5.0 -* Swift Package Manager support (requires Flutter 3.44+); CocoaPods podspec kept in sync +- Swift Package Manager support (requires Flutter 3.44+); CocoaPods podspec kept in sync ## 1.4.0 -* Flutter 3.27.1 compatibility, replace `ui.hash*` with `Object.hash* +- Flutter 3.27.1 compatibility, replace `ui.hash*` with `Object.hash\* ## 1.3.0 -* Animate marker position changes instead of removing and re-adding -* Fix Fatal error: Attempted to read an unowned reference but the object was already deallocated -* Fixed an issue where onCameraMove was not invoked by double-tapping -* Added insetsLayoutMarginsFromSafeArea +- Animate marker position changes instead of removing and re-adding +- Fix Fatal error: Attempted to read an unowned reference but the object was already deallocated +- Fixed an issue where onCameraMove was not invoked by double-tapping +- Added insetsLayoutMarginsFromSafeArea ## 1.2.0 -* Added a `markerAnnotationWithHue()` and `pinAnnotationWithHue()` method to allow custom marker/pin colors +- Added a `markerAnnotationWithHue()` and `pinAnnotationWithHue()` method to allow custom marker/pin colors ## 1.1.0 -* Added Annotation zIndex -* Added posibility to take snapshots of the map +- Added Annotation zIndex +- Added posibility to take snapshots of the map ## 1.0.3 -* Fixes an issue where mapController.moveCamera would animate the camera transition -* To animate a camera movement, mapController.animateCamera should be used instead +- Fixes an issue where mapController.moveCamera would animate the camera transition +- To animate a camera movement, mapController.animateCamera should be used instead ## 1.0.2 -* Removed Android folder to fix build failures +- Removed Android folder to fix build failures ## 1.0.1 -* Fixes memory leak -* Adds ability to take snapshots of the map +- Fixes memory leak +- Adds ability to take snapshots of the map + ## 1.0.0 Thanks to @jonbhanson -* Adds null safety. -* Refreshes the example app. -* Updates .gitignore and removes files that should not be tracked. + +- Adds null safety. +- Refreshes the example app. +- Updates .gitignore and removes files that should not be tracked. ## 0.1.4 -* Animate to bounds was added. (Thanks to @nghiashiyi) -* Fixed an issue where the user location was only displayed in `authorizationInUse` status. (Thanks to @zgosalvez) +- Animate to bounds was added. (Thanks to @nghiashiyi) +- Fixed an issue where the user location was only displayed in `authorizationInUse` status. (Thanks to @zgosalvez) -* minor fixes +- minor fixes ## 0.1.3 -* Thanks to @maxiundtesa the getter for the current zoomLevel was added -* iOS build failure for Flutter modules was fixed +- Thanks to @maxiundtesa the getter for the current zoomLevel was added +- iOS build failure for Flutter modules was fixed ## 0.1.2+5 -* Fixed build failure -* Added anchor param to Annotation -* Added missing comparison of Overlay coordinates, which caused +- Fixed build failure +- Added anchor param to Annotation +- Added missing comparison of Overlay coordinates, which caused Circles, Annotations, Polylines and Ploygons to not update correctly on coordinate changes. ## 0.1.2+4 -* Added configurable Anchor for infoWindows +- Added configurable Anchor for infoWindows ## 0.1.2+3 -* Fixed the offset of custom markers +- Fixed the offset of custom markers ## 0.1.2+2 -* Fixed the onTap event for Annotation Callouts +- Fixed the onTap event for Annotation Callouts ## 0.1.2+1 -* Added custom annotation icons from byte data -* Fixed scaling of icons from assets => see: https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets +- Added custom annotation icons from byte data +- Fixed scaling of icons from assets => see: https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets ## 0.1.2 -* Annotation rework: - * onTap for InfoWindow added - * Multiline InfoWindow subtitle support - * Overall Annotation handling refactored - * Correct UserTracking Button added +- Annotation rework: + - onTap for InfoWindow added + - Multiline InfoWindow subtitle support + - Overall Annotation handling refactored + - Correct UserTracking Button added ## 0.1.1+2 -* Fixed map freezing when setState is being called +- Fixed map freezing when setState is being called ## 0.1.1+1 -* Fixed Polygon and Circle Tap events. +- Fixed Polygon and Circle Tap events. ## 0.1.1 -* Added markerAnnotation as selectable annotation type. +- Added markerAnnotation as selectable annotation type. ## 0.1.0 -* Added ability to place circles on the map. +- Added ability to place circles on the map. ## 0.0.7 -* Added ability to place polygons on the map. +- Added ability to place polygons on the map. ## 0.0.6+4 -* Fixed build issues +- Fixed build issues ## 0.0.6+3 -* Fixes issue #6, location permission is only requested if it's actually used. +- Fixes issue #6, location permission is only requested if it's actually used. ## 0.0.6+2 -* Converted iOS code to swift 5. +- Converted iOS code to swift 5. ## 0.0.6+1 -* Changed annotation initialisation, fixes custom annotation icons not showing up on the map. +- Changed annotation initialisation, fixes custom annotation icons not showing up on the map. ## 0.0.6 -* Added ability to add padding to the map +- Added ability to add padding to the map ## 0.0.5 -* Added ability to place polylines. +- Added ability to place polylines. ## 0.0.4 -* Fixed error when updating Annotations on map. +- Fixed error when updating Annotations on map. ## 0.0.3 -* Added getter for visible map region. +- Added getter for visible map region. ## 0.0.2 -* Added zoomBy functionality. -* Added setter for min and max zoom levels. +- Added zoomBy functionality. +- Added setter for min and max zoom levels. ## 0.0.1 -* Initial release. +- Initial release. From aa0be38d19a8a0aa42ba6a5f4b938642ab0fd769 Mon Sep 17 00:00:00 2001 From: nimr77 Date: Thu, 22 Oct 2026 21:29:16 +0000 Subject: [PATCH 7/7] fix(ios): avoid crash on method calls after map view is disposed The channel handler captured the controller as unowned, so any call arriving after the platform view was released (pending widget updates, camera moves, hot restart) aborted with 'Attempted to read an unowned reference but the object was already destroyed'. Capture it weakly and ignore calls once it's gone, and skip Dart-side updates after dispose. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../apple_maps_flutter/MapView/AppleMapController.swift | 8 +++++++- lib/src/apple_map.dart | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift index 1345dcf..e8ca2f3 100644 --- a/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift +++ b/ios/apple_maps_flutter/Sources/apple_maps_flutter/MapView/AppleMapController.swift @@ -60,7 +60,13 @@ public class AppleMapController: NSObject, FlutterPlatformView { } private func setMethodCallHandlers() { - channel.setMethodCallHandler({ [unowned self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + // `weak`, not `unowned`: Dart can still send calls (pending widget + // updates, camera moves) after the platform view was disposed. + channel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + guard let self = self else { + result(nil) + return + } if let args: Dictionary = call.arguments as? Dictionary { switch(call.method) { case "annotations#update": diff --git a/lib/src/apple_map.dart b/lib/src/apple_map.dart index 793a269..4d6a1a9 100644 --- a/lib/src/apple_map.dart +++ b/lib/src/apple_map.dart @@ -235,12 +235,14 @@ class _AppleMapState extends State { return; } final AppleMapController controller = await _controller.future; + if (!mounted) return; controller._updateMapOptions(updates); _appleMapOptions = newOptions; } void _updateAnnotations() async { final AppleMapController controller = await _controller.future; + if (!mounted) return; controller._updateAnnotations(_AnnotationUpdates.from( _annotations.values.toSet(), widget.annotations)); _annotations = _keyByAnnotationId(widget.annotations); @@ -248,6 +250,7 @@ class _AppleMapState extends State { void _updatePolylines() async { final AppleMapController controller = await _controller.future; + if (!mounted) return; controller._updatePolylines( _PolylineUpdates.from(_polylines.values.toSet(), widget.polylines)); _polylines = _keyByPolylineId(widget.polylines); @@ -255,6 +258,7 @@ class _AppleMapState extends State { void _updatePolygons() async { final AppleMapController controller = await _controller.future; + if (!mounted) return; // ignore: unawaited_futures controller._updatePolygons( _PolygonUpdates.from(_polygons.values.toSet(), widget.polygons)); @@ -263,6 +267,7 @@ class _AppleMapState extends State { void _updateCircles() async { final AppleMapController controller = await _controller.future; + if (!mounted) return; // ignore: unawaited_futures controller._updateCircles( _CircleUpdates.from(_circles.values.toSet(), widget.circles)); @@ -275,6 +280,7 @@ class _AppleMapState extends State { widget.initialCameraPosition, this, ); + if (!mounted) return; _controller.complete(controller); widget.onMapCreated?.call(controller); }