maplibre_gl 0.26.2
maplibre_gl: ^0.26.2 copied to clipboard
A Flutter plugin for integrating MapLibre Maps inside a Flutter application on Android, iOS and web platforms.
Flutter MapLibre GL #
Interactive, vector-tile, fully styleable maps for Flutter on Android, iOS and Web, powered by the open source MapLibre engines. Vendor-neutral: host your own tiles or mix providers, no proprietary token required.
This project is a fork of flutter-mapbox-gl. If you're coming from it, see Migration.
🚀 Launch the interactive demo — explore the full example app, interact with the map, and try MapLibre GL features instantly in your browser. No installation, setup, or API keys required.
Platforms & feature support #
Engines: maplibre-native (Android/iOS), maplibre-gl-js (Web). Only a subset of native SDK APIs is exposed, PRs to extend coverage are welcome.
| Feature | Android | iOS | Web |
|---|---|---|---|
| Style, Camera, Gesture | ✅ | ✅ | ✅ |
| User Location | ✅ | ✅ | ✅ |
| Symbol / Circle / Line / Fill | ✅ | ✅ | ✅ |
| Fill Extrusion | ✅ | ✅ | ✅ |
| Heatmap Layer | ✅ | ✅ | ✅ |
Installation #
flutter pub add maplibre_gl
iOS #
If you use location features, add to ios/Runner/Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Explain why the app needs the user's location]</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>[Explain why the app needs the user's location in the background]</string>
NSLocationAlwaysUsageDescription is only required if you request always-on location access.
Android #
For location features, add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
The plugin does not request permissions at runtime — handle that yourself (e.g. with location).
Web #
Add to the <head> of web/index.html:
<script src='https://unpkg.com/maplibre-gl@^5.24.0/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@^5.24.0/dist/maplibre-gl.css' rel='stylesheet'/>
Always use the version that matches your installed maplibre_gl_web — check the example app's web/index.html for the tag currently in use.
Quick start #
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
class SimpleMapPage extends StatefulWidget {
const SimpleMapPage({super.key});
@override
State<SimpleMapPage> createState() => _SimpleMapPageState();
}
class _SimpleMapPageState extends State<SimpleMapPage> {
final _controller = Completer<MapLibreMapController>();
static const _initial = CameraPosition(target: LatLng(0, 0), zoom: 2);
@override
Widget build(BuildContext context) {
return Scaffold(
body: MapLibreMap(
initialCameraPosition: _initial,
onMapCreated: _controller.complete,
styleString: 'https://demotiles.maplibre.org/style.json',
),
);
}
}
See the example app for markers, layers, offline tiles and PMTiles.
Usage #
Camera #
await controller.animateCamera(
CameraUpdate.newLatLngBounds(bounds, left: 24, top: 24, right: 24, bottom: 24),
);
Defer camera animations until the style is ready (onStyleLoadedCallback).
Annotations #
await controller.addSymbol(SymbolOptions(
geometry: LatLng(37.7749, -122.4194),
iconImage: 'assets/icon_pin.png', // register as a style image first
iconSize: 1.2,
));
For lines/fills via a GeoJSON source, see the example app. Add sources before the layers that depend on them.
Styles #
styleString accepts any of: a remote URL, a bundled asset (registered in pubspec.yaml), an absolute file path, or a raw JSON string.
Tiles requiring an API key #
Embed the key in the tile URL:
https://tiles.example.com/{z}/{x}/{y}.vector.pbf?api_key=YOUR_KEY
Restrict keys at the provider (domain/referer, usage caps) and inject them at build time rather than committing them.
Advanced topics #
Offline / mbtiles — copy mbtiles, sprites and glyphs from assets to a writable directory, then point your style sources there. See issues #338 and #318.
PMTiles — load datasets via a custom protocol handler; see pmtiles.dart in the example app.
Expressions — data-driven styling follows the MapLibre style spec. For cross-platform safety use ["!", ["has", "field"]] rather than ["!has", "field"] (see FAQ).
Code generation — layer/source helpers are generated. Do not edit them directly; run melos run generate && melos format-all.
Migration #
Most APIs are source-compatible with flutter-mapbox-gl:
- Rename the dependency to
maplibre_gl. - Remove Mapbox token initialization — MapLibre uses open assets or your own endpoints.
- Replace Mapbox-specific style URLs with self-hosted or MapLibre-compatible ones.
- Audit filter expressions for iOS (
["!has", ...]→["!", ["has", ...]]).
Troubleshooting #
Loading mbtiles/sprites/glyphs from assets — copy to a writable directory first, then reference the new path.
Android UnsatisfiedLinkError — make sure abiFilters covers the ABIs you ship:
ndk { abiFilters 'armeabi-v7a','arm64-v8a','x86_64','x86' }
iOS crash on location — add NSLocationWhenInUseUsageDescription (see iOS setup).
iOS filter property must be a string — replace ["!has", "value"] with ["!", ["has", "value"]].
Architecture #
Multi-package melos workspace:
maplibre_gl— main plugin (mobile/native bindings)maplibre_gl_web— web implementationmaplibre_gl_platform_interface— shared platform interfacescripts/— code generation
Layer/source property helpers and expression utilities are generated. Don't edit generated files; run melos run generate && melos format-all.
Contributing #
dart pub global activate melos
melos bootstrap
Then run the example app to validate changes. See CONTRIBUTING.md before opening a PR.
Resources #
- API docs: https://pub.dev/documentation/maplibre_gl/latest/
- Changelog — always check before upgrading
- Help: Discussions, Issues, Slack, StackOverflow #maplibre
License #
See LICENSE.