PackageTrack
Sign in Get early access

maplibre_gl_web

Web platform implementation of maplibre_gl. This package is only intended to be used by the maplibre_gl package.

0.27.0 83K downloads/mo #1200 most downloaded on pub.dev maplibre/flutter-maplibre-gl

What this package is like to depend on

Last release 4 days ago

19 Aug 2026

Release timing varies

gaps range from 2 weeks to 9 months

Nearly every release is documented

notes for 11 of 12 stable releases

Nothing withdrawn

no release was ever pulled

2 years old

12 releases · first in 2024

8 releases in the last 12 months

see the full history below

Release timeline

12 releases · May 2024 to Aug 2026
2025 2026
Release Pre-release

Releases

latest 12
  1. 0.27.0 19 Aug 2026
    Release notes

    Release 0.27.0

    The plugin has a documentation site now, at maplibre.org/flutter-maplibre-gl: a guide for every part of the API, each with a live map you can pan and click.

    This release regenerates the whole style property surface from the current MapLibre style spec, closes long-standing gaps between the three platforms, and cuts start-up time and the cost of large data updates. Upgrading costs little: the only compile-time change is SourceProperties.copyWith moving to named parameters, plus one small change for Android apps and one or two for web apps, all below.

    Actions needed

    • Android: a map now survives its activity being destroyed and recreated, but your style content does not come back with it. Add sources, layers and images inside onStyleLoadedCallback, which fires again after every recreation, rather than in onMapCreated or initState. See the migration guide (#805).
    • Web: the plugin now loads MapLibre GL JS itself. Delete the maplibre-gl.js script tag and the maplibre-gl.css link tag from web/index.html: if they stay, your pinned copy silently overrides the version the plugin is tested against. See the migration guide (#928).
    • Web: MapLibre GL JS 6 requires WebGL2 and no longer falls back to WebGL1, so a browser without it (Safari and iOS before 15) shows no map. Point MapLibreMap.webLibrarySource at a version 5 build to keep those browsers working. Version 6 also slices vector tiles instead of overscaling them, so queryRenderedFeatures can return a different set of features. See the migration guide (#943).
    • SourceProperties.copyWith takes named parameters instead of a dozen required positional ones, so props.copyWith(cluster: true) works and future spec additions stop breaking every caller. Calls that passed values positionally need the parameter names added (#957).
    • Expressions.xor never meant xor: ^ is the style spec's exponentiation operator, so the expression raised the first input to the power of the second. It is now Expressions.power, and Expressions.modulo replaces the misspelled Expressions.precent. The old names still work as deprecated aliases, so nothing stops compiling, but any style built with xor was doing something else (#957).
    • Android, iOS: the two offline download errors that carried a vendor name are renamed, mapboxTileCountLimitExceeded to tileCountLimitExceeded and mapboxInvalidRegionDefinition to invalidRegionDefinition. When they fire does not change, but a catch matching either old PlatformException code needs the new one (#956).

    Added

    • Android, iOS, Web: addColorReliefLayer() colours the terrain by elevation from a raster DEM source, the hypsometric tint of a physical map. The colour ramp is a colorReliefColor expression over Expressions.elevation, and colorReliefOpacity sets how strongly it covers what is below. See the color relief guide (#958).
    • Android, iOS, Web: addBackgroundLayer() paints the whole map with backgroundColor or tiles it with backgroundPattern, the one layer type with no source. Pass belowLayerId to place it under the layers it backs. Most published styles already carry a layer called background, so reusing that id fails with layerAlreadyExists. Changing its properties after adding it can stop it drawing while the style's own background layer is still there (maplibre-native#4502). See the background layer guide (#959).
    • Web: setProjection() switches between the mercator, globe and vertical-perspective projections, or interpolates between them by zoom. setTerrain() raises the map by the elevation of a raster DEM source, with null to flatten it again, and setSky() draws the sky and the atmosphere above the horizon. All three throw an UnsupportedError on Android and iOS, where MapLibre Native implements none of them yet. See the globe, terrain and sky guide (#960).
    • Android, iOS, Web: the style property surface is regenerated from the current MapLibre style spec instead of a four-year-old copy. New layer properties: textVariableAnchorOffset on symbols, hillshadeMethod and hillshadeIlluminationAltitude for multidirectional hillshading. New expressions: Expressions.distance, within, indexOf, slice and elevation (#957).
    • MapLibreMap.preWarm() starts the map engine during app start-up, for apps whose first screen is a map. Saves roughly 170 to 480 ms on Android, 45 to 165 ms on iOS and 10 to 50 ms on web (#867).
    • Android: feature state (setFeatureState, getFeatureState, removeFeatureState) works on Android as well as web, so single features can be restyled without re-feeding the source. iOS throws; its SDK does not expose the API yet. promoteId stays web-only, so Android features need a top-level id in the GeoJSON. See the feature state guide (#889).
    • Android, iOS: offline regions can move between devices. exportOfflineDatabase() writes a shareable copy, mergeOfflineRegions() imports one (now on iOS too), and getOfflineDatabasePath() locates the store. See the offline regions guide (#886).
    • Android, iOS, Web: the location puck can be driven from your own position updates instead of the device GPS, with no location permission needed: locationSource: ManualLocationSource() plus controller.updateManualLocation(). See the user location guide (#840).
    • Android, iOS, Web: getClusterExpansionZoom() gives the zoom at which a cluster splits, so a cluster tap can zoom to exactly that instead of guessing with zoom + 2. getClusterLeaves() and getClusterChildren() read the points behind a cluster. All three take the cluster feature's cluster_id. See the cluster guide (#896).
    • getLayerProperties() and getSourceProperties() read a layer's or source's properties by id, in the same shape on every platform, or null if the id is unknown. On iOS only what came with the style is readable, so a layer or source you added at runtime answers null there while Android and web answer normally (#513, #985).
    • Android, iOS, Web: setLight() sets the style's light source, which shades extruded geometries: anchor, position, color and intensity. Android and iOS take constant values, web also accepts expressions; Android answers INVALID_ARGUMENT for a value it cannot take. See the globe, terrain and sky guide (#960).
    • setPadding() keeps map content centred while a bottom sheet or side panel covers part of the map, instead of passing padding to every camera call (#258).
    • pauseMap() and resumeMap() stop and restart rendering for a map that is alive but off screen, such as one on an inactive tab. No-op on web (#805).
    • Android, iOS: setTrackingCameraOptions() pitches the camera without giving up the active tracking mode, for a navigation-style view that stays tilted while it keeps following the user. On web it throws an UnsupportedError. See the user location guide (#888).
    • Web: setGlobalStateProperty() sets a value in the style's global state, which the Expressions.globalState expression reads, so one switch restyles any number of layers at once. Android and iOS throw an UnsupportedError. See the expressions guide (#960).
    • Web: MapLibreMap.webLibrarySource chooses where MapLibre GL JS comes from: the build the plugin is tested against, a self-hosted copy, or one the page loads itself. MapLibreMap.ensureWebLibraryLoaded() completes once MapLibre GL JS is loaded, for calling into it directly, for example addProtocol (#928).
    • Android, iOS, Web: fillExtrusionRoundedCornerDistance rounds the corners of extruded shapes. Its generated dartdoc still reads not on js, because the pinned style spec records MapLibre GL JS support for it as an open issue; it shipped in GL JS 6.2.0 and works on web (#957).
    • Android, iOS, Web: clusterMinPoints on a GeoJSON source sets how many points have to fall together before they become a cluster, instead of the fixed default of two (#957, #981).
    • Android, iOS: attributionButtonColor tints the attribution button, for styles where the default tint is hard to see. No effect on web (#805).
    • iOS: LocationEnginePlatforms.iOS takes intervalMs and pulseWindowMs to pulse GPS instead of tracking continuously, easing battery use on maps that stay open a long time. The default keeps continuous tracking (#901).
    • Android: volatile on vector, raster and raster-dem sources keeps their tiles out of the on-disk cache, for tiles that change often or must not be stored. The iOS SDK exposes no equivalent and MapLibre GL JS will not implement one, so it has no effect there (#957, #981).
    • Web: properties MapLibre GL JS supports but MapLibre Native does not implement yet are exposed too: iconOverlap and textOverlap on symbols, fillLayerOpacity and lineLayerOpacity, resampling on raster and hillshade layers, filter on GeoJSON sources, the custom raster-dem encoding with its factors, and the Expressions.globalState, join and split expressions. Android and iOS ignore them, except the custom encoding, which now throws, and join and split, which work on Android since 13.5.0 (#957, #981).

    Fixed

    • Android: a map no longer stays blank for good after its activity is recreated, whether by "Don't keep activities", memory pressure or rotation; the camera position comes back with it (#805).
    • Android: icons added with addImage are visible again, at the right size on high-density screens. Since 0.26.0 they could be dropped whenever draggable annotations were in use, which is the default. Undecodable bytes now report a clear error instead of crashing (#866, #868).
    • Android, iOS: symbols added with addSymbol are visible again on styles whose glyph server does not host the old default font; a missing font used to hide the whole symbol, icon included. The default is now Noto Sans Regular; for another font use addSymbolLayer with textFont (#940).
    • Android, iOS: losing the network no longer cancels an offline region download, nor deletes the region with it on iOS. The SDK retries those failures itself once the connection is back; only the tile count limit stops a download now (#986).
    • Android, iOS: adding or updating a GeoJSON source with a large payload no longer blocks the UI for the whole encode. Large payloads are encoded in the background, cutting the blocking time by a factor of two to three (#366).
    • Android: a hillshade layer takes its colours again. hillshadeShadowColor and its three companions became arrays in MapLibre Native 6.24 for multidirectional hillshading, so a single value was rejected with Expected array<color> but found string instead and the layer silently kept the default black and white. A single value is now wrapped for you on Android and iOS, and a list is passed through (#957).
    • Android, iOS, Web: queryRenderedFeaturesInRect() applies the filter it is given; no platform decoded it, so the answer held every feature in the rectangle. Note that this call takes the filter as a JSON string, unlike queryRenderedFeatures() (#949, #953).
    • A map that fails to be created now reports the failure. Code awaiting the controller, onMapCreated included, used to wait forever for a map that never arrived, with only an unhandled error in the console (#943).
    • Android, iOS: downloading an area that is already downloaded replaces its region instead of adding a duplicate. iOS keeps the region id the app already knows; Android assigns a new one, so read it back from the returned OfflineRegion (#886).
    • Android, iOS: setLayerProperties() works on fill-extrusion and heatmap layers too; both used to answer UNSUPPORTED_LAYER_TYPE (#960).
    • Android: maxzoom on a GeoJSON source is applied again. The converter read a camel-cased key the Dart side never sends, so the value was dropped on Android while iOS and web honoured it (#981).
    • Android: queryRenderedFeatures() and querySourceFeatures() say what is wrong with a call they cannot answer: STYLE_NOT_READY before the style has loaded, INVALID_ARGUMENT for a missing sourceId, layerIds or query geometry. The failure used to arrive as a bare error carrying a NullPointerException message (#954).
    • Android: merging an offline database whose regions carry no metadata, such as one produced by maplibre-native, no longer fails with type 'Null' is not a subtype of type 'Map<String, dynamic>' (#865).
    • iOS: queryRenderedFeatures() and querySourceFeatures() no longer skip a feature that failed to serialize without telling you, and no longer hang on a call they cannot answer, such as one made before the style has loaded (#949).
    • iOS: queryCameraPosition() returns the camera position even when trackCameraPosition is false, matching Android. It used to return null (#892).
    • iOS: controller.cameraPosition no longer sticks on a NaN zoom from a camera event that arrived before the first layout, which misplaced camera-anchored content on maps the user had not touched (#903).
    • Web: onMapIdle now fires, matching Android and iOS; code waiting on it never ran (#857).
    • Web: getFeatureState() returns the state instead of throwing, and reports no state as null rather than an empty map. removeFeatureState(sourceId) with no feature id now resets the whole source instead of doing nothing. A stateKey with no featureId is rejected. Both match Android (#889).
    • Web: querySourceFeatures() now reports an error when it is called before the style has loaded, matching Android and iOS. It answered with an empty list, which the caller cannot tell apart from a source holding no features (#952).
    • Web: queryCameraPosition() is implemented; it used to throw UnimplementedError (#892).
    • Web: updateContentInsets and the new setPadding no longer throw UnimplementedError (#258).
    • The bundled LICENSE no longer breaks Flutter's license collector, which showed an untitled, truncated first entry on every dependent app's showLicensePage() (#895).

    Changed

    • Web: MapLibre GL JS upgraded from 5 to 6.4.1; see Actions needed above. Over 6.2 this fixes a permanent frame-rate drop after a style switch, a rejected missing-image resolver taking the rest of its batch down with it, globe zoom drifting away from the pointer, globe panning stalling at the poles, and an attribute-sanitising hole in popup and marker HTML (#943).
    • Android: MapLibre Native upgraded from 13.3.0 to 13.5.0 (13.4.0, 13.4.1, 13.5.0), plus OkHttp 5.4.0, Play Services Location 21.4.0 and androidx.core-ktx 1.18.0. 13.5.0 repaints symbol paint properties driven by feature state, fixes a surface-changed ANR and works around a device-lost crash on Adreno GPUs (#877, #919, #929).
    • iOS: MapLibre Native upgraded from 6.27.0 to 6.28.0 (release notes): the map view is no longer blurry in landscape on iPad (#929).
    • iOS: the plugin supports Flutter's Swift Package Manager integration. The CocoaPods podspec still ships, so CocoaPods apps need no migration (#891).
    • Android, iOS: a raster-dem source with encoding: "custom" now throws an UnsupportedError from addSource(). MapLibre Native decodes only the mapbox and terrarium formulas and used to read custom tiles as mapbox-encoded, drawing a plausible map from wrong elevations. Only addSource() is checked: the same encoding declared inside MapLibreMap.styleString is still decoded as mapbox without an error. Custom encoding keeps working on web (#981).
    • Android, iOS: panning a pitched map no longer moves the camera past the horizon, and changing a layer's source-layer or source-id now takes effect (#929).
    • The style spec the code is generated from is now a verbatim copy of a pinned upstream release (@maplibre/maplibre-gl-style-spec, currently 26.2.1). A weekly workflow reports what a newer release would bring to each platform and opens the update PR, and properties the native SDKs do not implement yet are generated for web only, so upgrading the SDKs unlocks them automatically (#957).
    • Every generated layer property, source option and expression now documents which platforms implement it, from the spec's own sdk-support metadata: iconOverlap reads basic functionality with js (not on android, ios) instead of claiming all three. Where the spec credits a platform whose SDK exposes no such API, the docs say so rather than repeat the spec (#957, #981).
    • The error from addSymbol, addCircle, addLine and addFill when the annotation manager is missing now names the type and both causes: the style has not finished loading, or that type is not in the widget's annotationOrder (#910).
    • Android: the Kotlin Gradle Plugin is applied only below AGP 9, so builds on AGP 9 or later no longer break. Nothing changes on AGP 8 (#905).
    • Android: the unused android-plugin-annotation-v9 and android-plugin-offline-v9 dependencies are dropped, so apps pull two fewer artifacts (#929).
    • Android: the plugin builds against NDK 28.2.13676358, matching Flutter's default (#956).

    Docs

    • New guides for user location, startup and performance and feature state (#840, #867, #889).
    • Annotations: the two conditions every annotation depends on, and the constraints of both APIs, are now stated in one place (#910).
    • Android: MapLibreMap.useHybridComposition was documented with the wrong default; it has been false since 0.16.0. Its docs and the performance page now say what each value selects (#816).
    • Example app: new Manual Location Source and Feature State pages, and the Offline Regions demo now walks through the export and import round-trip (#840, #886, #889).
    • Example app: the Data-Driven Expressions demo drew the USA as a triangle across the Atlantic, because one coordinate was missing its minus sign. Its polygon is now a bounding box over the contiguous states (#956).

    Contributors

    Full Changelog: v0.26.2...v0.27.0

    Open source →
    Release notes

    The plugin has a documentation site now, at maplibre.org/flutter-maplibre-gl: a guide for every part of the API, each with a live map you can pan and click.

    This release regenerates the whole style property surface from the current MapLibre style spec, closes long-standing gaps between the three platforms, and cuts start-up time and the cost of large data updates. Upgrading costs little: the only compile-time change is SourceProperties.copyWith moving to named parameters, plus one small change for Android apps and one or two for web apps, all below.

    Actions needed

    • Android: a map now survives its activity being destroyed and recreated, but your style content does not come back with it. Add sources, layers and images inside onStyleLoadedCallback, which fires again after every recreation, rather than in onMapCreated or initState. See the migration guide (#805).
    • Web: the plugin now loads MapLibre GL JS itself. Delete the maplibre-gl.js script tag and the maplibre-gl.css link tag from web/index.html: if they stay, your pinned copy silently overrides the version the plugin is tested against. See the migration guide (#928).
    • Web: MapLibre GL JS 6 requires WebGL2 and no longer falls back to WebGL1, so a browser without it (Safari and iOS before 15) shows no map. Point MapLibreMap.webLibrarySource at a version 5 build to keep those browsers working. Version 6 also slices vector tiles instead of overscaling them, so queryRenderedFeatures can return a different set of features. See the migration guide (#943).
    • SourceProperties.copyWith takes named parameters instead of a dozen required positional ones, so props.copyWith(cluster: true) works and future spec additions stop breaking every caller. Calls that passed values positionally need the parameter names added (#957).
    • Expressions.xor never meant xor: ^ is the style spec's exponentiation operator, so the expression raised the first input to the power of the second. It is now Expressions.power, and Expressions.modulo replaces the misspelled Expressions.precent. The old names still work as deprecated aliases, so nothing stops compiling, but any style built with xor was doing something else (#957).
    • Android, iOS: the two offline download errors that carried a vendor name are renamed, mapboxTileCountLimitExceeded to tileCountLimitExceeded and mapboxInvalidRegionDefinition to invalidRegionDefinition. When they fire does not change, but a catch matching either old PlatformException code needs the new one (#956).

    Added

    • Android, iOS, Web: addColorReliefLayer() colours the terrain by elevation from a raster DEM source, the hypsometric tint of a physical map. The colour ramp is a colorReliefColor expression over Expressions.elevation, and colorReliefOpacity sets how strongly it covers what is below. See the color relief guide (#958).
    • Android, iOS, Web: addBackgroundLayer() paints the whole map with backgroundColor or tiles it with backgroundPattern, the one layer type with no source. Pass belowLayerId to place it under the layers it backs. Most published styles already carry a layer called background, so reusing that id fails with layerAlreadyExists. Changing its properties after adding it can stop it drawing while the style's own background layer is still there (maplibre-native#4502). See the background layer guide (#959).
    • Web: setProjection() switches between the mercator, globe and vertical-perspective projections, or interpolates between them by zoom. setTerrain() raises the map by the elevation of a raster DEM source, with null to flatten it again, and setSky() draws the sky and the atmosphere above the horizon. All three throw an UnsupportedError on Android and iOS, where MapLibre Native implements none of them yet. See the globe, terrain and sky guide (#960).
    • Android, iOS, Web: the style property surface is regenerated from the current MapLibre style spec instead of a four-year-old copy. New layer properties: textVariableAnchorOffset on symbols, hillshadeMethod and hillshadeIlluminationAltitude for multidirectional hillshading. New expressions: Expressions.distance, within, indexOf, slice and elevation (#957).
    • MapLibreMap.preWarm() starts the map engine during app start-up, for apps whose first screen is a map. Saves roughly 170 to 480 ms on Android, 45 to 165 ms on iOS and 10 to 50 ms on web (#867).
    • Android: feature state (setFeatureState, getFeatureState, removeFeatureState) works on Android as well as web, so single features can be restyled without re-feeding the source. iOS throws; its SDK does not expose the API yet. promoteId stays web-only, so Android features need a top-level id in the GeoJSON. See the feature state guide (#889).
    • Android, iOS: offline regions can move between devices. exportOfflineDatabase() writes a shareable copy, mergeOfflineRegions() imports one (now on iOS too), and getOfflineDatabasePath() locates the store. See the offline regions guide (#886).
    • Android, iOS, Web: the location puck can be driven from your own position updates instead of the device GPS, with no location permission needed: locationSource: ManualLocationSource() plus controller.updateManualLocation(). See the user location guide (#840).
    • Android, iOS, Web: getClusterExpansionZoom() gives the zoom at which a cluster splits, so a cluster tap can zoom to exactly that instead of guessing with zoom + 2. getClusterLeaves() and getClusterChildren() read the points behind a cluster. All three take the cluster feature's cluster_id. See the cluster guide (#896).
    • getLayerProperties() and getSourceProperties() read a layer's or source's properties by id, in the same shape on every platform, or null if the id is unknown. On iOS only what came with the style is readable, so a layer or source you added at runtime answers null there while Android and web answer normally (#513, #985).
    • Android, iOS, Web: setLight() sets the style's light source, which shades extruded geometries: anchor, position, color and intensity. Android and iOS take constant values, web also accepts expressions; Android answers INVALID_ARGUMENT for a value it cannot take. See the globe, terrain and sky guide (#960).
    • setPadding() keeps map content centred while a bottom sheet or side panel covers part of the map, instead of passing padding to every camera call (#258).
    • pauseMap() and resumeMap() stop and restart rendering for a map that is alive but off screen, such as one on an inactive tab. No-op on web (#805).
    • Android, iOS: setTrackingCameraOptions() pitches the camera without giving up the active tracking mode, for a navigation-style view that stays tilted while it keeps following the user. On web it throws an UnsupportedError. See the user location guide (#888).
    • Web: setGlobalStateProperty() sets a value in the style's global state, which the Expressions.globalState expression reads, so one switch restyles any number of layers at once. Android and iOS throw an UnsupportedError. See the expressions guide (#960).
    • Web: MapLibreMap.webLibrarySource chooses where MapLibre GL JS comes from: the build the plugin is tested against, a self-hosted copy, or one the page loads itself. MapLibreMap.ensureWebLibraryLoaded() completes once MapLibre GL JS is loaded, for calling into it directly, for example addProtocol (#928).
    • Android, iOS, Web: fillExtrusionRoundedCornerDistance rounds the corners of extruded shapes. Its generated dartdoc still reads not on js, because the pinned style spec records MapLibre GL JS support for it as an open issue; it shipped in GL JS 6.2.0 and works on web (#957).
    • Android, iOS, Web: clusterMinPoints on a GeoJSON source sets how many points have to fall together before they become a cluster, instead of the fixed default of two (#957, #981).
    • Android, iOS: attributionButtonColor tints the attribution button, for styles where the default tint is hard to see. No effect on web (#805).
    • iOS: LocationEnginePlatforms.iOS takes intervalMs and pulseWindowMs to pulse GPS instead of tracking continuously, easing battery use on maps that stay open a long time. The default keeps continuous tracking (#901).
    • Android: volatile on vector, raster and raster-dem sources keeps their tiles out of the on-disk cache, for tiles that change often or must not be stored. The iOS SDK exposes no equivalent and MapLibre GL JS will not implement one, so it has no effect there (#957, #981).
    • Web: properties MapLibre GL JS supports but MapLibre Native does not implement yet are exposed too: iconOverlap and textOverlap on symbols, fillLayerOpacity and lineLayerOpacity, resampling on raster and hillshade layers, filter on GeoJSON sources, the custom raster-dem encoding with its factors, and the Expressions.globalState, join and split expressions. Android and iOS ignore them, except the custom encoding, which now throws, and join and split, which work on Android since 13.5.0 (#957, #981).

    Fixed

    • Android: a map no longer stays blank for good after its activity is recreated, whether by "Don't keep activities", memory pressure or rotation; the camera position comes back with it (#805).
    • Android: icons added with addImage are visible again, at the right size on high-density screens. Since 0.26.0 they could be dropped whenever draggable annotations were in use, which is the default. Undecodable bytes now report a clear error instead of crashing (#866, #868).
    • Android, iOS: symbols added with addSymbol are visible again on styles whose glyph server does not host the old default font; a missing font used to hide the whole symbol, icon included. The default is now Noto Sans Regular; for another font use addSymbolLayer with textFont (#940).
    • Android, iOS: losing the network no longer cancels an offline region download, nor deletes the region with it on iOS. The SDK retries those failures itself once the connection is back; only the tile count limit stops a download now (#986).
    • Android, iOS: adding or updating a GeoJSON source with a large payload no longer blocks the UI for the whole encode. Large payloads are encoded in the background, cutting the blocking time by a factor of two to three (#366).
    • Android: a hillshade layer takes its colours again. hillshadeShadowColor and its three companions became arrays in MapLibre Native 6.24 for multidirectional hillshading, so a single value was rejected with Expected array<color> but found string instead and the layer silently kept the default black and white. A single value is now wrapped for you on Android and iOS, and a list is passed through (#957).
    • Android, iOS, Web: queryRenderedFeaturesInRect() applies the filter it is given; no platform decoded it, so the answer held every feature in the rectangle. Note that this call takes the filter as a JSON string, unlike queryRenderedFeatures() (#949, #953).
    • A map that fails to be created now reports the failure. Code awaiting the controller, onMapCreated included, used to wait forever for a map that never arrived, with only an unhandled error in the console (#943).
    • Android, iOS: downloading an area that is already downloaded replaces its region instead of adding a duplicate. iOS keeps the region id the app already knows; Android assigns a new one, so read it back from the returned OfflineRegion (#886).
    • Android, iOS: setLayerProperties() works on fill-extrusion and heatmap layers too; both used to answer UNSUPPORTED_LAYER_TYPE (#960).
    • Android: maxzoom on a GeoJSON source is applied again. The converter read a camel-cased key the Dart side never sends, so the value was dropped on Android while iOS and web honoured it (#981).
    • Android: queryRenderedFeatures() and querySourceFeatures() say what is wrong with a call they cannot answer: STYLE_NOT_READY before the style has loaded, INVALID_ARGUMENT for a missing sourceId, layerIds or query geometry. The failure used to arrive as a bare error carrying a NullPointerException message (#954).
    • Android: merging an offline database whose regions carry no metadata, such as one produced by maplibre-native, no longer fails with type 'Null' is not a subtype of type 'Map<String, dynamic>' (#865).
    • iOS: queryRenderedFeatures() and querySourceFeatures() no longer skip a feature that failed to serialize without telling you, and no longer hang on a call they cannot answer, such as one made before the style has loaded (#949).
    • iOS: queryCameraPosition() returns the camera position even when trackCameraPosition is false, matching Android. It used to return null (#892).
    • iOS: controller.cameraPosition no longer sticks on a NaN zoom from a camera event that arrived before the first layout, which misplaced camera-anchored content on maps the user had not touched (#903).
    • Web: onMapIdle now fires, matching Android and iOS; code waiting on it never ran (#857).
    • Web: getFeatureState() returns the state instead of throwing, and reports no state as null rather than an empty map. removeFeatureState(sourceId) with no feature id now resets the whole source instead of doing nothing. A stateKey with no featureId is rejected. Both match Android (#889).
    • Web: querySourceFeatures() now reports an error when it is called before the style has loaded, matching Android and iOS. It answered with an empty list, which the caller cannot tell apart from a source holding no features (#952).
    • Web: queryCameraPosition() is implemented; it used to throw UnimplementedError (#892).
    • Web: updateContentInsets and the new setPadding no longer throw UnimplementedError (#258).
    • The bundled LICENSE no longer breaks Flutter's license collector, which showed an untitled, truncated first entry on every dependent app's showLicensePage() (#895).

    Changed

    • Web: MapLibre GL JS upgraded from 5 to 6.4.1; see Actions needed above. Over 6.2 this fixes a permanent frame-rate drop after a style switch, a rejected missing-image resolver taking the rest of its batch down with it, globe zoom drifting away from the pointer, globe panning stalling at the poles, and an attribute-sanitising hole in popup and marker HTML (#943).
    • Android: MapLibre Native upgraded from 13.3.0 to 13.5.0 (13.4.0, 13.4.1, 13.5.0), plus OkHttp 5.4.0, Play Services Location 21.4.0 and androidx.core-ktx 1.18.0. 13.5.0 repaints symbol paint properties driven by feature state, fixes a surface-changed ANR and works around a device-lost crash on Adreno GPUs (#877, #919, #929).
    • iOS: MapLibre Native upgraded from 6.27.0 to 6.28.0 (release notes): the map view is no longer blurry in landscape on iPad (#929).
    • iOS: the plugin supports Flutter's Swift Package Manager integration. The CocoaPods podspec still ships, so CocoaPods apps need no migration (#891).
    • Android, iOS: a raster-dem source with encoding: "custom" now throws an UnsupportedError from addSource(). MapLibre Native decodes only the mapbox and terrarium formulas and used to read custom tiles as mapbox-encoded, drawing a plausible map from wrong elevations. Only addSource() is checked: the same encoding declared inside MapLibreMap.styleString is still decoded as mapbox without an error. Custom encoding keeps working on web (#981).
    • Android, iOS: panning a pitched map no longer moves the camera past the horizon, and changing a layer's source-layer or source-id now takes effect (#929).
    • The style spec the code is generated from is now a verbatim copy of a pinned upstream release (@maplibre/maplibre-gl-style-spec, currently 26.2.1). A weekly workflow reports what a newer release would bring to each platform and opens the update PR, and properties the native SDKs do not implement yet are generated for web only, so upgrading the SDKs unlocks them automatically (#957).
    • Every generated layer property, source option and expression now documents which platforms implement it, from the spec's own sdk-support metadata: iconOverlap reads basic functionality with js (not on android, ios) instead of claiming all three. Where the spec credits a platform whose SDK exposes no such API, the docs say so rather than repeat the spec (#957, #981).
    • The error from addSymbol, addCircle, addLine and addFill when the annotation manager is missing now names the type and both causes: the style has not finished loading, or that type is not in the widget's annotationOrder (#910).
    • Android: the Kotlin Gradle Plugin is applied only below AGP 9, so builds on AGP 9 or later no longer break. Nothing changes on AGP 8 (#905).
    • Android: the unused android-plugin-annotation-v9 and android-plugin-offline-v9 dependencies are dropped, so apps pull two fewer artifacts (#929).
    • Android: the plugin builds against NDK 28.2.13676358, matching Flutter's default (#956).

    Docs

    • New guides for user location, startup and performance and feature state (#840, #867, #889).
    • Annotations: the two conditions every annotation depends on, and the constraints of both APIs, are now stated in one place (#910).
    • Android: MapLibreMap.useHybridComposition was documented with the wrong default; it has been false since 0.16.0. Its docs and the performance page now say what each value selects (#816).
    • Example app: new Manual Location Source and Feature State pages, and the Offline Regions demo now walks through the export and import round-trip (#840, #886, #889).
    • Example app: the Data-Driven Expressions demo drew the USA as a triangle across the Atlantic, because one coordinate was missing its minus sign. Its polygon is now a bounding box over the contiguous states (#956).
    Open source →
    Release notes

    Added

    • queryCameraPosition() is implemented; it used to throw UnimplementedError (#892).
    • updateContentInsets() and the new setPadding() are implemented through the camera padding option; both used to throw UnimplementedError (#258).
    • getLayerProperties() and getSourceProperties() read a layer's or source's properties from the live style, in the same shape as Android and iOS (#513).
    • The manual location puck is drawn by the plugin, since maplibre-gl-js has no injectable location component. It reuses maplibre-gl-js' own user-location classes plus a bearing arrow, so it needs maplibre-gl.css. The plugin loads that with the library (#840).
    • The plugin loads maplibre-gl-js itself before the first map is built. It loads the build its interop is written against, or whatever MapLibreMap.webLibrarySource configures, and reuses an existing maplibregl global as it is. A failed stylesheet only logs, since it affects the controls and the puck, not the map. A failed import is not memoized, so the next map build retries (#928).
    • MapLibreGlobalWeb implements the new MapLibreGlobalPlatform at plugin registration, which is how MapLibreMap.preWarm() and MapLibreMap.ensureWebLibraryLoaded() get their web behaviour (#928).
    • getClusterExpansionZoom(), getClusterChildren() and getClusterLeaves() read a clustered GeoJSON source. maplibre-gl-js 6 returns promises from all three, where version 5 took a trailing callback, so the interop is typed as promises. The library rejects on a source that is not clustered or an unknown cluster id. getClusterChildren() and getClusterLeaves() report that as an empty list, matching Android and iOS; getClusterExpansionZoom() raises CLUSTER_NOT_FOUND, since 0 is a valid zoom and a caller could not tell it from a real answer (#896).
    • setTrackingCameraOptions() throws an UnsupportedError naming the platform, since maplibre-gl-js has no location component. GeolocateControl stops following the user on any programmatic camera change it did not make itself, and fires trackuserlocationend, which this package forwards as a tracking dismissal. Suppressing those events would report tracking that is no longer happening (#888).

    Fixed

    • getFeatureState() returns the state instead of throwing, and reports no state as null rather than as an empty map, matching Android. It converted the JS object with dartify() and cast the result to Map<String, dynamic>, which that conversion never produces, so every call that found a state threw (#889).
    • removeFeatureState(sourceId) with no feature id resets the whole source. It built the target with id: null, and maplibre-gl-js only treats a missing id as "every feature of this source", so the call cleared nothing without an error. A stateKey with no featureId now raises the same INVALID_ARGUMENT as Android instead of being ignored (#889).
    • onMapIdle now fires, matching Android and iOS; code waiting on it never ran (#857).
    • querySourceFeatures() raises STYLE_NOT_READY when the style has not loaded yet. It used to log and answer with an empty list, which the caller cannot tell apart from a source holding no features. The two queryRenderedFeatures calls keep answering with an empty list, since nothing is rendered yet either way (#952).
    • queryRenderedFeaturesInRect() decodes the JSON string filter before handing it to maplibre-gl-js, which accepts only the expression itself. The filter was ignored, so the call answered with every feature in the rectangle (#953).

    Changed

    • The pinned maplibre-gl-js build is 6.4.1. Over 6.2 it fixes a permanent frame-rate drop after a style switch, a rejected missing-image resolver taking the rest of its batch down with it, globe zoom drifting away from the pointer and globe panning stalling at the poles, and an attribute-sanitising hole in popup and marker HTML (#943).
    • MapLibre GL JS 5 replaced by 6. Version 6 ships as an ES module only, with no UMD bundle and no global of its own. The loader imports it with importModule and publishes the namespace as globalThis.maplibregl, which every @JS binding here addresses. MapLibreJsSource.urls now points at the .mjs build. An older non-module bundle still works: the loader keeps the global that build defines rather than publishing an empty namespace over it. A page using MapLibreJsSource.preloaded has to publish the global itself (#943).
    • setGeoJsonSource() and setFeatureForGeoJsonSource() complete once the data has been applied, rather than as soon as it has been handed over. Version 6 returns a promise from GeoJSONSource.setData where version 5 returned the source, so the promise is awaited when there is one. That also stops a rejection on invalid GeoJSON from going unhandled (#943).
    • Missing style images are supplied through Map.setMissingStyleImageResolver instead of a styleimagemissing listener. Since version 6 the listener can observe the request, but calling addImage from it no longer resolves it, which would have silently stopped asset images from loading. The listener is kept as a fallback when the library on the page has no resolver, so a page still providing version 5 keeps working (#943).
    • A map that comes up without a renderer now reports why through FlutterError.reportError, once per distinct cause. Version 6 requires WebGL2 and dropped the WebGL1 fallback. Rather than throwing like version 5, it fires an error from inside the constructor, too early to subscribe to, so the map would otherwise just be blank. The message tells a WebGL1 only browser apart from one with no WebGL at all, and names the version 5 build as the fix for the former (#943).
    Open source →
  2. 0.26.2 19 Jun 2026
    Release notes

    Note: This release enforces a minimum Flutter version of 3.29, which was already required in practice since 0.26.0 but not reflected in the package constraints (#823).

    Added

    Fixed

    • Setting map options inside a widget that rebuilds frequently (e.g. with setState) no longer causes unnecessary map updates. Options containing nested lists such as cameraTargetBounds were always treated as changed, even when the value was identical (#849).
    • Android, iOS: doubleClickZoomEnabled: false now works correctly. Previously this option was only respected on web, so single taps on Android and iOS always had a ~300 ms delay while the platform waited to rule out a double-tap (#829).
    • iOS: setCustomHeaders and setHttpHeaders now correctly apply to all map network requests (tiles, styles, sprites, glyphs). Both APIs were previously silently ignored on iOS (#831).
    • iOS: setMapLanguage now correctly changes map labels on non-Mapbox styles (e.g. OpenFreeMap Liberty). Previously, calling setMapLanguage on iOS had no effect and place names were displayed using the style's default language (#830). A new Map Language example in the example app demonstrates this across several languages.
    • iOS: Layer color properties now accept any valid CSS color string (rgb(), rgba(), hsl(), hsla(), named colors). Previously only hex colors were supported and anything else rendered as transparent (#832).
    • iOS: Fixed a crash that could occur when the app was sent to the background while using PMTiles sources (#833).
    • iOS: Fixed a crash on cold launch when the map was first displayed at zero size (e.g. inside a hidden widget or during app startup) (#841).
    • iOS: Fixed a memory leak where map resources were not fully released when the map widget was disposed (#837).
    • Android: Fixed a crash when style API methods were called while the map style was still loading.

    Changed

    • Android: MapLibre Android SDK upgraded from 13.1.0 to 13.3.0.
    • iOS: MapLibre iOS upgraded from 6.26.0 to 6.27.0.

    Contributors

    Full Changelog: v0.26.1...v0.26.2

    Open source →
    Release notes

    Note: This release enforces a minimum Flutter version of 3.29, which was already required in practice since 0.26.0 but not reflected in the package constraints (#823).

    Added

    Fixed

    • Setting map options inside a widget that rebuilds frequently (e.g. with setState) no longer causes unnecessary map updates. Options containing nested lists such as cameraTargetBounds were always treated as changed, even when the value was identical (#849).
    • Android, iOS: doubleClickZoomEnabled: false now works correctly. Previously this option was only respected on web, so single taps on Android and iOS always had a ~300 ms delay while the platform waited to rule out a double-tap (#829).
    • iOS: setCustomHeaders and setHttpHeaders now correctly apply to all map network requests (tiles, styles, sprites, glyphs). Both APIs were previously silently ignored on iOS (#831).
    • iOS: setMapLanguage now correctly changes map labels on non-Mapbox styles (e.g. OpenFreeMap Liberty). Previously, calling setMapLanguage on iOS had no effect and place names were displayed using the style's default language (#830). A new Map Language example in the example app demonstrates this across several languages.
    • iOS: Layer color properties now accept any valid CSS color string (rgb(), rgba(), hsl(), hsla(), named colors). Previously only hex colors were supported and anything else rendered as transparent (#832).
    • iOS: Fixed a crash that could occur when the app was sent to the background while using PMTiles sources (#833).
    • iOS: Fixed a crash on cold launch when the map was first displayed at zero size (e.g. inside a hidden widget or during app startup) (#841).
    • iOS: Fixed a memory leak where map resources were not fully released when the map widget was disposed (#837).
    • Android: Fixed a crash when style API methods were called while the map style was still loading.

    Changed

    • Android: MapLibre Android SDK upgraded from 13.1.0 to 13.3.0.
    • iOS: MapLibre iOS upgraded from 6.26.0 to 6.27.0.
    Open source →
    Release notes

    No web-specific changes; version aligned with the maplibre_gl 0.26.2 release. See the top-level CHANGELOG for full details.

    Open source →
  3. 0.26.1 14 May 2026
    Release notes

    0.26.1

    Note: Several users reported crashes on a range of Android devices after upgrading to 0.26.0, particularly on older / less recent hardware. These issues are addressed in 0.26.1 (see the Android fixes below).

    Fixed

    • Android: Hybrid composition now correctly enables textureMode when necessary, preventing crashes and rendering and issues with platform views (#816).
    • Android: Null-check mapView inside the onResume repaint runnable to avoid NullPointerException when the map is disposed (e.g. dialogs/bottom sheets) before the posted runnable drains (#809).
    • De-register the annotation drag callback on AnnotationManager.dispose() to prevent jumpy drags and _idToAnnotation.containsKey crashes after style reloads on Android and iOS (#806).

    Changed

    • Android: MapLibre Android SDK upgraded from 13.0.2 to 13.1.0 (#811).
    • iOS: MapLibre iOS upgraded from 6.25.1 to 6.26.0.

    Docs

    • Web: Updated maplibre-gl JavaScript and CSS version to 5.24.0 in README.md to avoid NoSuchMethodError on MapLibreMap dispose with the previously referenced 4.3.0 version (#814).

    Contributors

    Full Changelog: v0.26.0...v0.26.1

    Open source →
    Release notes

    Note: Several users reported crashes on a range of Android devices after upgrading to 0.26.0, particularly on older / less recent hardware. These issues are addressed in 0.26.1 (see the Android fixes below).

    Fixed

    • Android: Hybrid composition now correctly enables textureMode when necessary, preventing crashes and rendering and issues with platform views (#816).
    • Android: Null-check mapView inside the onResume repaint runnable to avoid NullPointerException when the map is disposed (e.g. dialogs/bottom sheets) before the posted runnable drains (#809).
    • De-register the annotation drag callback on AnnotationManager.dispose() to prevent jumpy drags and _idToAnnotation.containsKey crashes after style reloads on Android and iOS (#806).

    Changed

    • Android: MapLibre Android SDK upgraded from 13.0.2 to 13.1.0 (#811).
    • iOS: MapLibre iOS upgraded from 6.25.1 to 6.26.0.

    Docs

    • Web: Updated maplibre-gl JavaScript and CSS version to 5.24.0 in README.md to avoid NoSuchMethodError on MapLibreMap dispose with the previously referenced 4.3.0 version (#814).
    Open source →
    Release notes

    No web-specific changes; version aligned with the maplibre_gl 0.26.1 release. See the top-level CHANGELOG for full details.

    Open source →
  4. 0.26.0 24 Apr 2026
    Release notes

    Version 0.26.0 marks a milestone for flutter-maplibre-gl

    This release resolves numerous long-standing bugs accumulated over the years and completes the transition to WASM compilation for the web platform, ensuring full compatibility with Flutter's modern web toolchain.
    It also introduces a new Example App for users to explore the latest features - see maplibre_gl_example for details.

    Breaking

    • initialCameraPosition is now nullable to support style-defined camera options (#769).
    • Removed LocationEngineAndroidProperties. All fields flattened into LocationEnginePlatforms with nullable platform-specific fields.
      Use Platform-specific constructors: LocationEnginePlatforms.android(), .iOS(), .web() instead.
    • Removed deprecated typedefs: MaplibreMapController, MaplibreMap, MaplibreStyles. Use MapLibreMapController, MapLibreMap, MapLibreStyles instead.
    • Removed deprecated callback: onInfoWindowTapped from MapLibreMapController.
    • Removed deprecated methods: removeImageSource (use removeSource) and addLayerBelow (use addImageLayerBelow).

    Added

    • Cross-platform map snapshot functionality via takeSnapshot() (#726).
    • featureTapsTriggersMapClick option to control whether feature taps also trigger map click callbacks, defaults to false (#729).
    • Fire onMapClick for all map taps, including after interactive features (#707).
    • Unit tests for core packages (#765).
    • Offline Regions: richer control and observability over downloads on Android and iOS (#795).
      • pauseOfflineRegionDownload / resumeOfflineRegionDownload to control in-progress downloads.
      • getOfflineRegionStatus returning OfflineRegionStatus with resource counts, bytes, progress and completion.
      • InProgress events now carry completedResourceCount, requiredResourceCount, and completedResourceSize for tile/byte progress in addition to the percentage.
      • clearAmbientCache and resetOfflineDatabase globals to evict unpinned tiles or fully reset the offline DB (in-flight downloads are terminated Dart-side before reset/deletion).
      • setOfflineMaxConcurrentRequests to cap tile concurrency (total on Android, per-host on both) and avoid upstream rate limiting.
    • clusterProperties: Introduced native implementation of clusterProperties for clustered GeoJSON sources. Both the simple operator-string form (e.g. {'sum': ['+', ['get', 'x']]}) and the explicit reduce-expression form are now applied natively; previously the property was serialized from Dart but ignored by both native converters (#792).
    • easeCamera interpolation: easeCamera accepts an optional CameraAnimationInterpolation to control the animation easing curve (linear, easeInOut, easeOut, fastOutLinearIn). Use CameraAnimationInterpolation.linear for smooth continuous tracking (e.g. following a moving GPS target) without velocity discontinuities between successive calls. Omitting the parameter preserves the previous default behavior (#789).
      • iOS: all four curves are supported exactly; fastOutLinearIn is implemented via CAMediaTimingFunction(controlPoints: 0.4, 0.0, 1.0, 1.0) (Material Design cubic Bezier).
      • Android: MapLibre Android only exposes a boolean easing flag on easeCamera, so only linear is distinct — easeInOut, easeOut, and fastOutLinearIn all map to the native ease-in/ease-out. See CameraAnimationInterpolation dartdoc for per-value details.
      • Web: easeCamera is now fully implemented via MapLibre GL JS map.easeTo({easing}). Each interpolation value maps to a cubic-bezier callback (easeInOut(0.42, 0, 0.58, 1), easeOut(0, 0, 0.58, 1), fastOutLinearIn(0.4, 0, 1, 1)). Previously threw UnimplementedError.
    • iOS: Implemented setMaximumFps to control the preferred frame rate (#739).
    • Android: Google Mobile Services (GMS) Location Engine support (#721).
    • Web: Exposed onMouseMove and added feature state management (setFeatureState, getFeatureState, removeFeatureState) (#718).
    • Web: Added getLayerVisibility, web snapshot, and map sizing features (#722).
    • Web: Added Scale Control (#720).
    • iOS: Location engine support — enableHighAccuracy and displacement configurable via LocationEnginePlatforms.iOS().
    • Web: Location engine properties (enableHighAccuracy, maximumAge, timeout) via LocationEnginePlatforms.web().
    • Platform-specific constructors for LocationEnginePlatforms: .android(), .iOS(), .web().

    Changed

    • Android: Reduced MapLibre SDK logging verbosity to minimize log spam (#752).
    • Android: Enhanced GeoJSON source handling with type checks and error logging (#764).
    • Android: Check style exists and is loaded before adding a Layer (#768).
    • Android: Check source exists before adding (#734).
    • Android: MapLibre Android SDK upgraded from 13.0.0 to 13.0.2, switched to OpenGL renderer variant (android-sdk-opengl) for better stability and performance on older devices.
    • iOS: Updated project settings for UISceneDelegate compatibility (#767).
    • iOS: MapLibre iOS SDK upgraded from 6.19.1 to 6.25.1.
    • Web: Upgraded MapLibre GL JS from 4.7.1 to 5.24.0 (#761, #651).
    • Web: Migrated preserveDrawingBuffer, antialias, and failIfMajorPerformanceCaveat from top-level MapOptions to canvasContextAttributes.
    • Web: Updated on()/off()/once() event methods to handle v5's Subscription return type instead of map instance.
    • Web: Removed obsolete customAttribution from MapOptions (now part of AttributionControl options in v5).
    • GitHub Actions: actions/upload-artifact updated from v6 to v7 (#748).
    • Bumped Dart and melos version to latest (#762).
    • Minimum Dart SDK version bumped from 3.5.0 to 3.7.0 (#762).
    • Gradle wrapper updated to 9.4.0, Kotlin to 2.3.10, Android Gradle Plugin to 9.1.0 (#753-#758).

    Fixed

    • Fix data properties not being added to Annotation created in AnnotationManager (#770).
    • Fix double JSON encoding in layer properties causing Android/iOS type errors (#747).
    • iOS: icon-text-fit-padding insets now use the correct style-spec order [top, right, bottom, left]left and right were previously swapped (#792).
    • Fix text-font property handling on Android and iOS to correctly accept font stacks as string arrays instead of only expressions.
    • Fix textFont in SymbolManager to pass font names as a simple string array, resolving rendering issues on native platforms.
    • Add DEM encoding support (terrarium/mapbox) for raster-dem tile sources on Android and iOS.
    • Fix heatmap color expressions in example app to use proper Expressions.rgba/Expressions.rgb syntax.
    • Android: Fix map partially not responsive in split screen (#771).
    • Android: GeoJSON source updates are now synchronous when drag is enabled, preventing stale feature positions during drag interactions and improved performance (#716).
    • Android: Disabled texture mode by default and improved MapView lifecycle management (#723).
    • Android: Removed unnecessary OfflineActivity from AndroidManifest.xml (#724).
    • Offline Regions: retain download StreamSubscriptions in a module-level map so Dart's GC can't drop native events while a download is paused (#795).
    • Android (Offline): throttle progress events to 100ms and discard non-monotonic counts so cache-served bursts don't starve the isolate and block pause taps; track in-flight downloads to support pause/resume/status (#795).
    • iOS (Offline): track active MLNOfflinePack instances so pause/resume/status operate on the live pack rather than reloading from storage (#795).
    • iOS: Deferred onStyleLoaded callback to avoid race conditions (#719).
    • Web: Improved styleimagemissing handling (#725).
    • Web: Fixed JS Interop and WASM compilation in release mode (#714).
    • Web: Fixed missing prototype on empty JS object created via interop.
    • Web: removeLayer and removeSource no longer throw when the layer/source doesn't exist.
    • Web: setGeoJsonSource returns early instead of crashing when the source doesn't exist.
    • Web: GeolocateControl now respects MyLocationTrackingMode and triggers programmatically.
    • Example: GPS location page. Fixed web permission check, wired onUserLocationUpdated, web-specific tracking modes.
    • Example: GeoJSON cluster. Added ['has', 'point_count'] filter to fix null property errors on unclustered points.

    Contributors: @MichaelNeufeld, @danieljosua1, @EyreFree, @skol-pro, @gabbopalma
    Full Changelog: v0.25.0...v0.26.0

    Open source →
    Release notes

    Version 0.26.0 is a milestone release for flutter-maplibre-gl.
    This release addresses numerous long-standing bugs that have accumulated over the years and completes the transition to the WASM compilation for the web platform, ensuring full compatibility with Flutter's modern web toolchain.

    Breaking

    • initialCameraPosition is now nullable to support style-defined camera options (#769).
    • Removed LocationEngineAndroidProperties. All fields flattened into LocationEnginePlatforms with nullable platform-specific fields.
      Use Platform-specific constructors: LocationEnginePlatforms.android(), .iOS(), .web() instead.
    • Removed deprecated typedefs: MaplibreMapController, MaplibreMap, MaplibreStyles. Use MapLibreMapController, MapLibreMap, MapLibreStyles instead.
    • Removed deprecated callback: onInfoWindowTapped from MapLibreMapController.
    • Removed deprecated methods: removeImageSource (use removeSource) and addLayerBelow (use addImageLayerBelow).

    Added

    • Cross-platform map snapshot functionality via takeSnapshot() (#726).
    • featureTapsTriggersMapClick option to control whether feature taps also trigger map click callbacks, defaults to false (#729).
    • Fire onMapClick for all map taps, including after interactive features (#707).
    • Unit tests for core packages (#765).
    • Offline Regions: richer control and observability over downloads on Android and iOS (#795).
      • pauseOfflineRegionDownload / resumeOfflineRegionDownload to control in-progress downloads.
      • getOfflineRegionStatus returning OfflineRegionStatus with resource counts, bytes, progress and completion.
      • InProgress events now carry completedResourceCount, requiredResourceCount, and completedResourceSize for tile/byte progress in addition to the percentage.
      • clearAmbientCache and resetOfflineDatabase globals to evict unpinned tiles or fully reset the offline DB (in-flight downloads are terminated Dart-side before reset/deletion).
      • setOfflineMaxConcurrentRequests to cap tile concurrency (total on Android, per-host on both) and avoid upstream rate limiting.
    • clusterProperties: Introduced native implementation of clusterProperties for clustered GeoJSON sources. Both the simple operator-string form (e.g. {'sum': ['+', ['get', 'x']]}) and the explicit reduce-expression form are now applied natively; previously the property was serialized from Dart but ignored by both native converters (#792).
    • easeCamera interpolation: easeCamera accepts an optional CameraAnimationInterpolation to control the animation easing curve (linear, easeInOut, easeOut, fastOutLinearIn). Use CameraAnimationInterpolation.linear for smooth continuous tracking (e.g. following a moving GPS target) without velocity discontinuities between successive calls. Omitting the parameter preserves the previous default behavior (#789).
      • iOS: all four curves are supported exactly; fastOutLinearIn is implemented via CAMediaTimingFunction(controlPoints: 0.4, 0.0, 1.0, 1.0) (Material Design cubic Bezier).
      • Android: MapLibre Android only exposes a boolean easing flag on easeCamera, so only linear is distinct — easeInOut, easeOut, and fastOutLinearIn all map to the native ease-in/ease-out. See CameraAnimationInterpolation dartdoc for per-value details.
      • Web: easeCamera is now fully implemented via MapLibre GL JS map.easeTo({easing}). Each interpolation value maps to a cubic-bezier callback (easeInOut(0.42, 0, 0.58, 1), easeOut(0, 0, 0.58, 1), fastOutLinearIn(0.4, 0, 1, 1)). Previously threw UnimplementedError.
    • iOS: Implemented setMaximumFps to control the preferred frame rate (#739).
    • Android: Google Mobile Services (GMS) Location Engine support (#721).
    • Web: Exposed onMouseMove and added feature state management (setFeatureState, getFeatureState, removeFeatureState) (#718).
    • Web: Added getLayerVisibility, web snapshot, and map sizing features (#722).
    • Web: Added Scale Control (#720).
    • iOS: Location engine support — enableHighAccuracy and displacement configurable via LocationEnginePlatforms.iOS().
    • Web: Location engine properties (enableHighAccuracy, maximumAge, timeout) via LocationEnginePlatforms.web().
    • Platform-specific constructors for LocationEnginePlatforms: .android(), .iOS(), .web().

    Changed

    • Android: Reduced MapLibre SDK logging verbosity to minimize log spam (#752).
    • Android: Enhanced GeoJSON source handling with type checks and error logging (#764).
    • Android: Check style exists and is loaded before adding a Layer (#768).
    • Android: Check source exists before adding (#734).
    • Android: MapLibre Android SDK upgraded from 13.0.0 to 13.0.2, switched to OpenGL renderer variant (android-sdk-opengl) for better stability and performance on older devices.
    • iOS: Updated project settings for UISceneDelegate compatibility (#767).
    • iOS: MapLibre iOS SDK upgraded from 6.19.1 to 6.25.1.
    • Web: Upgraded MapLibre GL JS from 4.7.1 to 5.24.0 (#761, #651).
    • Web: Migrated preserveDrawingBuffer, antialias, and failIfMajorPerformanceCaveat from top-level MapOptions to canvasContextAttributes.
    • Web: Updated on()/off()/once() event methods to handle v5's Subscription return type instead of map instance.
    • Web: Removed obsolete customAttribution from MapOptions (now part of AttributionControl options in v5).
    • GitHub Actions: actions/upload-artifact updated from v6 to v7 (#748).
    • Bumped Dart and melos version to latest (#762).
    • Minimum Dart SDK version bumped from 3.5.0 to 3.7.0 (#762).
    • Gradle wrapper updated to 9.4.0, Kotlin to 2.3.10, Android Gradle Plugin to 9.1.0 (#753-#758).

    Fixed

    • Fix data properties not being added to Annotation created in AnnotationManager (#770).
    • Fix double JSON encoding in layer properties causing Android/iOS type errors (#747).
    • iOS: icon-text-fit-padding insets now use the correct style-spec order [top, right, bottom, left]left and right were previously swapped (#792).
    • Fix text-font property handling on Android and iOS to correctly accept font stacks as string arrays instead of only expressions.
    • Fix textFont in SymbolManager to pass font names as a simple string array, resolving rendering issues on native platforms.
    • Add DEM encoding support (terrarium/mapbox) for raster-dem tile sources on Android and iOS.
    • Fix heatmap color expressions in example app to use proper Expressions.rgba/Expressions.rgb syntax.
    • Android: Fix map partially not responsive in split screen (#771).
    • Android: GeoJSON source updates are now synchronous when drag is enabled, preventing stale feature positions during drag interactions and improved performance (#716).
    • Android: Disabled texture mode by default and improved MapView lifecycle management (#723).
    • Android: Removed unnecessary OfflineActivity from AndroidManifest.xml (#724).
    • Offline Regions: retain download StreamSubscriptions in a module-level map so Dart's GC can't drop native events while a download is paused (#795).
    • Android (Offline): throttle progress events to 100ms and discard non-monotonic counts so cache-served bursts don't starve the isolate and block pause taps; track in-flight downloads to support pause/resume/status (#795).
    • iOS (Offline): track active MLNOfflinePack instances so pause/resume/status operate on the live pack rather than reloading from storage (#795).
    • iOS: Deferred onStyleLoaded callback to avoid race conditions (#719).
    • Web: Improved styleimagemissing handling (#725).
    • Web: Fixed JS Interop and WASM compilation in release mode (#714).
    • Web: Fixed missing prototype on empty JS object created via interop.
    • Web: removeLayer and removeSource no longer throw when the layer/source doesn't exist.
    • Web: setGeoJsonSource returns early instead of crashing when the source doesn't exist.
    • Web: GeolocateControl now respects MyLocationTrackingMode and triggers programmatically.
    • Example: GPS location page. Fixed web permission check, wired onUserLocationUpdated, web-specific tracking modes.
    • Example: GeoJSON cluster. Added ['has', 'point_count'] filter to fix null property errors on unclustered points.

    Full Changelog: v0.25.0...v0.26.0

    Open source →
    Release notes

    Breaking

    • Upgraded MapLibre GL JS from 4.7.1 to 5.24.0 (#761, #651).
      • initialCameraPosition is now ignored if the map style contains camera properties (center, zoom, bearing, pitch). MapLibre GL JS v5 gives priority to style-defined camera values over constructor options. Use MapLibreMapController.moveCamera() or MapLibreMapController.animateCamera() after map load to override.
      • preserveDrawingBuffer, antialias, failIfMajorPerformanceCaveat now set via canvasContextAttributes (MapLibre GL JS v5 API change).
      • on()/off()/once() adapted for v5 Subscription return type.
      • Removed customAttribution from MapOptionsJsImpl (moved to AttributionControl options in v5).

    Added

    • Exposed onMouseMove and added feature state management (setFeatureState, getFeatureState, removeFeatureState) (#718).
    • Added getLayerVisibility, web snapshot, and map sizing features (#722).
    • Added Scale Control (#720).
    • Location engine properties support — enableHighAccuracy, maximumAge, timeout from LocationEnginePlatforms.web() passed to GeolocateControl's PositionOptions.
    • trackUserLocation on GeolocateControl managed based on MyLocationTrackingMode.
    • GeolocateControl.trigger() called programmatically when tracking mode is enabled.
    • easeCamera fully implemented via MapLibre GL JS map.easeTo({easing}); all four CameraAnimationInterpolation values are honored via cubic-bezier easing callbacks. Previously threw UnimplementedError (#789).
      • easeInOut → cubic-bezier (0.42, 0, 0.58, 1)
      • easeOut → cubic-bezier (0, 0, 0.58, 1)
      • fastOutLinearIn → cubic-bezier (0.4, 0, 1, 1) (Material Design)
      • linear → identity
      • Omitting the parameter falls through to MapLibre GL JS's built-in default curve.

    Changed

    • easeTo wrapper on MapLibreMap now jsifies Dart Map options the same way flyTo already did, enabling the new easeCamera implementation to pass a plain Dart options dict.

    Fixed

    • Improved styleimagemissing handling (#725).
    • Fixed JS Interop and WASM compilation in release mode (#714).
    • removeLayer and removeSource no longer throw when the layer/source doesn't exist.
    • setGeoJsonSource returns early instead of crashing when the source doesn't exist.
    Open source →
  5. 0.25.0 07 Jan 2026
    Release notes

    Flutter MapLibre GL v0.25.0

    ⚠️ Breaking changes?

    No breaking changes for users. Your existing code works without modifications.

    The web platform was migrated internally to support WASM and Flutter 3.38.4+, but the public API remains unchanged.

    🚀 What you can do now

    Added

    Logo Customization

    You can now control the MapLibre logo visibility and position on your maps:

    MapLibreMap(
      logoSettings: LogoSettings(
        enabled: true,  // Show the MapLibre logo!
        position: LogoPosition.bottomLeft,  // Or bottomRight, topLeft, topRight
      ),
    )

    New Web Platform Methods

    The web implementation now supports additional methods previously only available on mobile:

    • controller.getStyle() - Returns the current map style as a JSON string
    • controller.getSourceIds() - Returns a list of all source IDs in the current style
    • controller.getLayers() - Improved with safe null handling

    Explicit Annotation Manager Initialization (#668)

    Annotation methods now enforce explicit initialization with clear exceptions when the style is not loaded. This helps you catch issues early instead of getting silent failures or null dereferences:

    • Calling add* methods before style load now fails fast with a clear Exception
    • Better null safety for annotation collections (symbols, lines, circles, fills)
    • More predictable behavior across platforms

    Camera Target Bounds (#8bcd74a)

    Refactored cameraTargetBounds implementation for consistent behavior across platforms. You can now reliably constrain the camera to specific geographic bounds on both Android and iOS.

    iOS Attribution Support

    Source attribution strings are now properly displayed on iOS with clickable links. The implementation parses HTML attribution from source properties and creates native iOS attribution dialogs that open URLs when tapped.

    Changed

    WASM Compatible Web Platform (#687)

    • Migrated from deprecated dart:js_util to modern dart:js_interop API
    • Fully compatible with Flutter's WASM compilation target
    • Required for Flutter 3.38.4+ compatibility
    • Enhanced type safety for JavaScript ↔ Dart conversions

    MapLibre Android SDK v12.3.0 (#690)

    • Synchronous GeoJSON source updates
    • Support for MLT-format vector tile sources
    • Better frustum offset support
    • Improved stability and performance

    Example App Refactor

    • Complete UI redesign with responsive layouts
    • Maps now use 50-60% of screen height for better visibility
    • Improved button and control layouts across different screen sizes

    Fixed

    iOS

    • Min/max zoom preferences now work correctly (#5230fab)
    • queryRenderedFeatures with empty layer list now returns all targets, matching Android behavior (#680)
    • Enhanced LayerPropertyConverter to handle null values and improve expression parsing (#98660dc)
      • Better handling of null values in layer properties
      • Improved expression parsing for complex layer configurations

    Web

    • setPaintProperty and setLayoutProperty now handle nullable JSAny values correctly (#12dfad2)
    • Improved jsify function to create JS arrays correctly
    • Pattern images now load properly with correct RGBA format conversion (#9ce52a6)

    Cross-Platform

    • Fixed lineDasharray and pattern properties reset to null in layer properties (#2b550ed)
    • Fixed pattern images loading - no more "mismatched image size" errors (#9ce52a6)
    • Improved MapLibreMapController disposing to prevent memory leaks
    • Removed unnecessary disposing of mapController in example app - improved stability (#f989797)
    • Refactored cameraTargetBounds for consistent behavior on Android and iOS (#8bcd74a)

    📦 Upgrade

    dependencies:
      maplibre_gl: ^0.25.0

    That's it. No code changes needed.

    👪 Contributors

    A big thank you to everyone who contributed to this release!

    @srmncnk, @albertmoravec, and all community members who reported issues and provided feedback.

    📝 More Info

    Happy mapping! 🗺️ 💙

    Open source →
    Release notes

    Added

    • Logo customization options including visibility and position settings (#b4fb174).
    • Explicit annotation manager initialization with clear error handling (#668).
    • iOS: Attribution support for tile and raster sources with HTML link parsing.

    Changed

    • MapLibre Android SDK upgraded from 11.13.5 to 12.3.0 (#690).
    • OkHttp updated from 4.12.0 to 5.3.2 for Node.js 24 compatibility (#676, #700).
    • Kotlin updated to 2.3.0 (#697, #698).
    • Android Gradle Plugin updated to 8.13.2 (#695, #674).
    • Android Application Plugin updated to 8.13.2 (#696, #689).
    • GitHub Actions: actions/checkout updated from v5 to v6 (#672, #693).
    • GitHub Actions: actions/upload-artifact updated from v4 to v6 (#688, #694).

    Fixed

    • Min/max zoom preference on iOS (#5230fab).
    • queryRenderedFeatures now returns all targets when supplying empty layers list on iOS, aligning behavior with Android (#680).
    • iOS: Enhanced LayerPropertyConverter to handle null values and improve expression parsing (#98660dc).
    • Fixed lineDasharray and patterns reset to null in layer properties (#2b550ed).
    • Improved MapLibreMapController disposing to prevent memory leaks.
    • Removed unnecessary disposing of mapController in example app (#f989797).
    • Fixed setLayerProperties and pattern images on web and Android (#9ce52a6).
      • Pattern images now correctly converted to RGBA format on web
      • Fixed mismatched image size error when loading pattern images

    Refactor

    • Complete refactor of example app with new UI and improved user experience (#ac877a4).
    • Refactored cameraTargetBounds implementation on Android and iOS for consistent behavior (#8bcd74a).

    Full Changelog: v0.24.1...v0.25.0

    Open source →
    Release notes

    Major Changes

    BREAKING: Migration to Modern JS Interop (#687)

    • WASM Compatible: Migrated from deprecated dart:js_util to modern dart:js_interop API
    • Required for Flutter 3.38.4+ compatibility
    • Now fully compatible with Flutter's WASM compilation target
    • No public API changes - this is an internal implementation update

    Technical Details of JS Interop Migration:

    • Replaced dart:js_util with dart:js_interop and dart:js_interop_unsafe
    • Updated all JS interop classes to use @staticInterop + extension methods pattern
    • Migrated from @JS() factory constructors to new interop model
    • Converted allowInterop() callbacks to .toJS
    • Updated property access from getProperty()/setProperty() to native JS property access
    • Replaced jsify()/dartify() utilities to work with JSAny/JSObject types
    • Fixed primitive type conversions: JSString.toDart, JSNumber.toDartDouble, JSArray.toDart
    • Converted static methods to top-level functions (e.g., LngLat.convert()lngLatConvert())

    Added

    • Implemented getStyle() - returns map style as JSON string (previously threw UnimplementedError)
    • Implemented getSourceIds() - returns list of source IDs from current style
    • Improved getLayers() - safely handles null styles and returns empty list instead of crashing

    Fixed

    • Fixed setPaintProperty and setLayoutProperty to handle nullable JSAny values correctly (#12dfad2)
    • Improved jsify function to create JS arrays correctly
    • Enhanced error handling in getLayer(), getFilter(), and isStyleLoaded() with null-safety checks
    • Fixed pattern images loading - all images now correctly converted to RGBA format (#9ce52a6)
      • Resolves mismatched image size errors when loading pattern images
      • Ensures consistent image format across all image uploads

    Refactor

    • Improved null safety across the web platform
    • Enhanced type safety for JS ↔ Dart conversions
    • More descriptive error messages in the web implementation
    • Example app improvements:
      • Maps now use responsive sizing (50-60% of screen height)
      • Removed fixed width constraints for full-screen responsiveness
      • Better button and control layouts
    Open source →
  6. 0.24.1 20 Oct 2025
    Release notes

    What's Changed

    Added

    • Added onCameraMove callback in the controller and in MapLibreMap class. (#643)

    Changed

    • Rollback maplibre-gl to 4.7.1 version. (#660)

    Fixed

    • Annotation tap call callbacks twice. (#652)
    • Annotation APIs: use null-aware access for manager-backed collections (symbols, lines, circles, fills) to avoid null errors before style load. (#657)
    • Add methods enforce explicit manager initialization with clear exceptions when style is not loaded. (#657)
    • Calling add* before style load now fails fast with a clear Exception instead of risking null dereferences or silent failures. (#657)

    A big thank you to everyone who contributed to this update!

    Contributors: @andynewman10, @andrea689, @gabbopalma
    Full Changelog: v0.24.0...v0.24.1

    Open source →
    Release notes

    Fixed

    • Annotation tap call callbacks twice. (#652)
    • Annotation APIs: use null-aware access for manager-backed collections (symbols, lines, circles, fills) to avoid null errors before style load. (#657)
    • Add methods enforce explicit manager initialization with clear exceptions when style is not loaded. (#657)
    • Calling add* before style load now fails fast with a clear Exception instead of risking null dereferences or silent failures. (#657)

    Changed

    • Rollback maplibre-gl to 4.7.1 version. (#660)

    Added

    • Added onCameraMove callback in the controller and in MapLibreMap class. (#643)
    Open source →
    Release notes
    • Rollback maplibre-gl to 4.7.1 version. (#660)
    Open source →
  7. 0.24.0 02 Oct 2025
    Release notes

    What's Changed

    Note

    This release has breaking changes.
    We apologize for the quick change in 0.24.0: this version definitively stabilizes the signatures of feature interaction callbacks.

    This release restores the feature id and makes the Annotation parameter nullable for all feature interaction callbacks (tap / drag / hover).
    This unblocks interaction with style-layer features not managed by annotation managers (i.e. added via addLayer* / style APIs).

    Warning

    Breaking Changes

    • Tap: OnFeatureInteractionCallback(Point<double> point, LatLng coordinates, String id, String layerId, Annotation? annotation).

    • Drag: OnFeatureDragCallback(Point<double> point, LatLng origin, LatLng current, LatLng delta, String id, Annotation? annotation, DragEventType eventType).

    • Hover: OnFeatureHoverCallbackPoint<double> point, LatLng coordinates, String id, Annotation? annotation, HoverEventType eventType).

    • Update existing listeners: The short‑lived 0.23.0-only signatures (without id) are removed.

      • For unmanaged style layer features annotation is null (unmanaged means sources/layers you add via style APIs like addGeoJsonSource + addSymbolLayer).
      • For managed annotations it is the Annotation object.

    Reasoning

    In 0.23.0 the move to annotation objects inadvertently dropped interaction for unmanaged style features. Reintroducing id (and making annotation nullable) normalizes all three interaction paths without creating phantom annotation wrappers.

    Migration Example

    Before (0.23.0):

    controller.onFeatureTapped.add((p, latLng, annotation, layerId) {
      print(annotation.id);
    });
    

    After (>=0.24.0):

    controller.onFeatureTapped.add((p, latLng, id, layerId, annotation) {
      print('feature id=$id managed=${annotation != null}');
    });
    

    Refactor / Quality

    • (web) Refactored onMapClick (degenerate bbox + interactive layer filter) to surface features inserted via style APIs (unmanaged style-layer features) in onFeatureTapped (previously skipped; returned now with id, layerId and annotation = null) (#646).
    • (web) Ensure map container stretches vertically by adding style.height = '100%' to the registered div (prevents occasional zero-height layout issues in flexible parents) (#641)

    Contributors: @andynewman10, @gabbopalma
    Full Changelog: v0.23.0...v0.24.0

    Open source →
    Release notes

    Note: This release has breaking changes.
    We apologize for the quick change in 0.24.0: this version definitively stabilizes the signatures of feature interaction callbacks.

    This release restores the feature id and makes the Annotation parameter nullable for all feature interaction callbacks (tap / drag / hover).
    This unblocks interaction with style-layer features not managed by annotation managers (i.e. added via addLayer* / style APIs).

    Breaking Changes

    • Tap: OnFeatureInteractionCallback(Point<double> point, LatLng coordinates, String id, String layerId, Annotation? annotation).

    • Drag: OnFeatureDragCallback(Point<double> point, LatLng origin, LatLng current, LatLng delta, String id, Annotation? annotation, DragEventType eventType).

    • Hover: OnFeatureHoverCallbackPoint<double> point, LatLng coordinates, String id, Annotation? annotation, HoverEventType eventType).

    • Update existing listeners: The short‑lived 0.23.0-only signatures (without id) are removed.

      • For unmanaged style layer features annotation is null (unmanaged means sources/layers you add via style APIs like addGeoJsonSource + addSymbolLayer).
      • For managed annotations it is the Annotation object.

    Reasoning

    In 0.23.0 the move to annotation objects inadvertently dropped interaction for unmanaged style features. Reintroducing id (and making annotation nullable) normalizes all three interaction paths without creating phantom annotation wrappers.

    Migration Example

    Before (0.23.0):

    controller.onFeatureTapped.add((p, latLng, annotation, layerId) {
      print(annotation.id);
    });
    

    After (>=0.24.0):

    controller.onFeatureTapped.add((p, latLng, id, layerId, annotation) {
      print('feature id=$id managed=${annotation != null}');
    });
    

    Refactor / Quality

    • (web) Refactored onMapClick (degenerate bbox + interactive layer filter) to surface features inserted via style APIs (unmanaged style-layer features) in onFeatureTapped (previously skipped; returned now with id, layerId and annotation = null) (#646).
    • (web) Ensure map container stretches vertically by adding style.height = '100%' to the registered div (prevents occasional zero-height layout issues in flexible parents) (#641)

    Full Changelog: v0.23.0...v0.24.0

    Open source →
    Release notes

    Refactor / Quality (web)

    • Refactored onMapClick (degenerate bbox + interactive layer filter) so unmanaged style-layer features now trigger onFeatureTapped (feature id + layer id, annotation = null).
    • Ensured map container stretches vertically by setting style.height = '100%' on the registered div to avoid zero-height issues in flexible layouts.
    Open source →
  8. 0.23.0 30 Sep 2025
    Release notes

    What's Changed

    Caution

    USE 0.24.0 VERSION INSTEAD OF THIS ONE!

    This consolidated release delivers runtime style switching, hover interactions, heatmap & visibility features, native SDK updates, and broad naming / enum casing harmonization. It also fixes several interaction and stability issues across web and mobile.

    If you are upgrading from <= 0.22.x:

    • Review the breaking rename (Maplibre -> MapLibre) and enum / const lowerCamelCase migration.
    • Adapt feature interaction callbacks: onFeatureTapped / onFeatureDrag now receive an Annotation instead of an id argument.
    • Ensure any style access happens after onStyleLoaded due to stricter style readiness checks.

    A big thank you to everyone who contributed to this update!

    Warning

    Breaking changes

    • Rename Maplibre to MapLibre across APIs (#441).
    • Enum fields & const identifiers migrated to lower camel case (#415).
    • onFeatureTapped / onFeatureDrag replaced the raw id parameter with an Annotation annotation instance (update handler signatures).

    Added / Features

    • Runtime style switching APIs on controller (#444) and raw style JSON setting on iOS / Web (#603).
    • Hover interaction events (onFeatureHover) (#614).
    • Heatmap layer support (#365).
    • Bounds fitting API to change viewport to given bounds (#133).
    • Layer visibility control (#138).
    • LatLngBounds.contains convenience (#498).

    Changed / Updates

    • MapLibre Native: Android 11.13.5 & iOS 6.19.1.
    • Flutter / Gradle plugin & tooling compatibility update (#542).
    • Lint and analysis alignment via very_good_analysis and flutter_lints (#452, #434, #414, #419).
    • Package link updates & style resource relocation (MaplibreStyles moved to main package) (#435, #413).

    Fixed

    • iOS code generation (Offset / Translate / expression arrays) (#481).
    • Annotation tap consumption now properly respected (annotationConsumeTapEvents).
    • Prevent calling notifyListeners() after controller disposal (#621).
    • Web: event listener cancellation & hover handling robustness (#623).
    • Offline region download crash in example (#569).
    • Added style loaded safety checks (#563).
    • Aligned example and web pubspec versions (#476).
    • Web: enforce maplibre-gl-js 4.x & remove shadow root stylesheet workaround (#409).

    Refactor / Quality

    • Enable and fix additional lint rules (#452).

    New Contributors

    Contributors: @AlexanderThiele, @mhernz, @TarekTolba1, @srmncnk, @itheamc, @kuhnroyal, @albertmoravec, @gabbopalma
    Full Changelog: v0.22.0...v0.23.0

    Open source →
    Release notes

    Note: This release has breaking changes.

    This release aligns the plugin with the latest MapLibre Native (Android 11.9.0 / iOS 6.14.0), introduces runtime style switching APIs, hover interaction callbacks, and several annotation interaction improvements. It also contains a small breaking change for feature interaction callbacks.

    A big thank you to everyone who contributed to this update!

    Breaking Changes

    • onFeatureDrag / onFeatureTapped callback signatures now provide an Annotation annotation object instead of an id parameter. Update your handlers to remove the id argument and use annotation.id (or other annotation fields) as needed.

    Highlights

    • Runtime style switching via controller (setStyle…) without tearing down the map (#444, #603).
    • Hover interaction events (onFeatureHover) for richer desktop/web UX (#614).
    • Improved event handling reliability (cancellation & consumption fixes) (#621, #623).
    • Offline region download crash fix in example (#569) and style loaded safety checks (#563).
    • Updated MapLibre Native bringing PMTiles & performance improvements (#552, #582).

    Added / Updated

    • Feature: added set style method on controller (#444) & support setting raw style JSON on iOS/web (#603).
    • Feature: expose hovering events (onFeatureHover) (#614).
    • Update: bump Android to 11.9.0 & iOS to 6.14.0 (#582).
    • Update: update maplibre-native to the latest versions / PMTiles support (#552).
    • CI/Tooling: upgrade Flutter Gradle Plugin & compatibility with Flutter 3.29.0 (#542).

    Fixed

    • iOS code generation: corrected handling of Offset / Translate / expression arrays in generated bindings (#481).
    • Annotation tap consumption now respected (annotationConsumeTapEvents).
    • Prevent calling notifyListeners() after controller disposal (#621).
    • Web: event listener cancellation & hover handling robustness (#623).
    • Example: offline region download crash (#569).
    • Added style readiness checks before access (#563).

    Refactor / Quality

    • Enable and fix additional lint rules to enforce consistency (#452).

    Full Changelog: v0.22.0...v0.23.0

    Open source →
    Release notes

    Note: This release has breaking changes.

    see top-level CHANGELOG.md

    Open source →
  9. 0.22.0 05 Jun 2025
    Release notes

    This PR addresses several crashes that occurred when attempting to add
    layers and sources that already exist.

    The fix introduces appropriate guard statements and ensures that errors
    are correctly propagated to the Flutter layer when necessary.

    Previously, such issues would result in a crash. With this change, they
    now raise a platform exception instead.


    Co-authored-by: Alexander Thiele [email protected]

    Open source →
    Release notes

    Breaking changes

    • Updated maplibre-native for iOS to v6.14.0. This mainly introduces PMTiles support. See the maplibre-native changelog for more information.
    • Updated maplibre-native for Android to v11.9.0. This mainly introduces PMTiles support. Flutter version packed with OpenGL ES 3.0 build for now, later we could probably switch to Vulkan. See the maplibre-native changelog for more information.
    • queryRenderedFeaturesInRect support string feature ids on web (#576).

    Changed

    • Added await to all addLayer calls (#558).

    Fixed

    • Fixed Unsupported operation error on web (#551).
    Open source →
  10. 0.21.0 26 Feb 2025
    Release notes

    What's Changed

    New Contributors

    Full Changelog: v0.20.0...v0.21.0

    Open source →
    Release notes

    Added

    • added the clearAmbientCache functionality (#502).
    • added the contains functionality to LatLngBounds (#498).
    • added the possibility to set LocationEnginePlatforms properties for better device tracking on Android (#510).

    Changed

    • BREAKING: onFeatureTap returns the layerId (#475).
    • Changed iOS package name to support Swift Package Manager (#467).
    • Move the maplibre_gl package to a subdirectory of the repository and add melos to orchestrate all packages (#453).

    Removed

    • Removed support for Dart SDKs older than 3.4.0 (Flutter SDK 3.22.0) (#542)

    Fixed

    • Fixed exception when destroying mapView on Android by reordering cleanup (#459).
    Open source →
  11. 0.20.0 07 Jun 2024
    Release notes

    A lot of files/classes have been renamed and moved around in this release. If you notice any build errors, please make sure to run flutter clean.

    Breaking changes

    • All Dart enums have been migrated from mixed cases to lower camelcase according to the camel_case_types lint rule.
    • Move MapLibreStyles to the main maplibre_gl package. You can now use the
      demo style without adding maplibre_gl_platform_interface as a dependency.
    • Updated maplibre-native for ios to v6.5.0. This introduces the new iOS Metal renderer and the OpenGL ES renderer now uses OpenGL ES 3.0. Only iOS Devices with an Apple A7 GPU or later are supported onwards. See the maplibre-native changelog for more information.
    • Updated maplibre-native for android to v11.0.0. This version uses OpenGL ES 3.0. See the maplibre-native changelog for more information.
    • Renamed the method channel to plugins.flutter.io/maplibre_gl_* in all packages.
    • Renamed "Maplibre" to "MapLibre" to be in line with maplibre-native (affects for example the classes MaplibreMap and MaplibreMapController).

    Changes

    • Added support for Swift Package Manager usage on iOS.
    • Migrated main iOS plugin class from Objective-C to Swift.
    • Renamed iOS plugin classes from Mapbox to MapLibre.
    • Removed support for Kotlin versions older than 1.9.0 (#460).

    Full Changelog: v0.19.0+2...v0.20.0

    Open source →
  12. 0.19.0+2 22 May 2024

    Nothing published for this version

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive