Summary
When a RouteMap configured with a non-empty noServiceAreaLayers list reloads its
style more than once during the lifetime of a single map instance, the plugin throws
unhandled PlatformExceptions. Two distinct exceptions have been observed, and both are
rooted in style-loaded handling that is not idempotent and not race-safe against
repeated or overlapping style loads.
A style (re)load happens whenever styleUrl changes at runtime (for example switching
between a light and a dark map style), or when the underlying MapLibreMap platform view
re-emits onStyleLoaded — which can happen while the map is embedded in an animated /
modal container, or when the widget subtree is rebuilt and/or torn down and recreated in
quick succession.
Observed exceptions
A) sourceAlreadyExists — no-service-area sources are re-added without removal
PlatformException(sourceAlreadyExists, Source already exists, Source with id no_service_area_source_id_0 already exists., null)
#0 StandardMethodCodec.decodeEnvelope (message_codecs.dart:653)
#1 MethodChannel._invokeMethod (platform_channel.dart:366)
#2 MapLibreMethodChannel.addSource (method_channel_maplibre_gl.dart:940)
#3 _RouteMapNoServiceAreaLayerState._addNoServiceAreaLayer (route_map_no_service_area_layer.dart:48)
#4 _RouteMapNoServiceAreaLayerState._addNoServiceAreaLayers (route_map_no_service_area_layer.dart:11)
#5 _RouteMapState.build.<anonymous closure> (route_map_base.dart:321)
B) styleNotFound — annotation manager re-initialized against a torn-down style
PlatformException(styleNotFound, Style not found, Style not found., null)
#0 StandardMethodCodec.decodeEnvelope (message_codecs.dart:653)
#1 MethodChannel._invokeMethod (platform_channel.dart:366)
#2 MapLibreMethodChannel.addGeoJsonSource (method_channel_maplibre_gl.dart:758)
#3 MapLibreMapController.addGeoJsonSource (controller.dart:451)
#4 AnnotationManager.initialize (annotation_manager.dart:83)
#5 new MapLibreMapController.<anonymous closure> (controller.dart:235)
Exception A is raised by route_map itself. Exception B is raised inside maplibre_gl's
annotation-manager initialization, but it is driven by the same trigger (repeated /
overlapping style loads), so the two tend to appear together.
Steps to reproduce
- Build a
RouteMap with a non-empty noServiceAreaLayers list.
- Make the same map instance load its style at least twice, e.g.:
- change
styleUrl at runtime (light ↔ dark), and/or
- embed the map in a modal / animated container and open it, and/or rebuild the
surrounding subtree rapidly (frequent state updates).
- Observe the exceptions in the logs.
Expected
Repeated style loads re-apply the no-service-area layers cleanly (remove-then-add), and
in-flight style-loaded work does not crash if the style is superseded or disposed while
async work is still running. No unhandled PlatformException.
Actual
sourceAlreadyExists for no_service_area_source_id_0 on the second style load
(and, as a side effect, a duplicated grey no-service-area overlay can remain on the map).
styleNotFound when annotation managers are (re)initialized while the style is being
swapped / torn down.
Suspected cause (short)
_addNoServiceAreaLayers re-adds sources/layers with fixed, index-based IDs without
removing the previous ones first and without any try/catch or re-entrancy guard — unlike
the POI-layer path, which already removes-before-add and swallows removal errors. A full
root-cause walkthrough (with file/line references and suggested fixes for both exceptions)
is in a comment below.
Environment
- route_map:
feature/native-location-display @ 60683a2
- maplibre_gl:
0.26.2 (git, custom branch)
- Flutter:
3.41.3
- Platform: Android & iOS (failure originates on the native/method-channel side)
Summary
When a
RouteMapconfigured with a non-emptynoServiceAreaLayerslist reloads itsstyle more than once during the lifetime of a single map instance, the plugin throws
unhandled
PlatformExceptions. Two distinct exceptions have been observed, and both arerooted in style-loaded handling that is not idempotent and not race-safe against
repeated or overlapping style loads.
A style (re)load happens whenever
styleUrlchanges at runtime (for example switchingbetween a light and a dark map style), or when the underlying
MapLibreMapplatform viewre-emits
onStyleLoaded— which can happen while the map is embedded in an animated /modal container, or when the widget subtree is rebuilt and/or torn down and recreated in
quick succession.
Observed exceptions
A)
sourceAlreadyExists— no-service-area sources are re-added without removalB)
styleNotFound— annotation manager re-initialized against a torn-down styleException A is raised by
route_mapitself. Exception B is raised insidemaplibre_gl'sannotation-manager initialization, but it is driven by the same trigger (repeated /
overlapping style loads), so the two tend to appear together.
Steps to reproduce
RouteMapwith a non-emptynoServiceAreaLayerslist.styleUrlat runtime (light ↔ dark), and/orsurrounding subtree rapidly (frequent state updates).
Expected
Repeated style loads re-apply the no-service-area layers cleanly (remove-then-add), and
in-flight style-loaded work does not crash if the style is superseded or disposed while
async work is still running. No unhandled
PlatformException.Actual
sourceAlreadyExistsforno_service_area_source_id_0on the second style load(and, as a side effect, a duplicated grey no-service-area overlay can remain on the map).
styleNotFoundwhen annotation managers are (re)initialized while the style is beingswapped / torn down.
Suspected cause (short)
_addNoServiceAreaLayersre-adds sources/layers with fixed, index-based IDs withoutremoving the previous ones first and without any try/catch or re-entrancy guard — unlike
the POI-layer path, which already removes-before-add and swallows removal errors. A full
root-cause walkthrough (with file/line references and suggested fixes for both exceptions)
is in a comment below.
Environment
feature/native-location-display@60683a20.26.2(git, custom branch)3.41.3