Migrate from flutter_map
flutter_map and maplibre both take their layers as widget parameters and
update with setState(), so the structure of your map code stays the same.
The difference is the renderer: flutter_map draws tiles and layers with
Flutter in Dart, while maplibre renders vector and raster tiles with
MapLibre on the GPU and shows the result in a Flutter Texture (an HTML
element on web). Vector tiles,
labels, large data, 3D extrusions, offline regions and the user location
are built in instead of plugins.
Both packages have a MapOptions, MapController, MarkerLayer,
PolylineLayer, PolygonLayer and CircleLayer, so replace the import in
each file instead of using both.
1. Replace the dependency
flutter pub remove flutter_map latlong2
flutter pub add maplibre:^0.4.0-pre.2
Also remove the plugins that maplibre covers, such as vector_map_tiles,
flutter_map_tile_caching, flutter_map_location_marker and
flutter_map_marker_cluster. Then follow
Platform setup.
2. Map data
flutter_map gets its map from TileLayers. maplibre loads a
map style, which names the tile sources and how to draw them.
Use the style of a vector tile provider, or keep your raster tiles with a
style of one raster source:
FlutterMap(
options: const MapOptions(
initialCenter: LatLng(47.68, 9.17),
initialZoom: 7,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.example.app',
),
RichAttributionWidget(
attributions: [TextSourceAttribution('OpenStreetMap contributors')],
),
],
)
MapLibreMap(
options: MapOptions(
style: MapStyle.json('''
{
"version": 8,
"sources": {
"osm": {
"type": "raster",
"tiles": ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
"tileSize": 256,
"attribution": "© <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap contributors</a>"
}
},
"layers": [{"id": "osm", "type": "raster", "source": "osm"}]
}
'''),
initCamera: .position(
center: Geographic(lon: 9.17, lat: 47.68),
zoom: 7,
),
),
// OpenStreetMap requires an attribution that stays visible.
children: const [SourceAttribution(keepExpanded: true)],
)
For vector tiles, use a style URL instead, for example
MapStyle.url('https://tiles.openfreemap.org/styles/liberty'). Additional
tile layers become sources and
style layers, and WMS or other overlays become a
RasterSource.
3. Map options and controller
Coordinates are geobase Geographic(lon: …, lat: …) instead of latlong2's
LatLng(lat, lng), and bounds are a GeoBox instead of LatLngBounds.
| flutter_map | maplibre |
|---|---|
initialCenter, initialZoom, initialRotation |
MapOptions.initCamera: .position(center:, zoom:, bearing:) |
initialCameraFit |
MapOptions.initCamera: .bounds(…) or .points(…) |
minZoom, maxZoom |
MapOptions.minZoom, maxZoom |
cameraConstraint: CameraConstraint.contain(bounds:) |
MapOptions.maxBounds |
interactionOptions: InteractionOptions(flags: …) |
MapOptions.movements and MapOptions.gestures |
onTap, onLongPress, onSecondaryTap |
onClick, onLongClick, onSecondaryClick |
onPositionChanged, onMapEvent |
onCameraMove and the other events |
onMapReady |
onMapCreated, onStyleLoaded |
mapController.move(center, zoom) |
controller.moveCamera(.position(center:, zoom:)) |
mapController.rotate(degree) |
controller.moveCamera(.position(bearing:)) |
mapController.fitCamera(CameraFit.bounds(…)) |
controller.moveCamera(.bounds(…, padding:)) |
mapController.camera, MapCamera.of(context) |
controller.camera, CameraPosition.of(context) |
camera.latLngToScreenOffset, screenOffsetToLatLng |
controller.toScreenLocation, toLngLat |
flutter_map animates with plugins such as flutter_map_animations;
maplibre has controller.animateCamera with CameraAnimation.ease and
.fly. Unlike flutter_map, the map can also pitch, so MapOptions has
minPitch and maxPitch. See Map & Camera.
4. Layers
Annotation layers go into MapLibreMap.layers, not children. They take
geobase Features and style each one with
expressions, instead of an object with a
style per marker or line.
PolylineLayer(
polylines: [
Polyline(
points: const [LatLng(47.68, 9.17), LatLng(48, 9.5)],
color: Colors.red,
strokeWidth: 4,
),
],
)
PolylineLayer(
polylines: [
Feature(
geometry: LineString.from(const [
Geographic(lon: 9.17, lat: 47.68),
Geographic(lon: 9.5, lat: 48),
]),
),
],
color: const .literal(Colors.red),
width: const .literal(4),
)
| flutter_map | maplibre |
|---|---|
MarkerLayer with Marker(child: …) widgets |
WidgetLayer in children for widgets, or a MarkerLayer in layers for icons and labels drawn by the map |
PolylineLayer, Polyline |
PolylineLayer with LineString features |
PolygonLayer, Polygon |
PolygonLayer with Polygon features |
CircleLayer, CircleMarker |
CircleLayer with Point features. The radius is in logical pixels; draw a polygon for a radius in meters. |
hitNotifier, GestureDetector on markers |
onClick, onEnter, onLeave on the layer, see Click and Hover |
flutter_map_dragmarker |
draggable: true on the layer, see Drag |
flutter_map_marker_cluster |
A GeoJsonSource with a GeoJsonCluster |
flutter_map_heatmap |
HeatmapStyleLayer |
vector_map_tiles |
A style with vector tiles, built in |
A MarkerLayer of the map draws thousands of points without building a
widget for each. Prefer it over a WidgetLayer when the markers are icons or
text. For large data, a source with
style layers skips comparing the layer lists on
rebuilds.
5. Overlays and plugins
Widgets in MapLibreMap.children are drawn over the map, like non-layer
children in flutter_map. They can read the map with
MapController.of(context), MapOptions.of(context) and
CameraPosition.of(context), as plugins read MapCamera.of(context).
| flutter_map plugin | maplibre |
|---|---|
flutter_map_location_marker |
UserLocationLayer with maplibre_geolocator |
flutter_map_tile_caching |
Built-in cache and offline regions (not on web) |
RichAttributionWidget |
SourceAttribution, which reads the attribution of the style's sources |
Scalebar, compass and zoom button plugins |
MapScalebar, MapCompass, MapControlButtons, see User Interface |
6. What changes for your app
- The map is drawn by MapLibre, not by Flutter widgets, so painting the map
with your own widgets or
CustomPainters per tile is not possible. Draw over it withchildren, or style it with layers. - Styles and layers follow the MapLibre style spec, so you can edit them in Maputnik and reuse them in MapLibre apps on other platforms.