From 4315f0bc3a11453bce1439d4b8ab25651e14734d Mon Sep 17 00:00:00 2001 From: Mehmet Esen Date: Wed, 12 Aug 2026 11:18:33 +0300 Subject: [PATCH 1/2] fix(darwin): align overlay insertion index with the level's full array addFlutterOverlay counted only FlutterOverlay peers for the insertion index but applied it against the level's full overlay array, which can also hold tile overlays (FlutterTileOverlay doesn't conform to FlutterOverlay and shares the .aboveRoads default level). A tile overlay in that array skewed every subsequent vector-overlay insert, breaking zIndex order and letting vectors land below the tile. Now the position is found with firstIndex over the full array: insert directly before the first Flutter peer with a strictly greater zIndex, append when there is none. Non-Flutter peers can no longer shift the index. Co-Authored-By: Claude Sonnet 5 --- .../Overlays/FlutterOverlay.swift | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/darwin/mapkit_flutter/Sources/mapkit_flutter/Overlays/FlutterOverlay.swift b/darwin/mapkit_flutter/Sources/mapkit_flutter/Overlays/FlutterOverlay.swift index b09612c..445f14e 100644 --- a/darwin/mapkit_flutter/Sources/mapkit_flutter/Overlays/FlutterOverlay.swift +++ b/darwin/mapkit_flutter/Sources/mapkit_flutter/Overlays/FlutterOverlay.swift @@ -15,20 +15,22 @@ protocol FlutterOverlay: MKOverlay { extension MKMapView { /// Adds an overlay, using `zIndex` as an ordering hint within its level: - /// the overlay lands after every existing Flutter overlay whose zIndex is - /// `<=` its own, so equal indices preserve insertion order. The insertion - /// index is clamped to the level's array bounds. + /// the overlay lands directly before the first Flutter overlay whose + /// zIndex is greater than its own, so equal indices preserve insertion + /// order. Non-Flutter peers (tile overlays) carry no zIndex and never + /// shift the position; with no higher-zIndex peer the overlay is appended + /// on top of the level. func addFlutterOverlay(_ overlay: any FlutterOverlay) { let level = overlay.overlayLevel let peers = self.overlays(in: level) - let index = peers - .compactMap { $0 as? any FlutterOverlay } - .filter { $0.zIndex <= overlay.zIndex } - .count - if index >= peers.count { - addOverlay(overlay, level: level) - } else { + let index = peers.firstIndex { peer in + guard let flutterPeer = peer as? any FlutterOverlay else { return false } + return flutterPeer.zIndex > overlay.zIndex + } + if let index { insertOverlay(overlay, at: index, level: level) + } else { + addOverlay(overlay, level: level) } } From 86fe5b39283be3d792df1f89dddc9de6420be777 Mon Sep 17 00:00:00 2001 From: Mehmet Esen Date: Wed, 12 Aug 2026 11:18:35 +0300 Subject: [PATCH 2/2] perf(darwin): batch annotation adds and removals annotationsToAdd and annotationsToRemove called mapView.addAnnotation/removeAnnotation once per item, each an O(N) pass in MapKit. They now collect items and issue single addAnnotations/removeAnnotations calls, mirroring the batched overlay removal merged in PR #15. Dictionary bookkeeping and the replace-existing-id dedup behavior are unchanged; annotationsToChange keeps its in-place update path. Co-Authored-By: Claude Sonnet 5 --- .../Annotations/AnnotationController.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/darwin/mapkit_flutter/Sources/mapkit_flutter/Annotations/AnnotationController.swift b/darwin/mapkit_flutter/Sources/mapkit_flutter/Annotations/AnnotationController.swift index eb8a3be..a1663d3 100644 --- a/darwin/mapkit_flutter/Sources/mapkit_flutter/Annotations/AnnotationController.swift +++ b/darwin/mapkit_flutter/Sources/mapkit_flutter/Annotations/AnnotationController.swift @@ -86,9 +86,15 @@ extension MapKitViewHost { } func annotationsToAdd(_ annotations: [PlatformAnnotation]) { - for annotation in annotations { - addAnnotation(FlutterAnnotation(fromPlatform: annotation)) + let toAdd = annotations.map(FlutterAnnotation.init(fromPlatform:)) + let stale = toAdd.compactMap { self.annotationsById.removeValue(forKey: $0.id) } + if !stale.isEmpty { + self.mapView.removeAnnotations(stale) } + for annotation in toAdd { + self.annotationsById[annotation.id] = annotation + } + self.mapView.addAnnotations(toAdd) } func annotationsToChange(_ annotations: [PlatformAnnotation]) { @@ -107,8 +113,9 @@ extension MapKitViewHost { } func annotationsToRemove(_ annotationIds: [String]) { - for annotationId in annotationIds { - removeAnnotation(id: annotationId) + let toRemove = annotationIds.compactMap { self.annotationsById.removeValue(forKey: $0) } + if !toRemove.isEmpty { + self.mapView.removeAnnotations(toRemove) } }