Integrating With A Map
"Browse caching" occurs as the map loads tiles as the user interacts with it (or it is controlled by a MapController
).
To inject the browse caching logic into flutter_map's tile loading process, FMTC provides a custom TileProvider
: FMTCTileProvider
.
Setup is quick and easy in many cases, but this guides through every step in the order in which it should be done to ensure best performance and all factors have been considered.
Remember that a store can hold tiles from more than one server/template URL.
Walkthrough
Before you can get started, make sure you've initialised FMTC & created one or more Stores!
Choose where to construct the tile provider
Where & how you choose to construct the FMTCTileProvider
object has a major impact on performance and tile loading speeds, so it's important to get it right.
Minimize reconstructions of this provider by constructing it outside of the build
method of a widget wherever possible. Because it is not a const
ant constructor, and it will be in a non-const
ant context (TileLayer
), every rebuild will trigger a potentially expensive reconstruction.
However, in many cases, such as where one or more properties (as described in following stages) depends on inherited data (ie. via an InheritedWidget
, Provider
, etc.), this is not possible.
In this case, read the tip in the API documentation carefully. In summary, you should construct as many arguments as possible outside of the build
method, particularly a HTTP Client
and any objects or callbacks which do not have a useful equality/hash code themselves.
Choose which stores it will interact with
The tile provider can interact in multiple ways with multiple stores at once, affording maximum flexibility. Defining how it interacts with these stores will be done in following stages, but you first need to define which stores it will interact with.
How exactly you need to define the stores depends on how much flexibility you need:
It is more common you will want to interact with just one or a defined set of stores at any one time. In this case, use the default constructor. You'll need to choose how (what strategy) it interacts with each store in following stages.
The parameters you will need to use will depend on how advanced your use-case is, but it will progress in a linear fashion:
The mandatory
stores
argument takes a mapping of store names to the strategy to be used for that store.If you want to apply another strategy to all other available stores (whose names are not in the
stores
mapping), use theotherStoresStrategy
argument.If you define that strategy, but you still want to disable interaction with some stores altogether, add these stores to the
stores
mapping with associatednull
values. (IfotherStoresStrategy
is not defined, stores mapped tonull
have no difference to if they were not included in the mapping at all.)
Choose how it will interact with the stores
The BrowseStoreStrategy
s tell FMTC how it should read, update, and create tiles in the store it is associated to.
In the allStores
constructor, it is passed to allStoresStrategy
and applied to all available stores (as described in the previous stage).
Otherwise, in the default constructor, one strategy is assigned to each store, plus optionally one to all other available stores (as described in the previous stage).
There are three possible strategies:
.read
: only read tiles from the associated store.readUpdate
: read tiles, and also update existing tiles in the associated store, if necessary.readUpdateCreate
: read, update (if necessary), and create tiles in the associated store
Choose the preferred & fallback source for tiles
The BrowseLoadingStrategy
s (previously known as CacheBehavior
s) tell FMTC the preferred source for tiles to be loaded from, and how to fallback if that source fails. It is passed to the loadingStrategy
parameter.
There are three possible priorities:
.cacheOnly
Cache
Failure
Cache
Network (URL)
.onlineFirst
Network (URL)
Cache
Standard tile provider
Network (URL)
Failure
The cacheOnly
strategy essentially disables writing to the cache, and makes the chosen BrowseStoreStrategy
s above .read
redundant.
The onlineFirst
strategy may make tile loading appear slower when not connected to the Internet/network.
This is because the HTTP client may attempt to make the request anyway (Dart does not realise sometimes that the Internet is not available), in which case, the HTTP timeout set in the client must elapse before the tile is retrieved from the cache.
Also see how this strategy influences tile updates in stage 6.
Ensure tiles are resilient to URL changes
To reference (enable correct creation/updating/reading of) tiles, FMTC uses a 'storage-suitable UID' derived from the tile's URL. Any one tile from the same server (style, etc. allowing) should have one storage-suitable UID which does not change.
On some servers, it may be acceptable for the UID to be the same as the tile URL. For example, the OpenStreetMap tile server URL for the tile at 0/0/0 will always be https://tile.openstreetmap.org/0/0/0.png
.
However, on some servers, the URL may change, but still point to the same desired tile. Consider the following URL: https://tile.paid.server/0/0/0.png?volatile_key=123
. In this case, the URL requires an API key to retrieve the tile. If the UID was the same as the URL, but the key changes - for example, because it was leaked and refreshed - then FMTC would be unable to reference this tile when it encounters the same URL with the different key. This would mean the tile could not be read or updated, which may significantly impact your app's functionality.
To fix this, the urlTransformer
parameter takes a callback which gets passed the tile's real URL, and should return a stable storage-suitable UID. For example, it should remove the offending query parameters.
The urlTransformer
defined here should usually be the same as the transformer defined for a bulk download. Otherwise, tiles which have been bulk downloaded may not be able to be referenced, for example if an API key changes.
If the TileLayer
used to start the bulk download uses an FMTCTileProvider
with a defined urlTransformer
as the tile provider, it will be used automatically, otherwise the bulk download also takes the urlTransformer
directly.
If the offending part of the URL occurs as in the example above - as part of a query string - FMTC provides a utility callback which can be used as the transformer to remove the offending key & value cleanly.
FMTCTileProvider.urlTransformerOmitKeyValues
takes the tile URL as input, as well as a list of keys. It will remove both the key and associated value for each listed key.
It may also be customized to use a different 'link' ('=') and 'delimiter' ('&') character, and it will remove any key<link>value
found in the URL, not just from after the '?' character.
And that's it! FMTC will handle everything else behind the scenes.
If you bulk download tiles, they'll be able to be used automatically as well.
Examples
A single store in a simple static configuration
This is the most simple case where one store exists, using the default constructor and no other parameters except a BrowseLoadingStrategy
.
Two static named stores with a URL transformer
In this case, there are two stores which never change, which use different BrowseStoreStrategy
s. There is also a urlTransformer
defined, using the utility method.
Stores set from a Provider/Selector with a URL transformer
Note that the URL transformer callback and HTTP client have been defined outside of the FMTCTileProvider
constructor (which must lie within the build
method because it depends on inherited data).
Defining the URL transformer this way instead of an anonymous function ensures that the caching key works correctly, which improves the speed of tile loading.
Defining the HTTP client (although it is technically optional) ensures it remains open even when the provider is being repeatedly reconstructed, which means it does not have to keep re-creating connections to the tile server, improving tile loading speed. Note that it is not closed when the widget is destroyed: this prevents errors when the widget is destroyed whilst tiles are still being loaded, and there is very little potential for memory or performance leaks.
Using multiple stores alongside otherStoresStrategy
, and explicitly disabling a store
otherStoresStrategy
, and explicitly disabling a storeLast updated