Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A collection of cool projects that use flutter_map
Want to advertise your project here? For more information, and to apply, please see:
We also appreciate donations, and we're extremely grateful for anything you can spare.
We'll donate 15% of what we receive to the OpenStreetMap Foundation, as a thanks for their excellent work.
For more information, details about benefits, and the link to donate, please see #support-us.
There's many more that aren't listed here! See the dependents list on GitHub, and the #showcase channel for more minor projects on our Discord server.
Please consider donating anything you can to us, we're extremely grateful for any and all donations. They keep us going and will allow us to cover any unforeseen or future costs, and potentially open up more doors and opportunities in future!
We'll donate 15% of what we receive to the OpenStreetMap Foundation, as a thanks for their excellent work. The remainder goes directly to improving flutter_map.
Enter a nickname to be listed below in #past-supporters, and enter your Discord username to be granted the "Supporter" role on our server and gain access to a special supporter-only chat channel. These benefits last indefinitely (but may take 48 hours to grant, as this is done manually).
Donate with GBP & British specific payment methods
Donate with EUR & European specific payment methods
We support a variety of payment methods through Stripe, although some have lower fees for us. In general, cards have the highest fees, and the closer the method to the bank, the lower the fees. Donating in the GBP currency also reduces our fees, but we provide EUR checkout to add extra payment methods for those living in Europe. If paying in a currency other than your own, your bank's exchange rate/terms will apply. At this time, only one-time donations are accepted: please get in touch if you'd like to donate on a regular basis.
We use Stripe for payments, analytics, and other business services. Stripe may collect personal data including via cookies and similar technologies. The personal data Stripe collects may include transactional data and identifying information about devices that connect to its services. Stripe uses this information to operate and improve the services it provides to us, including for fraud detection, loss prevention, authentication, and analytics related to the performance of its services. You can learn more about Stripe and read its privacy policy at https://stripe.com/privacy.
Huge thanks to all our past supporters, you help keep this project going. In no particular order, thanks to:
androidseb
Roundtrip
corepuncher
Maxi
V3ntus
... and everyone else who donated anonymously
If you don't know about standard map things, such as the latitude/longitude system and projections, you should probably read about these first!
If you want a truly British insight into this, look no further than: https://youtu.be/3mHC-Pf8-dU & https://youtu.be/jtBV3GgQLg8.
Interactive maps are formed from multiple layers of data, which can be panned (moved), rotated, and sometimes tilted/pitched, based on the user's gesture input, or another programmatic control.
One type of layer included on every map is known as a tile layer, which displays tiles, square segments of a map.
When multiple tiles, which are each the same dimensions, are laid out around each other, they give the illusion of one continuous map.
Tiles can be referenced/identified in a few different ways, such as:
Slippy Map Convention (the most popular/common)
TMS (very similar to the Slippy Map Convention)
Tiles themselves can be of two types:
Raster Each tile is a normal pre-rendered standard image, such as JPG or PNG
Vector Each tile is a special format containing the data for the tile, and is then rendered by the end library
This library/documentation focuses on maps accessible via the Slippy Map Convention, although all are supported.
This library only supports raster tiles. See Raster vs Vector Tiles for more information.
For more information about the Slippy Map Convention, visit the OpenStreetMap Wiki.
Slippy map tiles are accessed by 3 coordinates, x/y/z.
X & Y coordinates correspond to all the latitudes and longitudes contained within that tile, however they are not actual longitude and latitude. For example, geographic coordinate (61.127, -0.123) in the tile (128983, 430239).
The Z value represents the current zoom level, where one tile (0/0/0) covers the entire planet with extremely low detail at level 0, to level 20 (although some tile servers will support even higher zoom levels) where over 1 trillion tiles are required to cover the entire surface of the Earth.
Tiles, especially raster tiles, take a lot of computing power and time to generate, because of the massive scale of all the input and output data. Therefore, most tiles are sourced externally, from an online tile server (either publicly or by users holding an API key), or sometimes from the local filesystem or asset store of the app.
A tile provider (within flutter_map) is responsible for:
Constructing the path/URL to a tile, when given its coordinates (x/y/z): #slippy-map-convention
Using an ImageProvider
or other mechanism to fetch that tile: #sourcing-tiles
Performing any other processing steps, such as caching
But don't worry! flutter_map (or a plugin) creates a provider for you, so for most use cases and tile sources, you shouldn't need to handle this yourself!
This can be quite confusing for newcomers!
Within this library, 'tile providers' use 'tile servers' to retrieve tiles from the Internet. On the other hand, 'tile servers' and external sites usually use 'tile providers' to mean 'tile servers'!
Huge thanks to everyone who uses, supports, and/or contributes to flutter_map in any way, you've helped make the most popular non-commercially aimed mapping solution for Flutter!
In particular, thanks go to:
All the current maintainers:
@ibrierley
@JaffaKetchup
@mootw (previously @MooNag)
@TesteurManiak
@josxha
All the previous maintainers:
John P Ryan - the original founder of this project, over at AppTree Software
@kengu
@maRci002
The authors of this documentation:
@JaffaKetchup
We're always happy to receive improvements and fixes, so please submit them whenever you can! A few key points are listed below.
Many feature additions are more suitable for plugins, instead of being added to the core. This is aimed to reduce the future maintenance burden/cost on the maintainers.
If your PR will add a major or breaking change, please discuss it with us first, via the Issue Tracker We don't want to waste your time if we think it's more appropriate for a plugin, and it helps to make a clear plan before starting work
Create a draft PR as soon as work starts, and take it out of draft status when ready for review Keep everyone in the loop, so no-one tries working on the same thing as you
Don't change the package version, GitHub workflows, lints, or any other meta files without clarification We rely on a standardized process and procedure to ensure top-quality releases
It is important to note that 'flutter_map' only supports raster tiles natively. Vector tiles can be used with a community maintained plugin.
This is described in more detail at the bottom of this page.
There are 2 main types of tiles a server can serve: raster and vector; each has their own advantages and drawbacks. This page is designed to help you choose a type for your app, and help you use vector tiles if you choose to.
Raster tiles are the 'older' type of tile, and are raster images (usually .png or .jpg). These tiles are good because they can render quickly and easily, can be viewed without special software, and are readily available from most mapping services. As such, this makes them the popular choice for beginners.
However, raster tiles cannot be easily themed: a theme needs a whole new set of map tiles. This makes apps using light and dark themes have mismatching maps. As well as this, raster tiles usually have larger file sizes meaning slower download times, and they can become blurred/pixelated when viewed at a larger scale: a problem for users when zooming between zoom levels. Another issue is that shapes/text inside tiles cannot be rotated, hence the name 'static tiles': therefore, rotating the map will not rotate the name of a road, for example.
Vector tiles can be considered the 'newer' standard. These images might contain a specialised format (such as .pbf) dictating the mathematics and coordinates used to draw lines and shapes. Because these tiles are drawn at render time instead of at request/server time, theming can be used to make the map fit in better with an app's theme. The math-based image means that the images/tiles can be scaled without any loss of clarity.
However it does add complexity to the rendering process as each element needs to be parsed and painted individually, meaning an impact to performance. Text elements and certain shapes can also be rotated (unlike raster tiles) to match the user's orientation, not the orientation of the map; but calculating this rotation needs to be done every frame, meaning an even larger impact on performance.
Just import the package as you would normally, from the command line:
Commits available from Git (GitHub) may not be stable. Only use this method if you have no other choice.
If you urgently need the latest version, a specific branch, or a specific fork, you can use this method.
Always force usage of the CanvasKit renderer instead of the HTML renderer, even on mobile devices.
The HTML renderer causes performance issues, and may also cause other bugs. Although the CanvasKit renderer does require slightly more Javascript (and therefore a longer download time), it works much better with flutter_map.
flutter_map needs to access the Internet to load tiles, in most cases. On Android, apps must include the INTERNET permission in their manifest. Add the following line to all manifests:
flutter_map needs to access the Internet to load tiles, in most cases. On MacOS, apps must include a dedicated entitlement. Add the following lines to 'macos/Runner/DebugProfile.entitlements' and 'macos/Runner/Release.entitlements':
After installing the package, import it into the necessary files in your project:
You must comply with the appropriate restrictions and terms of service set by your tile server. Failure to do so may lead to any punishment, at the tile server's discretion.
This library and/or the creator(s) are not responsible for any violations you make using this package.
Your advert here!
Support us and get a top advert slot! More information in #apply-to-be-listed.
Your advert here!
More information in #apply-to-be-listed.
Anyone who has contributed to making flutter_map:
Anyone who has made plugins for flutter_map:
Anyone who has donated to flutter_map:
If we deny your PR for this reason, please do consider publishing a plugin, and we'll be happy to add it to the ! See for more information.
Use a clear (preferably ) PR title This makes it easier for us to group commits for release and write correct CHANGELOGs
Due to the complications mentioned above, 'flutter_map' does not natively support vector tiles. However, vector tiles can be used with a to do this.
All users should also to work with coordinates in 'flutter_map'.
In the event that the LatLng
object provided by that library conflicts with another, for example the one provided by Google Maps, you may need to .
First, use , then add the following lines to your pubspec.yaml file, as a root object:
For more information about web renderers, see .
On the web platform, restrictions designed to protect resources on websites and control where they can be loaded from. Some tile servers may not be intended for external consumption, or may be incorrectly configured, which could prevent tiles from loading. If tiles load correctly on platforms other than the web, then this is likely the cause.
The OpenStreetMap Tile Server (as used in this documentation) ToS can be . Other servers may have different terms.
Start by adding some Tile Layer to children
, then configure the map in Options. Additionally, if required, add a MapController
: Controllers & Cameras.
It is recommended to make the map as large as possible, to allow it to display a lot of useful information easily.
As such, we recommend using a depth-based layout (eg. using Stack
s) instead of a flat-based layout (eg. using Column
s). The following 3rd party packages might help with creating a modern design:
If you must restrict the widget's size, you won't find a height
or width
property. Instead, use a SizedBox
or Column
/Row
& Expanded
.
The map widget will expand as much as possible.
To avoid errors about infinite/unspecified dimensions, ensure the map is contained within a constrained widget.
If the map is displayed lazily in something like a PageView
, changing the page and unloading the map will cause it to reset to its initial positioning.
To prevent this, set MapOptions.keepAlive
true
, which will activate an internal AutomaticKeepAliveClientMixin
. This will retain the internal state container in memory, even when it would otherwise be disposed.
flutter_map provides an example application showcasing much of its functionality. In some cases, the example app contains undocumented functionality, so it's definitely worth checking out!
Note that the web demo is built automatically from the 'master' branch, so may not reflect the the latest release on pub.dev.
Please don't abuse the web demo! It runs on limited bandwidth and won't hold up to thousands of loads.
If you're going to be straining the application, please see #prebuilt-artifacts, and serve the application yourself.
If you can't build from source for your platform, our GitHub Actions CI system compiles the example app to GitHub Artifacts for Windows, Web, and Android.
The Windows and Android artifacts just require unzipping and installing the .exe or .apk found inside.
The Web artifact requires unzipping and serving, as it contains more than one unbundled file. You may be able to use dhttpd for this purpose.
Note that these artifacts are built automatically from the 'master' branch, so may not reflect the the latest release on pub.dev.
If you need to use the example app on another platform, you can build from source, using the 'example' directory of the repository.
To dictate & restrict what the map can and should do, regardless of its contents, it needs some guidance!
It provides options that can be categorized into three main parts:
Initial positioning Defines the location of the map when it is first loaded
Permanent rules Defines restrictions that last throughout the map's lifetime
Event handling Defines methods that are called on specific map events
Changing these properties after the map has already been built for the first time will have no effect: they only apply on initialisation.
To control the map programatically, use a MapController
: Controllers & Cameras.
One part of MapOptions
responsibilities is to define how the map should be positioned when first loaded. There's two ways to do this (that are incompatible):
initialCenter
(LatLng
) & initialZoom
initialCameraFit
by bounds (): CameraFit.bounds
by bounds (): CameraFit.insideBounds
by coordinates (): CameraFit.coordinates
It is possible to also set the map's initialRotation
in degrees, if you don't want it North (0°) facing initially.
If rotation is enabled/allowed, if using initialCameraFit
, prefer defining it by coordinates for a more intended/tight fit.
One part of MapOptions
responsibilities is to define the restrictions and limitations of the map and what users can/cannot do with it.
Some of the options are described elsewhere in this documentation, in context. In addition, the API docs show all the available options, and below is a partial list of options:
cameraConstraint
camera bounds inside bounds: CameraConstraint.bounds
camera center inside bounds: CameraConstraint.center
unconstrained (default): CameraConstraint.unconstrained
maxZoom
and minZoom
Sets a hard limit on the maximum and minimum amounts that the map can be zoomed
interactionOptions
Configures the gestures that the user can use to interact with the map - for example, disable rotation or configure cursor/keyboard rotation
Instead of maxZoom
(or in addition to), consider setting maxNativeZoom
per TileLayer
instead, to allow tiles to scale (and lose quality) on the final zoom level, instead of setting a hard limit.
This update has renewed two of the oldest surviving sections of 'flutter_map' (state/MapController
and TileProvider
s), fixed bugs, and added features!
This is significant progress in our aim to renew the project and bring it up to date. In the long run, this will bring it inline with up-to-date Flutter good practises and techniques, improve its performance and stability, and reduce the maintenance burden.
There are major breaking changes for all users, as well as some things users should check and possibly change.
Some changes have deprecations and messages, some do not. Please refer to the sections below for information on how to migrate your project, as well as in-code documentation and deprecation messages, if your migration is not listed below. Some changes are omitted if they are deemed unlikely to affect implementations.
There's loads of changes in this release, which will improve performance and reduce costs! Check out these highlights, along with the full changelog:
We've added in-memory caching to the underlying custom ImageProvider
. This means that they do not need to be re-requested if they are pruned then re-loaded, reducing tile loading times, and reduce tile requests and costs!
No action is needed to benefit from this.
If you're developing an app for the web, there's an exciting new performance boost available. By aborting in-flight HTTP requests if tiles are pruned before they are fully-loaded, connections can be freed up, reducing tile loading times, and potentially saving you money!
There are also advantages for other platforms, although they may not be quite as visible.
Manual action is required to benefit from this. See #cancellablenetworktileprovider for more information.
Rotation is now supported on desktop! Simply use the CTRL (or equivalent) keyboard key (customizable in MapOptions
) and mouse.
We've added some warning & recommendation logs in-code, that will trigger under certain circumstances. If they trigger, make sure to listen to them to benefit from performance and efficiency improvements!
It is possible to add more than one TileLayer
! Transparency in one layer will reveal the layers underneath.
Each layer is isolated from the other layers, and so handles its own independent logic and handling. However, they can access and modify the internal state of the map, as well as respond to changes.
Most layers are 'mobile', such as the TileLayer
. These use a MobileLayerTransformer
widget internally, which enables the layer to properly move and rotate with the map's current camera.
Both of these layer types are defined in the same children
list. Most of the time, static layers go atop mobile layers, so should be at the end of the list.
flutter_map makes use of InheritedModel
to share 3 'aspects' with its built children:
MapController
: use to programatically control the map camera & access some helper functionality - control camera
MapCamera
: use to read the current state/position of the map camera & access some helper functionality that depends on the camera (such as latlngToPoint
) - read camera
MapOptions
is also an aspect, which reflects the MapOptions
defined on the FlutterMap.options
parameter.
All 3 aspects can be retrieved from within the context of a FlutterMap
, which all built descendants should have access to. This usually means from within a layer: anywhere where there is at least one 'visible' builder method between the FlutterMap
and the invocation.
Use the static of
(or null-safe maybeOf
) method to access the inherited aspect. For example, to access the MapCamera
:
Using this method directly in the children
list (not inside another widget), and in any MapOptions
callback, is not possible: there is no* builder method between the FlutterMap
and the children
or callback.
MapCamera
Then use the camera
getter on the MapController
instance.
Avoid using this method to access the camera when MapCamera.of()
is available.
MapController
For more information about correctly setting up an external(ly accessible) MapController
, see:
MapOptions
It is not possible to access the MapOptions
in this way outside of FlutterMap
descendants.
This is because it is not changed by FlutterMap
, and so that would be unnecessary.
The InteractionOptions
object passed to MapOptions.interactiveOptions
configures the gestures that the user can use to interact with the map. For example, disable rotation or configure cursor/keyboard rotation.
Note that some gestures must be configured by other means, either instead of using flags, or in addition to.
By default, all
gestures are enabled, but a non-interactive map can be created using none
(and other options in addition).
The recommended way to create an entirely non-interactive map is to wrap the FlutterMap
widget in an IgnorePointer
widget.
Otherwise, to set flags, there's two methods:
Add flags, with the bitwise 'OR' (|
) operator in-between
For example, InteractiveFlag.drag | InteractiveFlag.rotate
Remove flags from all
, using the &
and ~
operators in-between
For example, InteractiveFlag.all & ~InteractiveFlag.rotate
Cursor/keyboard rotation is designed for desktop platforms, and allows the cursor to be used to set the rotation of the map whilst a (customizable) keyboard key (by default, any of the 'Control' keys) is held down.
The CursorKeyboardRotationOptions
object passed to the property with the corresponding name configures this behaviour. The CursorKeyboardRotationOptions.disabled()
constructor can be used to disable cursor/keyboard rotation.
There's many customization options, see the API docs for more information:
This is advanced behaviour that affects how gestures 'win' in the gesture arena, and does not usually need changing.
There's two ways to interact with the map - that is to control it, as well as receive data from it - and it's current viewport, aka. 'camera'.
The first way is through user interaction, where they perform gestures (such as drags/pans), and the map reacts automatically to those gestures to change the camera view of the map.
However, the map camera can also be controlled by calling methods on a controller, and its state read by getting values from an exposed camera.
Changing the state of MapOptions.initial*
will not update the map camera. It may only be updated through a MapController
.
For more information, see:
To display anything on the map, you'll need to include at least one layer. This is usually a , which displays the map tiles themselves: without it, the map isn't really a very good map!
To insert a layer, add it to the children
property. Other layers (sometimes referred to as 'feature layers', as they are map features) can then be stacked on top, where the last widget in the children
list is topmost. For example, you might display a , or any widget as your own custom layer ()!
To display a widget in a sized and positioned box, similar to , try the community maintained !
However, some layers are 'static', such as the s. These aren't designed to move nor rotate with the map, and usually make use of a widget like Align
and/or SizedBox.expand
to achieve this.
However, it is mostly irrelevant, except for when .
This will attach the widget to the state of the map, causing it to rebuild whenever the depended-on aspects change. See for more information.
Instead, follow , or, wrap the necessary layers with a Builder
widget.
For example, the code snippet below hides a TileLayer
when above zoom level 13:
To access the MapCamera
outside of a FlutterMap
descendant, first .
flags
is a that enables and disables the vast majority of gestures. Although technically the type is of int
, it is usually set with a combination of InteractiveFlag
s.
These are usually restricted by . It is possible to disable all input, either by disabling all gestures, or by wrapping the map with something like IgnorePointer
.
You must comply with the appropriate restrictions and terms of service set by your tile server. Always read the ToS before using a tile server. Failure to do so may lead to any punishment, at the tile server's discretion.
This library and/or the creator(s) are not responsible for any violations you make using this package.
The OpenStreetMap Tile Server (as used below) ToS can be found here. Other servers may have different terms.
The basis of any map is a TileLayer
, which displays square raster images in a continuous grid, sourced from the Internet or a local file system.
flutter_map supports WMS Usage, but most map tiles are accessed through Slippy Map/CARTO/XYZ URLs, as described here.
Although setting up a basic tile layer couldn't be simpler, it helps to spend a little bit more time fine-tuning it! We recommend covering this list at least, for every tile layer.
#url-template Choose a suitable tile server for your app
#useragentpackagename
Always set userAgentPackageName
, even though it is technically optional
#subdomains Consider whether you should set subdomains for your template URL
#retina-mode
If your tile server supports retina tiles natively, set up the retinaMode
property
#cancellablenetworktileprovider
Especially on web, consider using this more advanced TileProvider
to improve performance
maxNativeZoom
Set the maximum zoom level that the tile server supports to prevent flutter_map from trying to exceed this (especially when not set appropriately in MapOptions.maxZoom
)
This parameter must be specified unless wmsOptions
is specified.
The URL template is a string that contains placeholders, which, when filled in, create a URL/URI to a specific tile.
Specifically, flutter_map supports the Slippy Map format, sometimes referred to as CARTO or Raster XYZ. Tiles are referred to by their zoom level, and position on the X & Y axis. For more information, read How Does It Work?.
These templates are usually documented by your tile server, and will always include the following placeholders:
{x}
: x axis coordinate
{y}
: y axis coordinate
{z}
: zoom level
Sometimes, they also include:
{s}
: #subdomains
{r}
: #retina-mode
Additional placeholders can also be added freely to the template, and are filled in with the specified values in additionalOptions
. This can be used to easier add switchable styles or access tokens, for example.
Some tile servers provide mirrors/redirects of the main tile server on/via subdomains, such as 'a', 'b', 'c'.
These were necessary to bypass browsers' limitations on simultaneous HTTP connections, thus increasing the number of tiles that can load at once.
To use subdomains, add the {s}
placeholder, and specify the available subdomains in TileLayer.subdomains
. flutter_map will then fill the placeholder with one of these values based on internal logic.
Subdomains are now usually considered redundant due to the usage of HTTP/2 & HTTP/3 which don't have the same restrictions.
Usage of subdomains will also hinder Flutter's ability to cache tiles, potentially leading to increased tile requests and costs.
If the server supports HTTP/2 or HTTP/3 (how to check), avoid using subdomains.
Retina mode improves the resolution of map tiles, an effect particularly visible on high density (aka. retina) displays.
Raster map tiles can look especially pixelated on retina displays, so some servers support high-resolution "@2x" tiles, which are tiles at twice the resolution of normal tiles.
Where the display is high density, and the server supports retina tiles - usually indicated by an {r}
placeholder in the URL template - it is recommended to enable retina mode.
Therefore, where {r}
is available, it is recommended to call the method RetinaMode.isHighDensity
with the current BuildContext
, and pass the result to TileLayer.retinaMode
. This will enable retina mode on retina displays by filling the {r}
placeholder with "@2x".
It is also possible to emulate retina mode, even when the server does not natively support it. If retinaMode
is true
, and no {r}
placeholder is present, flutter_map will emulate it by requesting four tiles at a larger zoom level and combining them together in place of one.
Emulating retina mode has multiple negative effects:
it increases tile requests
it likely causes text/labels and POI markers embedded in the tiles to become smaller and unreadable
it decreases the effective maximum zoom by 1
Therefore, carefully consider whether emulating retina mode is appropriate for your application, and disable it if necessary. Always prefer native retina tiles if they are available.
It's also possible to specify a fallbackUrl
template, used if fetching a tile from the primary urlTemplate
fails (which has the same format as this).
Specifying a fallbackUrl
does have negative effects on performance and efficiency. Avoid specifying fallbackUrl
unless necessary.
See in-code documentation and Tile Providers for more information.
Some TileProvider
s may not support/provide any functionality for fallbackUrl
template.
userAgentPackageName
Although it is programatically optional, always specify the userAgentPackageName
argument to avoid being blocked by your tile server.
This parameter should be passed the application's package name, such as 'com.example.app'. This is important to avoid blocking by tile servers due to high-levels of unidentified traffic. If no value is passed, it defaults to 'unknown'.
This is then formatted into a 'User-Agent' header, and appended to the TileProvider
's headers
map, if it is not already present.
This is ignored on the web, where the 'User-Agent' header cannot be changed due to a limitation of Dart/browsers.
If a large proportion of your users use the web platform, it is preferable to use CancellableNetworkTileProvider
, instead of the default NetworkTileProvider
. It may also be beneficial to use this tile provider on other platforms as well.
See #cancellablenetworktileprovider for more information.
Need more control over how the URL template is interpreted and/or tiles are fetched? You'll need to change the TileProvider
.
TileUpdateTransformer
(s
) is a power-user feature. Most applications won't require it.
A TileUpdateTransformer
restricts and limits TileUpdateEvent
s (which are emitted 'by' MapEvent
s), which cause tiles to update.
For example, a transformer can delay (throttle or debounce) updates through one of the built-in transformers, or pause updates during an animation, or force updates even when a MapEvent
wasn't emitted.
For more information, see:
When the state of a MapCamera
changes, because of an update to its position or zoom, for example, a MapEvent
, which can be handled by you.
Specifying a callback method in MapOptions.onMapEvent
If only a couple of events need to be caught, such as just an onTap
handler, it is possible to avoid handling the raw Stream
of MapEvent
s. Instead, MapOptions
has callbacks available for the following events:
onTap
onLongPress
onPositionChanged
onPointerDown
/onPointerUp
/onPointerHover
/onPointerCancel
The MapEventTap
event may be emitted (or the onTap
callback called) 250ms after the actual tap occurred, as this is the acceptable delay between the two taps in a double tap zoom gesture.
This disables the double tap handler, so the MapEventTap
is emitted 'instantly' on tap.
For more information about what a MapController
is, and when it is necessary to set one up in this way, see:
The FlutterMap.controller
parameter takes an externally intialised MapController
instance, and attaches it to the map.
An externally attached controller will be accurately reflected when depending on the MapController
aspect.
It is not safe to assume that the MapController
is ready to use as soon as an instance has been initialised, for example within initState
.
See below for more information.
initState
)It is not safe to assume that the MapController
is ready to use as soon as an instance has been initialised, for example within initState
.
It must first be attached to the FlutterMap
, which could take up to multiple frames to occur (similar to the way a ScrollController
is attached to a scrollable view). It helps to avoid errors by thinking of the controller in this way.
Use of the MapController
before it has been attached to a FlutterMap
will result in an error being thrown, usually a LateInitialisationError
.
It is usually safe to use a controller from within a callback manually initiated by the user without further complications.
Instead, use the MapOptions.onMapReady
callback. At this point, it is guaranteed that the controller will have been attached. You could also use this method to complete a Completer
(and await
its Future
elsewhere) if you need to use it elsewhere.
Don't define/intialise the a MapController
within a class or widget that doesn't also contain the FlutterMap
, such as a state model (eg. ChangeNotifier
), then try to use it by querying the state in the FlutterMap.controller
parameter.
Instead, some extra care should be taken, which may feel a little backwards at first. The state model should be used AFTER the normal setup.
In your state model, create a nullable (and initially uninitialised) MapController
containing field
It may then be beneficial to unset the state model field when the controller is disposed: it should be disposed when the FlutterMap
is disposed, which should occur just before the widget building the FlutterMap
is disposed. Therefore, you can override the dispose
method.
flutter_map supports WMS tile servers through WMSTileLayerOptions
- wmsOptions
in TileLayer
s.
For usage, please refer to the Full API Reference, and the examples in the example app.
Omit urlTemplate
if using WMS tiles. The template is now specified in the baseUrl
property of WMSTileLayerOptions
.
This has a default of NetworkTileProvider
which gets tiles from the internet through a dedicated image provider.
There's two situations in which you'll need to change the tile provider:
These tile providers use the urlTemplate
to get the appropriate tile from the a network, usually the World Wide Web.
The underlying custom ImageProvider
s will cache tiles in memory, so that they do not require another request to the tile server if they are pruned then re-loaded. This should result in them being loaded quicker, as well as enabling already loaded tiles to appear even without Internet connection (at least in the same session).
Specifying any fallbackUrl
(even if it is not used) in the TileLayer
will prevent loaded tiles from being cached in memory.
This is to avoid issues where the urlTemplate
is flaky (sometimes works, sometimes doesn't), to prevent potentially different tilesets being displayed at the same time.
NetworkTileProvider
This is the default tile provider, and does nothing particularly special. It takes two arguments, but you'll usually never need to specify them:
httpClient
: BaseClient
By default, a RetryClient
backed by a standard Client
is used
This plugin is part of the official 'flutter_map' organisation, and maintained by the same maintainers.
Tiles that are removed/pruned before they are fully loaded do not need to complete (down)loading, and therefore do not need to complete the HTTP interaction. Cancelling these unnecessary tile requests early could:
Reduce tile loading durations (particularly on the web)
Reduce users' (cellular) data and cache space consumption
Reduce costly tile requests to tile servers*
Improve performance by reducing CPU and IO work
Although HTTP request abortion is supported on all platforms, it is especially useful on the web - and therefore recommended for web apps. This is because the web platform has a limited number of simulatous HTTP requests, and so closing the requests allows new requests to be made for new tiles. On other platforms, the other benefits may still occur, but may not be as visible as on the web.
These tile providers use the urlTemplate
to get the appropriate tile from the asset store of the application, or from a file on the users device, respectively.
Specifying any fallbackUrl
(even if it is not used) in the TileLayer
will reduce the performance of these providers.
AssetTileProvider
This tile providers uses the templateUrl
to get the appropriate tile from the asset store of the application.
Asset management in Flutter leaves a lot to be desired! Unfortunately, every single sub-directory (to the level of tiles) must be listed.
FileTileProvider
This tile providers uses the templateUrl
to get the appropriate tile from the a path/directory/file on the user's device - either internal application storage or external storage.
On the web, FileTileProvider()
will throw an UnsupportedError
when a tile request is attempted, due to the lack of the web platform's access to the local filesystem.
To cause a widget inside FlutterMap
's context to rebuild when an changes, see .
There's two methods to catch all emitted MapEvent
s. These methods expose the raw MapEvent
, and is recommended in cases where multiple events need to be caught, or there's no more specific callback method available in MapOptions
(see ).
Listening to a 's mapEventStream
, which exposes events via a Stream
onMapReady
Primarily used for advanced MapController
If this causes noticeable jank or a bad experience (for example, on desktop platforms), disable .doubleTapZoom
:
For example, it is sometimes necessary to use a controller in the initState()
method (for example to ). However, because this method executes before the widget has been built, a controller defined here will not be ready for use.
MapController
methods that change the position of the map should not be used directly (not as a result of another callback) in onMapReady
- see . This is an unsupported and usually unnecessary usecase.
Setup the controller as in , where the MapController
is defined & initialised directly adjacent to the FlutterMap
Follow to setup an onMapReady
callback. Then within this callback, set the state model field.
Whilst animated movements through MapController
s aren't built-in, the provides this, and much more!
The tileProvider
parameter in TileLayer
takes a TileProvider
object specifying a to use for that layer.
Sourcing tiles from the filesystem or asset store:
Using a that instructs you to do so ()
headers
: Map<String, String>
By default, only headers sent by the platform are included with each request, plus an overridden (where possible) 'User-Agent' header based on the property
This requires the '' plugin to be installed.
This provider uses '', which supports aborting unnecessary HTTP requests in-flight, after they have already been sent.
Once HTTP request abortion is , NetworkTileProvider
will be updated to take advantage of it, replacing and deprecating this provider. This tile provider is currently a separate package and not the default due to the reliance on the additional Dio dependency.
It will cause with AssetTileProvider
, and will cause main thread blocking when requesting tiles from FileTileProvider
.
If you know you are running on the web platform, use a or a custom tile provider.
You can add single point features - such as pins, labels, or markers - to maps using MarkerLayer
and Marker
s.
No more image only markers! Unlike other popular mapping libraries, we allow usage of any widget as the marker.
Excessive use of markers may create performance issues.
Consider using a clustering plugin to merge nearby markers together, reducing the work that needs to be done when rendering: #marker-clustering.
The marker widget will be centered over the geographic coordinate by default. However, this can be changed with the alignment
argument, which aligns the widget relative to the point.
The center of rotation when rotate
is true
will be the point.
The default alignment for all Marker
s within a MarkerLayer
can be set by changing the same property on the MarkerLayer
.
It is possible to enable the Marker
to automatically counter-rotate to the camera's rotation, to ensure it remains facing upwards, via the rotate
argument.
The default alignment for all Marker
s within a MarkerLayer
can be set by changing the same property on the MarkerLayer
.
There is no built-in support to rotate markers to a specific degree. However, this is easy to implement through a rotating widget, such as Transform.rotate
.
There is no built-in support to handle gestures on Marker
s, such as taps. However, this is easy to implement using a standard GestureDetector
.
You can add areas/shapes to maps by making them out of individual coordinates using PolygonLayer
and Polygon
s.
PolygonLayer
s and Polygons
s support hit detection and interactivity.
flutter_map includes many performance optimizations built in, especially as of v7. Some are enabled by default, but may be only 'weakly' applied, and others must be enabled manually. There are also some other actions that can be taken externally to improve performance
The following list is ordered from least 'intrusive'/extreme, to most intrusive. Remember to consider the potential risks of enabling an optimization before doing so.
The example application includes a stress test which loads multiple Polygon
s from a GeoJson file, with a total of 138,000 points.
Overlapping colors that are not completely opaque will not recieve the 'darkening'/layering effect - the overlapping area will just be the single colour.
On layers with (many) only small polygons (those with few points), disabling simplification may yield better performance.
Polygons may overlap after simplification when they did not before, and vice versa.
Self-intersecting (complex) Polygon
s are not supported by the triangulation algorithm, and could cause errors.
The Shamos-Hoey algorithm could be used to automatically detect self-intersections, and set the feature-level flag correspondingly. If doing this, remember that the simplification step (which runs prior to this) could either add or remove a self-intersection.
Holes are supported.
This pathway may be slower than the standard pathway, especially when used on a large scale but with simplification disabled, or used on an especially small scale.
It is intended for use when prior profiling indicates more performance is required after other methods are already in use.
Rarely, some visible artefacts may be introduced by the triangulation algorithm.
'flutter_map' doesn't provide any public methods to manipulate polygons, as these would be deemed out of scope.
However, some useful methods can be found in libraries such as 'latlong2' and 'poly_bool_dart'. These can be applied to the input of Polygon
's points
argument, and the map will do it's best to try to render them. However, more complex polygons - such as those with holes - may be painted inaccurately, and may therefore require manual adjustment (of holePointsList
, for example).
You can add lines to maps by making them out of individual coordinates using PolylineLayer
and Polyline
s.
Polyline
s support a solid
, dotted
, and dashed
style, through PolylinePattern
/Polyline.pattern
. These are flexible, and spacing and sizing may be customized.
dotted
and dashed
patterns should 'fit' the Polyline
they are applied to, otherwise the final point in that line may not be visually clear. The fit can be adjusted when defining the pattern through the PatternFit
enum.
PolylineLayer
s and Polyline
s support hit detection and interactivity.
If any polylines are very thin, it is recommended for accessibility reasons to increase the size of the 'hitbox' (the area where a hit is detected on a polyline) to larger than the line itself in order to make it easier to tap/interact with the polyline.
The minimumHitbox
argument adjusts the minimum size of the hitbox - the size of the hitbox will usually be the entire visual area/thickness of the polyline (and border), but will be no less than this value. It defaults to 10.
The example application includes a stress test which generates a Polyline
with 200,000 points.
To improve performance, line segments that are entirely offscreen are effectively removed - they are not processed or painted/rendered. This is enabled by default and disabling it is not recommended.
Polylines that are particularly wide (due to their strokeWidth
/borderStrokeWidth
may be improperly culled if using the default configuration. This is because culling decisions are made on the 'infinitely thin line' joining the points
, not the visible line, for performance reasons.
Therefore, the cullingMargin
parameter is provided, which effectively increases the distance a segement needs to be from the viewport edges before it can be culled. Increase this value from its default if line segements are visibly disappearing unexpectedly.
Culling cannot be applied to polylines with a gradient fill, as this would cause inconsistent gradients.
These will be automatically internally excluded from culling: it is not necessary to disable it layer-wide - unless all polylines have gradient fills, in which case that may yield better performance.
Avoid using these if performance is of importance. Instead, try using multiple polylines with coarser color differences.
Simplification algorithms reduce the number of points in each line by removing unnecessary points that are 'too close' to other points which create tiny line segements invisible to the eye. This reduces the number of draw calls and strain on the raster/render thread. This should have minimal negative visual impact (high quality), but should drastically improve performance.
For this reason, polylines can be more simplified at lower zoom levels (more zoomed out) and less simplified at higher zoom levels (more zoomed in), where the effect of culling on performance improves and trades-off. This is done by scaling the
simplificationTolerance
parameter (see below) automatically internally based on the zoom level.
To adjust the quality and performance of the simplification, the maximum distance between removable points can be adjusted through the simplificationTolerance
parameter. Increasing this value (from its default of 0.4) results in a more jagged, less accurate (lower quality) simplification, with improved performance; and vice versa. Many applications use a value in the range 0 - 1.
To disable simplification, set simplificationTolerance
to 0.
On layers with (many) only 'short' polylines (those with few points), disabling simplification may yield better performance.
Routing is out of scope for 'flutter_map'. However, if you can get a list of coordinates from a 3rd party, then you can use polylines to show them!
You may have a polyline with 'Google Polyline Encoding' (which is a lossy compression algorithm to convert coordinates into a string and back). These are often returned from routing engines, for example. In this case, you'll need to decode the polyline to the correct format first, before you can use it in a Polyline
's points
argument.
You can then use the package and the above snippet by doing:
You can add circle areas to maps by making them out of a center coordinate and radius using CircleLayer
and CircleMarker
s.
Excessive use of circles may create performance issues.
To improve performance, polylines are 'simplified' before being culled and painted/rendered. The well-known is used to perform this, and is enabled by default.
Good open source options that can be self-hosted include (which includes a public demo server) and . You can also use a commercial solution such as Mapbox or Google Maps - these can often give more accurate/preferable results because they can use their traffic data when routing.
One way to accomplish this is to use another Flutter library called , together with a custom method. You can use the code snippet below, which can just be pasted into a file and imported whenever needed:
Layer interactivity is different to map interactivity. See Interaction Options to control map interactivity.
For information about how hit testing behaves in flutter_map, see Hit Testing Behaviour.
It is important to note that hit testing != interactivity, and hit testing is always executed on interactable layers by default.
The following layers are interactable - they have specialised hitTest
ers and support external hit detection:
These all follow roughly the same pattern to setup hit detection/interactivity, and there's three or four easy steps to setting it up.
Direct callbacks, such as onTap,
aren't provided on layers or individual elements, to maximize flexibility.
Pass a LayerHitNotifier
to the hitNotifier
parameter of the layer. The LayerHitNotifier
should be created as a ValueNotifier
defaulting to null
, but strongly typed to LayerHitNotifier
.
This notifier will be notified whenever a hit test occurs on the layer, with a LayerHitResult
when a feature within the layer is hit, and with null
when a feature is not hit (but the layer is).
It is possible to listen to the notifier directly with addListener
, if you want to handle all hit events (including, for example, hover events).
However, most use cases just need to handle particular gestures (such as taps). This can be done with a wrapper widget to 'filter' the events appropriately: #id-3.-gesture-detection.
hitValue
To ElementsTo identify which particular element (such as a Polyline
or Polygon
) was hit (which will be useful when handling the hit events in later steps), supported elements have a hitValue
property.
This can be set to any object, but if one layer contains all the same type, type casting can be avoided (if the type is also specified in the LayerHitNotifier
's type argument).
The equality of the element depends on the equality of the hitValue
.
Therefore, any object passed to the hitValue
should have a valid and useful equality method.
Objects such as records do this behind the scenes, and can be a good choice to store small amounts of uncomplicated data alongside the element.
To only handle certain hits based on the type of gesture the user performed (such as a tap), wrap the layer with a gesture/hit responsive widget, such as GestureDetector
or MouseRegion
.
These widgets are smart enough to delegate whether they detect a hit (and therefore whether they can detect a gesture) to the child - although HitTestBehavior.deferToChild
may be required for some widgets to enable this functionality.
This means the layer can report whether it had any form of hit, and the handler widget can detect whether the gesture performed on it actually triggered a hit on the layer below.
Once a LayerHitResult
object is obtained, through the hit notifier, you can retrieve:
hitValues
: the hitValue
s of all elements that were hit, ordered by their corresponding feature, first-to-last, visually top-to-bottom
coordinate
: the geographic coordinate of the hit location (which may not lie on any feature)
point
: the screen point of the hit location
If all the hitValue
s in a layer are of the same type, and the created hit notifier specifies that type in the type argument, typing is preserved all the way to retrieval.
Because the HitNotifier
is a special type of ValueNotifier
, it can be both listened to (like a Stream
), and its value instantly retrieved (like a normal variable).
Therefore, there are two ways to retrieve a LayerHitResult
(or null
) from the notifier:
Using .value
to instantly retrieve the value
This is usually done within a gesture handler, such as GestureDetector.onTap
, as demonstrated below.
Adding a listener (.addListener
) to retrieve all hit results
This is useful where you want to apply some custom/advanced filtering to the values, and is not a typical usecase.
The behaviour of hit testing can be confusing at first. These rules define how hit testing usually behaves:
Gesture callbacks in MapOptions
are always invoked, no matter what is within the layers or the result of hitTest
s in those layers, with the exception of custom defined hit test behaviours (not those layers that support interactivity, see Layer Interactivity), such as applying GestureDetector
s around Marker.child
ren
GestureDetector
s absorb hit tests, and so corresponding callbacks in MapOptions
will not be invoked if they are defined/invoked in the GestureDetector
.
Workarounds to resolve this are discussed below.
Hit testing is always* performed on the interactable layers (see Layer Interactivity) even if they have not been set-up for interactivity: hit testing != interactivity
Non-interactable layers (such as Overlay Image Layer) have no defined hitTest
, and behaviour is situation dependent
A successful hit test (true
) from an interactable layer will prevent hit testing on layers below it in the children
stack
To change this behviour, make use of these three widgets, wrapping them around layers when and as necessary:
TransulcentPointer
: a general purpose 'widget' included with flutter_map that allows the child to hit test as normal, but also allows widgets beneath it to hit test as normal, both seperately
For example, a marker with a GestureDetector
child that detects taps beneath a Polyline
will not detect a tap, no matter if the PolylineLayer
has a defined hitNotifier
or the Polyline
has a defined hitValue
. A defined onTap
callback in MapOptions
would be called however. If the Marker
were no longer obscured by the Polyline
, it's onTap
callback would be fired instead of the one defined in MapOptions
.
However, this behaviour could be changed by wrapping the PolylineLayer
with a TranslucentPointer
. This would allow interacitivity to function as normal, but also allow the Marker
beneath to have it's onTap
callback fired. Further wrapping another TransclucentPointer
around the MarkerLayer
would allow all 3 detections to function.
You can overlay images on the map (for example, town or floor plans) using OverlayImageLayer
and OverlayImage
s or RotatedOverlayImage
s.
There have been issues in the past where these images failed to appear properly, sometimes not showing up at all, sometimes showing up malformed or corrupted.
If this issue occurs to you, and you're using Impeller, try disabling Impeller at launch/build time to see if the issue rectifies itself. If it does, this is an Impeller issue, and should be reported to the Flutter team.
RotatedOverlayImage
supports rotation and parallelogram skewing, by accepting 3 points instead of 2.
To calculate a rotation without skewing, given a center and a 3rd corner, see https://stackoverflow.com/a/78064659/11846040.
Before publishing your app to users, you should credit any sources you use, according to their Terms of Service.
There are two built in methods to provide attribution, RichAttributionWidget
and SimpleAttributionWidget
, but you can also build your own using a simple Align
widget.
You must comply with the appropriate restrictions and terms of service set by your tile server. Failure to do so may lead to any punishment, at the tile server's discretion.
This library and/or the creator(s) are not responsible for any violations you make using this package.
The OpenStreetMap Tile Server (as used above) ToS can be found here. Other servers may have different terms.
Please consider crediting flutter_map. It helps us to gain more awareness, which helps make this project better for everyone!
RichAttributionWidget
An animated, interactive attribution layer that supports both logos/images (displayed permanently) and text (displayed in a popup controlled by an icon button adjacent to the logos).
It is heavily customizable (in both animation and contents), and designed to easily meet the needs of most ToSs out of the box.
For more information about configuration and all the many options this supports, see the in-code API documentation.
SimpleAttributionWidget
We also provide a more 'classic' styled box, similar to those found on many web maps. These are less customizable, but might be preferred over RichAttributionWidget
for maps with limited interactivity.
'flutter_map' is in no way associated or related with Stadia Maps.
To display their map tiles, Stadia Maps usually provides a 'Static Maps Base URL' for map styles. However, to integrate with 3rd-party APIs, they also provide a 'Raster XYZ PNGs URL' , and tiles requested through this endpoint consume 'Styled Raster Map Tiles' credits. This URL needs no extra configuration to integrate with flutter_map.
Retina tiles (high-DPI) tiles are available. Use the URLs containing '@2x' instead of '{r}'. The maximum zoom level that Stadia Maps supports is 20, so it is recommended to set maxNativeZoom
or maxZoom
as such.
However, please note that this method of integration is still experimental. Many of the Stadia Maps styles utilize advanced features of the Mapbox GL JSON style language which are not yet well-supported.
'flutter_map' is in no way associated or related with Mapbox.
To display their map tiles, Mapbox usually provides a 'Style URL' for map styles. However, to integrate with 3rd-party APIs, they also provide a 'CARTO Integration URL', and tiles requested through this endpoint consume the 'Static Tiles API' quota. This URL needs no extra configuration to integrate with flutter_map.
The maximum zoom level that Mapbox supports is 22, so it is recommended to set maxNativeZoom
or maxZoom
as such.
Mapbox supports creating and using custom styled maps through Studio.
Create a custom style using the editor
Click "Share...", or the share icon
Choose between Draft or Production
Scroll to the bottom of the dialog, and choose Third Party
Select "CARTO" from the dropdown menu
Click the copy button to copy the template URL
The URL includes an '@2x' string, which forces usage of high-definition tiles on all displays, without extra setup.
This URL should be used as above, although you may need to insert the placeholders manually.
'flutter_map' is in no way associated or related with Thunderforest (or Gravitystorm Limited).
Thunderforest is a popular tiered-payment (with free tier) tile provider solution, especially for generic mapping applications. Note that using 'flutter_map' uses up your 'Map Tiles API' requests.
Stadia Maps' home page: Stadia Maps' pricing page: Stadia Maps' documentation page:
Attribution is required, see .
Consider using the or s, which meet the requirements.
Stadia Maps offers a variety of ready-made map styles that don't require customization. URLs are found with the style: see the available . The URL should be used as above.
Stadia Maps' also provides vector tiles. For more information about using vector tiles, please see .
Mapbox's Maps home page: Mapbox's Maps pricing page: Mapbox's Maps documentation:
Attribution is required, see .
Consider using the , which meets the requirements by supporting both logo and text attribution.
Should you need to let flutter_map interfere, and only use retina tiles on retina/high-density displays, replace it with the '{r}' placeholder, then see for more information.
Mapbox offers a variety of ready-made map styles that don't require customization. An example URL can be found in .
Thunderstorm's home page: Thunderstorm's pricing page: Thunderstorm's documentation page:
'flutter_map' is in no way associated or related with Bing Maps.
Bing Maps' home page: microsoft.com/maps
To display map tiles from Bing Maps, a little more effort is needed, as they use a RESTful API with quadkeys, rather than the standard slippy map system.
Luckily, we've constructed all the code you should need below! Feel free to copy and paste into your projects.
Thanks to Luka Glušica for discovering the basic initial implementation.
Attribution is not demonstrated here, but may be required. Ensure you comply with Bing Maps' ToS.
There are plenty of other tile servers you can choose from, free or paid. Most provide a static tile service/API, usually called Static Tiles or just Tile Requests (if no vector tiles are supported).
If you're responsible for a tile server, and want to have your tile server and setup instructions listed in this documentation, please get in touch!
A good catalogue of servers (usually called Providers elsewhere) can be found at the websites below:
Google Maps does not document a static raster tile server. Therefore, flutter_map is unable to show Google Maps.
There is an undocumented endpoint available, however it violates the Google Maps Platform ToS.
Switch2OSM also provides detailed instructions on how to serve your own tiles: this can be surprisingly economical and enjoyable if you don't mind a few hours in a Linux console.
However, this will require a very high-spec computer, especially for larger areas, and hosting this might be more complicated than it's worth. It's very difficult to fully understand the technologies involved.
Using maps without an Internet connection is common requirement. Luckily, there are a few options available to you to implement offline mapping in your app.
flutter_map only provides caching in-memory. All cached tiles will be cleared after the app session is ended.
You must comply with the appropriate restrictions and terms of service set by your tile server. Always read the ToS before using a tile server. Failure to do so may lead to any punishment, at the tile server's discretion. Some tile servers may require longer-term caching to be implemented.
Caching is used usually to improve user experience by reducing network waiting times, not necessarily to prepare for no-Internet situations. Caching can be more temporary (eg. in-memory/session-only, where the cache is cleared after the app is closed), or more long-term (eg. app cache, where the OS takes responsibility for clearing the app cache when necessary/when requested).
There's 3 methods that basic caching can be implemented in your app, two of which rely on community maintained plugins:
You must comply with the appropriate restrictions and terms of service set by your tile server. Always read the ToS before using a tile server. Failure to do so may lead to any punishment, at the tile server's discretion. Many tile servers will forbid or restrict bulk downloading, as it places additional strain on their servers.
Bulk downloading is used to prepare for known no-Internet situations by downloading map tiles, then serving these from local storage.
If you have a set of custom raster tiles that you need to provide to all your users, you may want to consider bundling them together, to make a them easier to deploy to your users. Bundles can be provided in two formats.
Container formats, such as the traditional MBTiles, or the more recent PMTiles, store tiles usually in a database or binary internal format.
These require a special parser to read on demand, usually provided as a TileProvider
by a plugin. The following community-maintained plugins are available to read these formats:
When uncontained, tiles are usually in a tree structure formed by directories, usually 'zoom/x/y.png'. These don't require special parsing, and can be provided directly to the TileLayer
using one of the built-in local TileProvider
s.
AssetTileProvider
You can ship an entire tile tree as part of your application bundle, and register it as assets in your app's pubspec.yaml.
This means that they will be downloaded together with your application, keeping setup simple, but at the expense of a larger application bundle size.
If using AssetTileProvider
, every sub-directory of the tree must be listed seperately. See the example application's 'pubspec.yaml' for an example.
FileTileProvider
This allows for more flexibility: you could store a tile tree on a remote server, then download the entire tree later to the device's filesystem, perhaps after intial setup, or just an area that the user has selected.
This means that the setup may be more complicated for users, and it introduces a potentially long-running blocking action, but the application's bundle size will be much smaller.
There are many independently maintained 'plugins' created by the 'flutter_map' community that give extra, prebuilt functionality, saving you even more time and potentially money.
Some pages in this documentation provide direct links to these plugins to make it easier for you to find a suitable plugin.
However, if you're just browsing, a full list is provided below (in no particular order), containing many of the available plugins. You can click on any of the tiles to visit its GitHub repo or pub.dev package.
Although these plugins have been checked by 'flutter_map' maintainers, we do not accept responsibility for any issues or threats posed by independently maintained plugins.
Use plugins at your own risk.
There is no guarantee that any of these plugins will support the latest version of flutter_map. Please remain patient with the plugin authors/owners.
Many plugins provide multiple methods to achieve similar goals. It is recommended to read the documentation of each potential plugin before using it in your project, as they might have slightly different feature sets or stability.
To help choose between one of these plugins or a DIY solution, see:
Marker
sPolyline
s & Polygon
sIf one of the doesn't suit your needs, then you can create your own, to achieve maximum customizability.
Most plugins create either new , or new . Some plugins just provide additional useful tools.
If you've made your own plugin that you're willing to share, please let us know in the #plugins channel on the flutter_map Discord server. We can then add it to the . We're always looking forward to see what you've made!
Automatically store tiles as the user loads them through interacting with the map, usually on a temporary basis
Download an entire area/region of tiles in one shot, ready for a known no-Internet situation
Provide a set of pre-determined tiles to all users through app assets or the filesystem
(lightweight and MIT licensed)
(also includes , but GPL licensed)
Custom implementation, via a and ImageProvider
(either custom or via a package such as )
Bulk downloading is more complex than , especially for regions that are a non-rectangular shape. Implementing this can be very time consuming and prone to issues.
The includes advanced bulk downloading functionality, of multiple different region shapes, and other functionality. It is however GPL licensed. To help choose whether FMTC or DIY is more appropriate for your use case, please see:
: (when using vector tiles)
: ( when using vector tiles, also works in online contexts)
One common requirement is a custom TileProvider
, and potentially a custom ImageProvider
inside. This will allow your plugin to intercept all tile requests made by a map, and take your own action(s), before finally returning a tile.
Check the Plugins List for providers that already implement the behaviour you wish to replicate.
TileProvider
To create your own usable TileProvider
, the first step is making a class that extends
the abstract class, and adding a constructor.
The constructor should accept an argument of super.headers
, without a const
ant default.
If using an object that needs closing/cancelling, such as an HttpClient
, override the dispose
method.
TileProvider
s must implement a method to return an ImageProvider
(the image of a tile), given its coordinates and the TileLayer
it is used within.
It is best to put as much logic as possible into a custom ImageProvider
, to avoid blocking the main thread.
There's two methods that could be called by flutter_map internals to retrieve a tile: getImage
or getImageWithCancelLoadingSupport
.
Prefer overriding getImageWithCancelLoadingSupport
for TileProvider
s that can cancel the loading of a tile in-flight, if the tile is pruned before it is fully loaded. An example of a provider that may be able to do this is one that makes HTTP requests, as HTTP requests can be aborted on the web (although Dart does not 'natively' support it yet, so a library such as Dio is necessary). Otherwise, getImage
must be overridden.
In addition to the coordinates and TileLayer
, the method also takes a Future<void>
that is completed when the tile is pruned. It should be listened to for completion (for example, with then
), then used to trigger the cancellation.
For an example of this, see #cancellablenetworktileprovider.
If developing a plugin, you may wish to adjust the 'User-Agent' header has been to further differentiate your plugin's traffic from vanilla 'flutter_map' traffic.
Some custom TileProvider
s may want to change the way URLs are generated for tiles, given a coordinate.
It's possible to override:
how the urlTemplate
's placeholders are populated: populateTemplatePlaceholders
the values used to populate those placeholders: generateReplacementMap
the generation method itself: getTileUrl
and/or getTileFallbackUrl
Avoid overriding the generation method itself, as it is not usually necessary.
Creating a new map layer is a great way to achieve a more custom, performant, map design. For example, it might be used to display a scale bar, or overlay a grid.
Check the Plugins List for layers that already implement the behaviour you wish to replicate.
It starts with a normal StatelessWidget
or StatefulWidget
, which then starts its widget tree with a widget dependent on whether the layer is designed to be either 'mobile' or 'static', depending on the purpose of the layer. For more information, see #mobile-vs-static-layers.
Then, there are three possible methods that could be used to retrieve separate 'aspects' of the state of the map.
Calling these inside a build
method will also cause the layer to rebuild automatically when the depended-on aspects change.
Using these methods will restrict this widget to only being usable inside the context of a FlutterMap
.
flutter_map_floating_marker_titles (androidseb)
Enables the display of 'floating' titles over markers
A versatile mapping package for Flutter. Simple and easy to learn, yet completely customizable and configurable, it's the best choice for mapping in your Flutter app.
Setting up an interactive and map is simpler than making your lunch-time coffee! It can be accomplished in just under 30 lines and a minute or two to install.
This code snippet demonstrates everything you need for a simple map (in just over 20 lines!), but of course, FM is capable of much more than just this, and you could find yourself lost in the many options available and possibilities opened!
Not quite sure about something? No problem. Please get in touch via any of these methods, and we'll be with you as soon as possible. Please remember that we are volunteers, so we cannot guarantee (fast) support.
For bug reports & feature requests: check the #faqs then GitHub Issues
For support & everything else: check the #faqs then flutter_map Discord server
Due to time shortages, wait times for feature request implementations are currently extremely long and may not happen at all.
We'd love to have your contributions to add your own or others' pull requests!
We get quite a lot of similar questions, so please check if your question is here before you ask!
flutter_osrm (JaffaKetchup)
flutter_map_query_osm_features (JaffaKetchup)
Layer that allows any widget to be displayed inside a positioned box, similar to
flutter_map_fast_markers (KanarekApp)
🗺️ Supports any* map style
We natively support any static raster tile server, including from a web server or even from the local file system or app asset store. No more vendor lock-in!
💪 Stress-free setup and use
Migrating from a commercial library such as Google Maps has never been easier! No more complex platform-specific setup, no more API keys: just add a widget and you're done.
🧩 Wide ecosystem of plugins
In the unlikely event that flutter_map doesn't natively contain something you need, just check to see if there's a community maintained plugin that does what you need!
➕ Add other map features easily
Add polygons, polylines, and markers/pins to your map easily and quickly. Markers support displaying any widget you might want.