A Dart Implementation of Leaflet for Flutter App, flutter_map
flutter_map
A Dart implementation of Leaflet to provide a Map widget for Flutter apps.
Contents
Installation
Add flutter_map to your pubspec:
dependencies: flutter_map: any # or the latest version on Pub
Android
Configure your app to use the INTERNET
permission in the manifest file located in <project root>/android/app/src/main/AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET"/>
Usage
Configure the map using MapOptions
and layer options:
Widget build(BuildContext context) { return FlutterMap( options: MapOptions( center: LatLng(51.5, -0.09), zoom: 13.0, ), layers: [ TileLayerOptions( urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c'], attributionBuilder: (_) { return Text("© OpenStreetMap contributors"); }, ), MarkerLayerOptions( markers: [ Marker( width: 80.0, height: 80.0, point: LatLng(51.5, -0.09), builder: (ctx) => Container( child: FlutterLogo(), ), ), ], ), ], ); }
Alternatively, initialize the map by specifying bounds
instead of center
and zoom
.
MapOptions( bounds: LatLngBounds(LatLng(58.8, 6.1), LatLng(59, 6.2)), boundsOptions: FitBoundsOptions(padding: EdgeInsets.all(8.0)), ),
Azure Maps provider
To configure Azure Maps, use the following MapOptions
and layer options:
Widget build(BuildContext context) { return new FlutterMap( options: new MapOptions( center: new LatLng(51.5, -0.09), zoom: 13.0, ), layers: [ new TileLayerOptions( urlTemplate: "https://atlas.microsoft.com/map/tile/png?api-version=1&layer=basic&style=main&tileSize=256&view=Auto&zoom={z}&x={x}&y={y}&subscription-key={subscriptionKey}", additionalOptions: { 'subscriptionKey': '<YOUR_AZURE_MAPS_SUBSCRIPTON_KEY>' }, ), new MarkerLayerOptions( markers: [ new Marker( width: 80.0, height: 80.0, point: new LatLng(51.5, -0.09), builder: (ctx) => new Container( child: new FlutterLogo(), ), ), ], ), ], ); }
To use Azure Maps, set up an account and get a subscription key
Open Street Map provider
Configure the map to use Open Street Map by using the following MapOptions
and layer options:
Widget build(BuildContext context) { return new FlutterMap( options: new MapOptions( center: new LatLng(51.5, -0.09), zoom: 13.0, ), layers: [ new TileLayerOptions( urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c'] ), new MarkerLayerOptions( markers: [ new Marker( width: 80.0, height: 80.0, point: new LatLng(51.5, -0.09), builder: (ctx) => new Container( child: new FlutterLogo(), ), ), ], ), ], ); }
Widget Layers
Use the new way to create layers (compatible with previous version)
Widget build(BuildContext context) { return FlutterMap( options: MapOptions( center: LatLng(51.5, -0.09), zoom: 13.0, ), layers: [ MarkerLayerOptions( markers: [ Marker( width: 80.0, height: 80.0, point: LatLng(51.5, -0.09), builder: (ctx) => Container( child: FlutterLogo(), ), ), ], ), ], children: <Widget>[ TileLayerWidget(options: TileLayerOptions( urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c'] )), MarkerLayerWidget(options: MarkerLayerOptions( markers: [ Marker( width: 80.0, height: 80.0, point: LatLng(51.5, -0.09), builder: (ctx) => Container( child: FlutterLogo(), ), ), ], )), ], ); }
Custom CRS
By default flutter_map supports only WGS84 (EPSG:4326) and Google Mercator (EPSG:3857) projections. The proj4dart package provides additional reference systems (CRS).
To use proj4dart, first define a custom CRS:
var resolutions = <double>[32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128]; var maxZoom = (resolutions.length - 1).toDouble(); var epsg3413CRS = Proj4Crs.fromFactory( code: 'EPSG:3413', proj4Projection: proj4.Projection.add('EPSG:3413', '+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'), resolutions: resolutions, );
Then use the custom CRS in the map layer and in WMS layers:
child: FlutterMap( options: MapOptions( // Set the map's CRS crs: epsg3413CRS, center: LatLng(65.05166470332148, -19.171744826394896), maxZoom: maxZoom, ), layers: [ TileLayerOptions( wmsOptions: WMSTileLayerOptions( // Set the WMS layer's CRS too crs: epsg3413CRS, baseUrl: 'https://www.gebco.net/data_and_products/gebco_web_services/north_polar_view_wms/mapserv?', layers: ['gebco_north_polar_view'], ), ), ], );
For more details, see the custom CRS README.
Run the example
See the example/
folder for a working example app. Here is a snippet:
- main.dart
import 'package:flutter/material.dart';
import 'package:flutter_map_example/pages/map_inside_listview.dart';
import 'package:flutter_map_example/pages/network_tile_provider.dart';
import './pages/animated_map_controller.dart';
import './pages/circle.dart';
import './pages/custom_crs/custom_crs.dart';
import './pages/esri.dart';
import './pages/home.dart';
import './pages/live_location.dart';
import './pages/many_markers.dart';
import './pages/map_controller.dart';
import './pages/marker_anchor.dart';
import './pages/marker_rotate.dart';
import './pages/moving_markers.dart';
import './pages/offline_map.dart';
import './pages/on_tap.dart';
import './pages/overlay_image.dart';
import './pages/plugin_api.dart';
import './pages/plugin_scalebar.dart';
import './pages/plugin_zoombuttons.dart';
import './pages/polyline.dart';
import './pages/reset_tile_layer.dart';
import './pages/sliding_map.dart';
import './pages/stateful_markers.dart';
import './pages/tap_to_add.dart';
import './pages/tile_builder_example.dart';
import './pages/tile_loading_error_handle.dart';
import './pages/widgets.dart';
import './pages/wms_tile_layer.dart';
import 'pages/interactive_test_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Map Example',
theme: ThemeData(
primarySwatch: mapBoxBlue,
),
home: HomePage(),
routes: <String, WidgetBuilder>{
NetworkTileProviderPage.route: (context) => NetworkTileProviderPage(),
WidgetsPage.route: (context) => WidgetsPage(),
TapToAddPage.route: (context) => TapToAddPage(),
EsriPage.route: (context) => EsriPage(),
PolylinePage.route: (context) => PolylinePage(),
MapControllerPage.route: (context) => MapControllerPage(),
AnimatedMapControllerPage.route: (context) =>
AnimatedMapControllerPage(),
MarkerAnchorPage.route: (context) => MarkerAnchorPage(),
PluginPage.route: (context) => PluginPage(),
PluginScaleBar.route: (context) => PluginScaleBar(),
PluginZoomButtons.route: (context) => PluginZoomButtons(),
OfflineMapPage.route: (context) => OfflineMapPage(),
OnTapPage.route: (context) => OnTapPage(),
MarkerRotatePage.route: (context) => MarkerRotatePage(),
MovingMarkersPage.route: (context) => MovingMarkersPage(),
CirclePage.route: (context) => CirclePage(),
OverlayImagePage.route: (context) => OverlayImagePage(),
SlidingMapPage.route: (_) => SlidingMapPage(),
WMSLayerPage.route: (context) => WMSLayerPage(),
CustomCrsPage.route: (context) => CustomCrsPage(),
LiveLocationPage.route: (context) => LiveLocationPage(),
TileLoadingErrorHandle.route: (context) => TileLoadingErrorHandle(),
TileBuilderPage.route: (context) => TileBuilderPage(),
InteractiveTestPage.route: (context) => InteractiveTestPage(),
ManyMarkersPage.route: (context) => ManyMarkersPage(),
StatefulMarkersPage.route: (context) => StatefulMarkersPage(),
MapInsideListViewPage.route: (context) => MapInsideListViewPage(),
ResetTileLayerPage.route: (context) => ResetTileLayerPage(),
},
);
}
}
// Generated using Material Design Palette/Theme Generator
// http://mcg.mbitson.com/
// https://github.com/mbitson/mcg
const int _bluePrimary = 0xFF395afa;
const MaterialColor mapBoxBlue = MaterialColor(
_bluePrimary,
<int, Color>{
50: Color(0xFFE7EBFE),
100: Color(0xFFC4CEFE),
200: Color(0xFF9CADFD),
300: Color(0xFF748CFC),
400: Color(0xFF5773FB),
500: Color(_bluePrimary),
600: Color(0xFF3352F9),
700: Color(0xFF2C48F9),
800: Color(0xFF243FF8),
900: Color(0xFF172EF6),
},
);
To run it, in a terminal cd into the folder. Then execute ulimit -S -n 2048
(ref). Then execute flutter run
with a running emulator.
Downloading and caching offline maps
This section provides an overview of the available caching tile providers. If you would like to provide preconfigured and prepackaged map tiles to your app users, see the ‘Preconfigured Offline Maps’ section below.
The two available options included in flutter_map
1. Use NetworkImage
by using NonCachingNetworkTileProvider
Whilst the name might make you think differently, it is designed to prevent you from using it and expecting it to cache; because it doesn’t.
The FlutterMap
NonCachingNetworkTileProvider
implementaion uses NetworkImage
which should cache images in memory until the app restart (through Image.network
). See the Image.network docs and NetworkImage docs for more details.
2. Using the cached_network_image
dependency
This dependency has an ImageProvider
that caches images to disk, which means the cache persists through an app restart. You’ll need to include the package in your pubspec.yaml
.
Create your own provider using the code below:
import 'package:cached_network_image/cached_network_image.dart'; class CachedTileProvider extends TileProvider { const CachedTileProvider(); @override ImageProvider getImage(Coords<num> coords, TileLayerOptions options) { return CachedNetworkImageProvider( getTileUrl(coords, options), //Now you can set options that determine how the image gets cached via whichever plugin you use. ); } }
Then, add the CachedTileProvider
TileProvider
to the appropriate TileLayerOptions
:
TileLayerOptions( urlTemplate: 'https://example.com/{x}/{y}/{z}', tileProvider: const CachedTileProvider() )
Offline Maps using TileMill
This section provides instructions for preconfigured and prepackaged offline maps. To see how to setup caching and downloading, see the ‘Dynamically Downloading & Caching Offline Maps’ section above.
This guide uses an open source program called TileMill.
First, install TileMill on your machine. Then, follow these instructions.
Once you have your map exported to .mbtiles
, you can use [mbtilesToPng][mbTilesToPngs] to unpack into /{z}/{x}/{y}.png
.
Move this to assets folder and add the appropriate asset directories to pubspec.yaml
. Minimum required fields for this solution are:
Widget build(ctx) { return FlutterMap( options: MapOptions( center: LatLng(56.704173, 11.543808), zoom: 13.0, swPanBoundary: LatLng(56.6877, 11.5089), nePanBoundary: LatLng(56.7378, 11.6644), ), layers: [ TileLayerOptions( tileProvider: AssetTileProvider(), urlTemplate: "assets/offlineMap/{z}/{x}/{y}.png", ), ], ); }
A missing asset error will be thrown if the PanBoundaries are outside the offline map boundary.
See the flutter_map_example/
folder for a working example.
See also FileTileProvider()
, which loads tiles from the filesystem.
Plugins
- flutter_map_tile_caching: Provides ability to properly cache tiles for offline use & easily download a region of a map for later offline use
- flutter_map_marker_cluster: Provides Beautiful Animated Marker Clustering functionality
- user_location: A plugin to handle and plot the current user location in FlutterMap
- flutter_map_location: A plugin to request and display the users location and heading on the map
- flutter_map_location_marker: A simple and powerful plugin display the users location and heading
- flutter_map_tappable_polyline: A plugin to add
onTap
callback toPolyline
- lat_lon_grid_plugin: Adds a latitude / longitude grid as plugin to the FlutterMap
- flutter_map_marker_popup: A plugin to show customisable popups for markers.
- map_elevation: A widget to display elevation of a track (polyline) like Leaflet.Elevation
- flutter_map_floating_marker_titles: Displaying floating marker titles on the map view
- vector_map_tiles: A plugin that enables the use of vector tiles.
Roadmap
For the latest roadmap, please see the Issue Tracker
GitHub
Source Code: flutter_map.