Migration Guide
Upgrading to 0.27.0
No breaking API changes, so nothing stops compiling. Two platforms do need action: Android apps need one code change, described under Android: style content, and web apps should delete two tags from web/index.html, described under Web: remove the script and stylesheet tags.
Update your pubspec.yaml:
Then run flutter pub upgrade maplibre_gl. See the CHANGELOG for the full list of changes. Minimum SDK versions are unchanged from 0.26.x; see Minimum versions.
Android: style content
A map on Android now survives its host activity being destroyed and recreated, whether by the "Don't keep activities" developer option, by a configuration change such as rotation, or under memory pressure. The MapView is rebuilt and the camera position is restored for you.
What is not restored automatically is style content: sources, layers, images and runtime style switches. Apply those inside onStyleLoadedCallback, which fires again after each recreation, so the content comes back with the new map:
MapLibreMapController? _controller;
MapLibreMap(
onMapCreated: (controller) => _controller = controller,
onStyleLoadedCallback: () async {
// Runs on the first style load and again after every activity recreation.
await _controller?.addGeoJsonSource('route', routeGeoJson);
await _controller?.addLineLayer(
'route',
'route-line',
const LineLayerProperties(lineColor: '#ff0000'),
);
},
)
If your app adds that content in onMapCreated or initState instead, move it into onStyleLoadedCallback. Without the move, the map comes back after a recreation with the base style only, and your own layers missing.
Web: remove the script and stylesheet tags
The plugin now loads MapLibre GL JS itself, pinned to the exact build it is tested against, before the first map is built. The two tags every web app had to carry are no longer needed: delete the <script> tag that loads maplibre-gl.js and the <link> tag that loads maplibre-gl.css from your web/index.html.
If you leave them in, nothing breaks today: an existing maplibregl global is reused as it is. But your pinned copy then silently overrides the version the plugin is tested against, on this upgrade and every future one.
Two setups need more than deleting the tags:
- A Content-Security-Policy that blocks the CDN, or a self-hosted copy of the library: point the plugin at your copy with
MapLibreMap.webLibrarySource. See Self-hosting MapLibre GL JS. - Your own JS interop into MapLibre GL JS, for example registering a protocol with
addProtocol: themaplibreglglobal no longer exists at page parse time, so awaitMapLibreMap.ensureWebLibraryLoaded()first. See Calling MapLibre GL JS yourself.
Web: the engine moves to MapLibre GL JS 6
The web build now runs on MapLibre GL JS 6, which ships as an ES module. The plugin imports it for you, so most apps need nothing. Three setups do:
- You self-host the library. Point
MapLibreJsSource.urlsat the.mjsbuild rather than.js, and serve the wholedistdirectory: the library resolves its worker relative to its own URL. Pointing at the old.jsbuild still works, because the plugin keeps the global that build defines, but you stay on version 5 while the plugin's interop is written against 6. - The page loads the library itself (
MapLibreJsSource.preloaded). An ES module defines no global, so the page has to publishglobalThis.maplibreglexplicitly. A page that still loads a version 5 bundle keeps working as before. - Your users are not all on WebGL2. Version 6 requires it and dropped the WebGL1 fallback, so a browser without WebGL2 now shows no map where version 5 showed a slow one. Where WebGL2 is missing altogether, as on Safari and iOS before 15, Flutter's own renderer still runs on WebGL1, so only the map goes missing and pointing
MapLibreJsSource.urlsat a version 5 build brings it back; the plugin logs as much. Where WebGL2 exists but no context can be created, a blocklisted GPU driver for example, Flutter does not fall back either and the whole app stays blank, which no library version changes. See Requirements.
Content-Security-Policy rules do not change: version 5 also built its Web Worker from a blob: URL, so worker-src 'self' blob: was already required and still is. What changes goes in your favour: version 6 only needs blob: when the library
is loaded cross-origin, so a self-hosted same-origin copy can now drop it. See Content-Security-Policy.
queryRenderedFeatures can also return a different set of features. Version 6 slices vector tiles instead of overscaling them, which upstream turned on by default because it fixes label placement, and that changes both rendering and query results. The plugin follows that default; there is no per-app switch for it. If it costs you something concrete, please open an issue.
Behaviour changes
None of these need a code change, but they are the places where code that worked before starts behaving differently.
- Web:
onMapIdlenow fires. If you worked around it never running on web, that workaround can go. - Web:
queryCameraPosition(),updateContentInsets()and the newsetPadding()no longer throwUnimplementedError, so any guard you put around them for web is no longer needed. - Android, iOS: symbol annotations now render their text in
Noto Sans Regularinstead of the oldOpen Sans Regular,Arial Unicode MS Regulardefault, which most glyph servers do not host. If your symbols were invisible, they appear now; if they were visible, the typeface changes. The font belongs to the annotation layer, so a different one means a symbol style layer withtextFontset. See Markers. - Android: feature state works instead of throwing, so a
kIsWebguard around those calls can go.promoteIdis still ignored outside web, so features must carry a top-levelidin the GeoJSON for feature state to key off. - Android, iOS: the offline download errors
mapboxTileCountLimitExceededandmapboxInvalidRegionDefinitionare nowtileCountLimitExceededandinvalidRegionDefinition. Code that matches onPlatformException.codefromdownloadOfflineRegionneeds the new strings; nothing else changes. - Android, iOS: downloading an area that is already downloaded replaces the existing region rather than adding a duplicate. On iOS the replacement keeps the region id the app already knows; on Android the SDK assigns the id when it creates a region, so the replacement gets a new one. Read the id back from the returned
OfflineRegionafter every download.
Android apps on AGP 9
The plugin no longer applies the Kotlin Gradle Plugin when your app builds with Android Gradle Plugin 9 or later, which is what broke that build before. Apps on AGP 8 are unaffected and need no change.
Upgrading from earlier 0.26 releases
No structural changes to the public API. Run flutter pub upgrade maplibre_gl and check the CHANGELOG for any deprecation notices.
Release history
What each release was about, most recent first. Full details live in the CHANGELOG, also rendered on pub.dev.
| Version | Highlights |
|---|---|
| 0.27.0 | Platform gaps closed (feature state on Android, cluster inspection, offline export/import), faster start-up with preWarm(), a newer web renderer, documentation site. No breaking API changes; see Upgrading to 0.27.0. |
| 0.26.2 | Enforces the Flutter 3.29 minimum in the package constraints; fixes redundant map updates on rebuild and doubleClickZoomEnabled on Android and iOS. |
| 0.26.1 | Android stability fixes after 0.26.0, including crashes on older hardware and disposal races. |
| 0.26.0 | Milestone release: WASM-compatible web build, a new example app, many long-standing fixes. Breaking: initialCameraPosition and requestMyLocationLatLng() became nullable. |
| 0.25.0 | Logo visibility and position options, web parity work (getStyle(), getSourceIds()), clearer annotation-manager errors. |
| 0.24.1 | Fixes double-fired annotation taps and add-before-style-load failures. |
| 0.24.0 | Breaking: feature interaction callbacks stabilized with the feature id and a nullable Annotation, unblocking taps on style-layer features. |
| 0.23.0 | Breaking: Maplibre renamed to MapLibre with lowerCamelCase enums. Runtime style switching, hover interactions, heatmaps. |
| 0.22.0 | Breaking: MapLibre Native updates on Android and iOS, bringing PMTiles support. |
| 0.21.0 | clearAmbientCache(), LatLngBounds.contains(), Android location engine options. Breaking: onFeatureTap gained the layerId. |