Flutter MapLibre GL lets you embed interactive, vector-tile based, fully styleable maps directly inside your Flutter app across mobile & web.
This project is a fork of flutter-mapbox-gl, replacing its usage of Mapbox GL libraries with the open source MapLibre GL libraries.
Click to expand
| Capability | Description | 
|---|---|
| Vector tile rendering | High quality, styleable vector maps via MapLibre engines | 
| Dynamic styling | Swap or mutate styles at runtime, apply expressions | 
| Camera control | Smooth programmatic & gesture driven map camera updates | 
| User location | Native location indicator & tracking (permissions required) | 
| Annotations | Symbols, circles, lines, fills, fill extrusions, heatmaps | 
| Web support | Backed by maplibre-gl-js for web targets | 
| Offline friendly | Patterns for mbtiles / cached assets (see advanced topics) | 
| Extensible | Separate platform interface & web implementation packages | 
MapLibre is a vendor-neutral, open source set of mapping libraries born from the community. Using MapLibre helps you:
- Avoid vendor lock-in & proprietary billing tie-ins
 - Host your own tiles or mix commercial/open sources
 - Keep transparent, auditable code in production
 - Align with OSS licensing and long-term sustainability
 - One of the fastest map SDKs available. Capable of drawing a large number of vector objects.
 
If you previously used flutter-mapbox-gl, you can migrate with minimal changes (see migration guide).
Underlying engines:
- Android / iOS — maplibre-native
 - Web — maplibre-gl-js
 
Note: Only a subset of the native SDK APIs are currently exposed. PRs to extend surface area are welcome.
| Feature | Android | iOS | Web | 
|---|---|---|---|
| Style | ✅ | ✅ | ✅ | 
| Camera | ✅ | ✅ | ✅ | 
| Gesture | ✅ | ✅ | ✅ | 
| User Location | ✅ | ✅ | ✅ | 
| Symbol | ✅ | ✅ | ✅ | 
| Circle | ✅ | ✅ | ✅ | 
| Line | ✅ | ✅ | ✅ | 
| Fill | ✅ | ✅ | ✅ | 
| Fill Extrusion | ✅ | ✅ | ✅ | 
| Heatmap Layer | ✅ | ✅ | ✅ | 
Add the dependency:
dependencies:
  maplibre_gl: ^LATEST_VERSION(See the current version badge above or on pub.dev).
Then run:
flutter pub getAdd permission usage description for location (if using location features) to ios/Runner/Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Explain why the app needs the user's location]</string>Minimum supported Kotlin version: 2.1.0. In android/settings.gradle:
plugins {
  id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}Add location permissions if required in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />Request permissions at runtime yourself (e.g. via the location plugin). The plugin does not prompt automatically.
Include the following JavaScript and CSS files in the <head> of your web/index.html file:
<script src="https://unpkg.com/maplibre-gl@^4.3/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@^4.3/dist/maplibre-gl.css" rel="stylesheet" />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 _controllerCompleter = Completer<MapLibreMapController>();
  bool _styleLoaded = false;
  static const _initial = CameraPosition(target: LatLng(0, 0), zoom: 2);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('MapLibre Quick Start')),
      floatingActionButton: _styleLoaded
          ? FloatingActionButton.small(
              onPressed: _goHome,
              child: const Icon(Icons.explore),
            )
          : null,
      body: MapLibreMap(
        initialCameraPosition: _initial,
        onMapCreated: (c) => _controllerCompleter.complete(c),
        onStyleLoadedCallback: () => setState(() => _styleLoaded = true),
      ),
    );
  }
  Future<void> _goHome() async {
    final c = await _controllerCompleter.future;
    await c.animateCamera(CameraUpdate.newCameraPosition(_initial));
  }
}Check the example project for richer demos (markers, layers, offline patterns, PMTiles, etc.).
await controller.animateCamera(
  CameraUpdate.newLatLngBounds(bounds, left: 24, top: 24, right: 24, bottom: 24),
);Add symbols (markers):
await controller.addSymbol(SymbolOptions(
  geometry: LatLng(37.7749, -122.4194),
  iconImage: 'assets/icon_pin.png', // ensure added as style image first
  iconSize: 1.2,
));Add line layer (via geojson source) – see example app for full workflow.
Provide a styleString:
- Remote URL (
https://.../style.json) - App asset (
assets/map_style.jsonwith pubspec asset registration) - Local file system absolute path
 - Raw JSON string
 
Embed the key directly in the vector tile URL:
https://tiles.example.com/{z}/{x}/{y}.vector.pbf?api_key=YOUR_KEY
Copy mbtiles (or sprites/glyphs) from bundled assets to a writable directory (e.g. cache) and point your style sources there. See issues #338 & #318 for community approaches.
The example app includes a pmtiles usage sample demonstrating how to load datasets via a custom protocol handler / tile source. (Look for pmtiles.dart in maplibre_gl_example.)
Use data‑driven styling with expressions similar to Mapbox / MapLibre style spec. Some platform discrepancies exist (see FAQ around !has). For cross‑platform safety prefer form: ["!", ["has", "field"]].
Layer/source property helpers & expression utilities are generated from templates under scripts/. Do not edit generated files directly—run:
melos run generate
melos format-all
This repository is a multi-package workspace:
maplibre_gl– main Flutter plugin (mobile/native bindings)maplibre_gl_web– web implementationmaplibre_gl_platform_interface– shared platform interface (enables adding alternative implementations)scripts/– code generation templates & tooling
Most APIs are source-compatible. Key differences to watch:
- Dependency name changes to 
maplibre_gl. - Remove any Mapbox token initialization (MapLibre uses open assets or your own tile endpoints).
 - If you referenced Mapbox-specific style endpoints, replace with self-hosted / MapLibre friendly ones.
 - Audit expressions for iOS compatibility (
["!has", ...]variant change noted in FAQ). 
- Reuse a single 
MapLibreMapwidget when possible instead of recreating it in tab/page switches. - Batch style mutations (e.g. add sources before adding dependent layers) to avoid intermediate layout recalculations.
 - Use simpler geometries or tiling strategies for very dense data.
 - Avoid large uncompressed GeoJSON inline; host as a URL source if size is large.
 - Defer camera animations until style load (
onStyleLoadedCallback). 
If you embed API keys in style URLs for third-party tiles, ensure you:
- Restrict keys at the provider (domain / referer / usage caps).
 - Avoid shipping unnecessary privileges (read-only tile scopes where possible). Environment variable injection at build time is recommended for CI-driven apps—avoid committing raw secrets.
 
Copy them to a writable directory (cache/documents) first, then reference the new path in the style or source configuration. See issues #338 & #318.
Ensure abiFilters include the ABIs you intend to ship:
buildTypes {
  release {
    ndk { abiFilters 'armeabi-v7a','arm64-v8a','x86_64','x86' }
  }
}Add NSLocationWhenInUseUsageDescription (see iOS setup).
Error: Invalid filter value: filter property must be a string. Replace ["!has", "value"] with ["!", ["has", "value"]].
- Minimal example (for pub.dev & quick start): see 
example/lib/main.dart - Full featured example app: 
maplibre_gl_example - API docs: https://pub.dev/documentation/maplibre_gl/latest/
 - MapLibre upstream projects: maplibre-gl-js, maplibre-native
 
Releases follow semantic versioning as practical; breaking changes are documented in the CHANGELOG (root and per package). Always consult the changelog when upgrading.
This is a multi-package workspace managed by melos.
Basic flow:
dart pub global activate melos  # activate melos package
melos bootstrap                 # initialize & link local packages
Then open & run the example app to validate and check changes.
Please read the full CONTRIBUTING.md before submitting a PR.
Generated code: Some API surface (layer/source property helpers, expression utilities) is produced via a generator under
scripts/. Do not modify generated files directly — see Code Generation & Formatting section for workflow.
- Join our Slack channel
 - StackOverflow tag: #maplibre
 - Discussions: https://github.com/maplibre/flutter-maplibre-gl/discussions
 - Bugs / Features: Open an issue (include reproduction details/logs where possible)
 
See LICENSE for details.
If this plugin helps you build something cool, consider starring the repo to support visibility.
