This Flutter plugin allows to show embedded interactive and customizable vector maps as a Flutter widget.
- 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
- Support for web through maplibre-gl-js
- Support for android and iOS through maplibre-native
This project only supports a subset of the API exposed by these libraries.
Feature | Android | iOS | Web |
---|---|---|---|
Style | ✅ | ✅ | ✅ |
Camera | ✅ | ✅ | ✅ |
Gesture | ✅ | ✅ | ✅ |
User Location | ✅ | ✅ | ✅ |
Symbol | ✅ | ✅ | ✅ |
Circle | ✅ | ✅ | ✅ |
Line | ✅ | ✅ | ✅ |
Fill | ✅ | ✅ | ✅ |
Fill Extrusion | ✅ | ✅ | ✅ |
Heatmap Layer | ✅ | ✅ | ✅ |
For installing the plugin, follow the instructions on pub.dev.
In order to access the device location, you need to add the following key
to the ios/Runner/Info.plist
file, to explain why you need access to their
location data:
<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
</dict>
The minimum supported Kotlin version is 1.9.0
. Accordingly, you will have to
set your Kotlin version in the android/settings.gradle
file like so:
plugins {
id "org.jetbrains.kotlin.android" version "1.9.0" apply false
}
If you want to show the user's location on the map you need to add
the ACCESS_COARSE_LOCATION
or ACCESS_FINE_LOCATION
permission in the
application manifest android/app/src/main/AndroidManifest.xml
:
<manifest>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
Starting from Android API level 23 you also need to request it at runtime. This plugin does not handle this for you. Our example app uses the flutter "location" plugin for this.
For the map to work in the web, 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'/>
The following shows you how to add the map widget to your code and start interacting with it. For more examples, head over to the example project.
class MapParentWidgetState extends State<MapParentWidget> {
final Completer<MapLibreMapController> mapController = Completer();
bool canInteractWithMap = false;
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation
.miniCenterFloat,
floatingActionButton: canInteractWithMap
? FloatingActionButton(
onPressed: _moveCameraToNullIsland,
mini: true,
child: const Icon(Icons.restore),
)
: null,
body: MapLibreMap(
onMapCreated: (controller) => mapController.complete(controller),
initialCameraPosition: _nullIsland,
onStyleLoadedCallback: () => setState(() => canInteractWithMap = true),
),
);
}
void _moveCameraToNullIsland() =>
mapController.future.then((c) =>
c.animateCamera(CameraUpdate.newCameraPosition(_nullIsland)));
}
Map styles can be supplied by setting the styleString
in the MapLibreMap
constructor. The following formats are supported:
- Passing the URL of the map style. This should be a custom map style served
remotely using a URL that start with
http(s)://
- Passing the style as a local asset. Create a JSON file in the
assets
and add a reference inpubspec.yml
. Set the style string to the relative path for this asset in order to load it into the map. - Passing the style as a local file. create an JSON file in app directory (e.g. ApplicationDocumentsDirectory). Set the style string to the absolute path of this JSON file.
- Passing the raw JSON of the map style. This is only supported on Android.
If your tile source requires an API key, we recommend directly specifying a source url with the API key included. For example:
https://tiles.example.com/{z}/{x}/{y}.vector.pbf?api_key={your_key}
- Check the API documentation.
- See example implementations in our example project.
- For more information about the MapLibre libraries visit maplibre-gl-js and maplibre-native.
- Need help with your code?: Check the discussions on this repo or open a new one. Or look for previous questions on the #maplibre tag — or ask a new question.
- Have a bug to report? Open an issue. If possible, include a full log, code and information which shows the issue.
- Have a feature request? Open an issue. Tell us what the feature should do and why you want the feature.
Click here to expand / hide.
One approach that has been used successfully to do that is to copy the files from the app's assets directory to another directory, e.g. the app's cache directory, and then reference that location. See e.g. issues #338 and #318
Click here to expand / hide.
Update buildTypes in android\app\build.gradle
buildTypes {
release {
// other configs
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86_64', 'x86'
}
}
}
Click here to expand / hide.
Please include the NSLocationWhenInUseUsageDescription
as
described here
Click here to expand / hide.
Have a look in your LayerProperties
object, if you supply a lineColor
argument, (or any color argument) the issue might come from here.
Android supports the following format : 'rgba(192, 192, 255, 1.0)'
, but on
iOS, this doesn't work!
You have to have the color in the following format : #C0C0FF
'NSInvalidArgumentException', reason: 'Invalid filter value: filter property must be a string'
Click here to expand / hide.
Check if one of your expression is : ["!has", "value"]
. Android support this
format, but iOS does not.
You can replace your expression with : ["!",["has", "value"] ]
which works
both in Android and iOS.
Note : iOS will display the
error :
NSPredicate: Use of 'mgl_does:have:' as an NSExpression function is forbidden
,
but it seems like the expression still works well.
Setup melos and run the
melos bootstrap
command in the plugin root directory. Run the example app and familiarize yourself with the plugin directory structure.
Feedback, contributing pull requests and bug reports are very welcome - check the CONTRIBUTING.md guidelines.