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...
In a one word answer: Yes.
FMTC aims to provide all the functionality you will need for advanced offline mapping in your app, and the unique features that will help set your app apart from the competition, in a way that requires little knowledge of the internals of 'flutter_map' and other caching fundamentals, and little effort from you (for most setups).
However, this doesn't mean there aren't any other options to consider! The flutter_map documentation gives a good overview of the different types of caching.
In general, there's a few reasons why I wouldn't necessarily recommend using FMTC:
You're not planning to make use of bulk downloading or import/export functionality
You don't need the fine grained control of stores (and their many-to-many relationship with tiles that keeps duplication minimal across them)
You want to ship every user a standardized tileset, and you don't really need other functionality after that point
Although FMTC will still handle all these situations comfortably, other options may be better suited, and/or more lightweight.
FMTC is an all-in-one solution. It specialises in the functionalities which are difficult and time-consuming to get right (such as bulk downloading), and their integration with the essential function of browse caching, through a clean, unified API, and a clean, decluttered, and fast backend.
Other libraries or DIY solutions may not be all-in-one, but they may be all that's required for your app. Caching alone is not difficult or time consuming to setup yourself, either through a custom TileProvider
backed by a non-specialised image caching provider such as 'cached_network_image', or the other browse-caching-only flutter_map plugin 'flutter_map_cache' if you need to save even more time at the expense of more external dependencies.
Another consideration for non-GPL-licensed projects is abiding by FMTC's GPL license. This is especially true for proprietary products.
For more information about licensing, please see (Proprietary) Licensing.
If you're still not sure, please get in touch: #get-help! I'm always happy to offer honest guidance :)
Hi there 👋 My name is Luka, but I go by JaffaKetchup!
I'm currently in full-time education in the UK studying Computer Science, Geography, and Mathematics, and have been building my software development skills alongside my education for many years. I specialise in the Flutter and Dart technologies to build cross-platform applications, and have over 4 years of experience both from a hobby and commercial standpoint.
I've worked as a senior developer with a small team at WatchEnterprise to develop their Flutter-based WatchCrunch social media app for Android & iOS.
I'm one of a small team of maintainers for Flutter's №1 non-commercially aimed mapping library 'flutter_map', for which we make internal contributions, regulate and collaborate with external contributors, and offer support to a large community. I also personally develop a multitude of extension libraries, such as 'flutter_map_tile_caching'. In addition, I regularly contribute to OpenStreetMap, and continously improve my skills with personal experimental projects.
I'm eager to earn other languages, and more than happy to talk about anything software development related!
Sponsorships & donations allow me to further my education, whilst spening even more time developing the open-source projects that you use & love. I'm grateful for any amount you can spare, all support means a lot to me :) If you can't support me financially, please consider leaving a star and a like on projects that worked well for you.
I'm extremely greatful for any amount you can spare!
Many thanks to all my supporters, donations of all sizes mean a lot to me, and encourage and enable me to continue my work on FMTC and other open-source projects.
In no particular order:
+ more anonymous or private donors
FMTC is licensed under GPL-v3.
If you're developing an application that isn't licensed under GPL, this affects you and your application's legal right to distribution. For more information, please see .
Looking to start using FMTC in your project? Check out the guide!
This is the recommended method of installing this package as it ensures you only receive the latest stable versions, and you can be sure pub.dev is reliable.
Just import the package as you would normally, from the command line:
If you urgently need the latest version, a specific branch, or a specific fork, you can use this method.
Commits available from Git (GitHub) may not be stable. Only use this method if you have no other choice.
First, add the normal dependency following the instructions. Then, add the following lines to your pubspec.yaml file under the dependencies_override
section:
After installing the package, import it into the necessary files in your project:
Also ensure you've followed flutter_map's installation instructions!
And also a thank you to the businesses listed in , and more, for abiding by the GPL license of this repository.
FMTC is licensed under GPL-v3.
If you're developing an application that isn't licensed under GPL, this affects you and your application's legal right to distribution.
I am not a lawyer, and this information is to the best of my understanding only. You are urged to read the license yourself for a thorough understanding.
This project is released under GPL v3. For detailed information about this license, see https://www.gnu.org/licenses/gpl-3.0.en.html. choosealicense.com summarises the license with the following paragraph:
Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
Essentially, whilst you can use this code within commercial projects, they must not be proprietary - they incorporate this 'licensed work' so they must be available under the same license. You must distribute your source code (at least on request) (under the same GPL v3 license) to anyone who uses your program.
I learnt (and am still learning) to code with free, open-source software due to my age and lack of money, and for that reason, I believe in promoting open-source wherever possible to give equal opportunities to everybody, no matter their age, financial position, or any other characteristic. I'm not sure it's fair for commercial & proprietary applications to use software made by people for free out of generosity without giving back to the ecosystem or maintainer(s). On the other hand, I recognise that commercial businesses may want to use my projects for their own proprietary applications, and are happy to support me, and I am also trying to make a small amount of money from my projects, by donations and by selling licenses!
Therefore, if you would like a license to use this software within a proprietary application, I am willing to sell a (preferably yearly) license. If this seems like what you'd be interested in, please do not hesitate to get in touch at fmtc@jaffaketchup.dev. Please include details of your project if you can, and the approximate scale/audience for your app; I try to find something that works for everyone, and I'm happy to negotiate! If you're a non-profit organization, I'm happy to also offer an alternative license for free!
Want to see who else uses FMTC? Check out #trusted-by-many!
FMTC is licensed under GPL-v3.
If you're developing an application that isn't licensed under GPL, this affects you and your application's legal right to distribution. For more information, please see (Proprietary) Licensing.
This page guides you through a simple, fast setup of FMTC that just enables basic browse caching, without any of the cool features that you can discover throughout the rest of this documentation.
Depend on the latest version of the package from pub.dev, then import it into the appropriate files of your project.
Perform the startup procedure to allow usage of FMTC's APIs and allow FMTC to spin-up the underlying connections & systems.
Here, we'll use the built-in, default 'backend' storage, which uses ObjectBox. We'll perform the intialisation just before the app starts, so we can be sure that it will be ready and accessible throughout the app, at any time.
Create a container that is capable of storing tiles, and can be used to and bulk download.
Here, we'll create one called 'mapStore', directly after initialisation. Any number of stores can be created, at any point!
Add FMTC's specialised TileProvider
to the TileLayer
, to enable browse caching, and retrieval of tiles from the specified store.
Double check that the name of the store specified here is the same as the store created above!
You should now have a basic working implementation of FMTC that caches tiles for you as you browse the map!
There's a lot more to discover, from store management to bulk downloading, and from statistics to exporting/importing.
Before using FMTC, especially to bulk download or import/export, ensure you 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.
For example, OpenStreetMap's tile server forbids bulk downloading: https://operations.osmfoundation.org/policies/tiles. And Mapbox has restrictions on importing/exporting from outside of the user's own device.
For testing purposes, check out the testing tile server included in the FMTC project: Testing Tile Server.
This page covers more advanced FMTC usage, and is not appropriate for beginners or simple use cases.
TLDR; FMTC provides a single storage mechanism by default, named FMTCObjectBoxBackend
.
FMTC supports attachment of any custom storage mechanism, through an FMTCBackend
. This allows users to pick their favourite database engine, or conduct in-memory testing.
Only one backend is built-into FMTC: the FMTCObjectBoxBackend
. This backend uses the ObjectBox library to store data.
More info coming soon...
FMTC relies on a self-contained 'environment', called a backend, that requires initialisation (and configuration) before it can be used. This allows the backend to start any necessary seperate threads/isolates, load any prerequisites, and open and maintain a connection to a database. This environment/backend is then accessible internally through a() singleton, so initialisation is not required again.
Initialisation should be performed before any other FMTC or backend methods are used, and so it is usually placed just before runApp
, in the main
method. This shouldn't have any significant effect on application startup time.
If initialising in the main
method before runApp
is called, ensure you also call WidgetsFlutterBinding.ensureInitialised()
prior to the backend initialisation.
Do not call any other FMTC methods before initialisation. Doing so will cause a RootUnavailable
error to be thrown.
Do not attempt to initialise the same backend multiple times, or initialise multiple backends simultaenously. Doing so will cause a RootAlreadyInitialised
error to be thrown.
Avoid using FMTC in a seperate thread/Isolate
. FMTC backends already make extensive use of multi-threading to improve performance.
If it is essential to use FMTC in a seperate thread, ensure that the initialisation is called in the thread where it is used. Be cautious of using FMTC manually across multiple threads simultaneously, as backends may not properly support this, and unexpected behaviours may occur.
It is also possible to un-initialise FMTC and the current backend. This should be rarely required, but can be performed through the uninitialise
method of the backend if required. Initialisation is possible after manual uninitialisation.
v9 is a complete rewrite of FMTC internals, written over hundreds of hours. It focuses on:
improved future maintainability by modularity
improved stability & performance across the board
support of 'tiles across stores': reduced duplication
Check out the CHANGELOG: https://pub.dev/packages/flutter_map_tile_caching/changelog! This page only covers breaking changes, not feature additions and fixes.
Please consider donating: #supporting-me! Any amount is hugely appriciated!
These migration instructions will not cover every scenario, because the number of breaking changes is so high. I've done my best to organise them into categories.
Stores and roots from previous versions will be incompatible with v9, and will not be migratable.
This is because the underlying storage technology has chnaged from Isar to ObjectBox (in the default instance, at least).
Therefore, before publishing a version of your app with v9, it may be beneficial to notify users of the upcoming change, so the sudden dissappearance of their cached data is not unexpected.
Removal of the old cache will need to be performed manually.
This page is a work in progress! Check back later for more tips and tricks.
Keep the number of stores small to improve performance!
FMTC isn't internally optimized to handle large numbers of stores within the database queries and processing, and algorithms often have polynomial time complexities based on the number of stores. This is because of the feature that tiles can belong to multiple stores, and therefore a lot of extra work is required to keep track of this all.
A plugin for 'flutter_map' providing advanced offline functionality
In addition to our generous supporters, FMTC is also trusted and all around the world:
Too easy ! Take a look at Google Maps, or Strava, or whichever other app of your choice.
This project is wholly open source and funded by generous supporters like you! Any amount you can spare that you think FMTC deserves is hugely appriciated, and means a lot to me.
FMTC is licensed under GPL-v3.
If you're developing an application that isn't licensed under GPL, this affects you and your application's legal right to distribution.
Not quite sure about something? No problem, I'm happy to help!
Please get in touch via the correct method below for your issue, and I'll be there to help ASAP!
For bug reports & feature requests: GitHub Issues
For implementation/general support: The #plugin channel on the flutter_map Discord server
For other inquiries and licensing: fmtc@jaffaketchup.dev
FMTC uses a root and stores to structure its data.
There is generally a single root (which generally corresponds to a single ), which contains multiple stores. Cached tiles can belong to multiple stores, which keeps duplication minimized.
The structures use the ambient backend when a method is invoked on it, not at construction time.
Therefore, it is possible to construct an FMTCStore
/FMTCRoot
(see below) before initialisation, but 'using' it will throw RootUnavailable
.
Because FMTC has complex internals that rely heavily on external factors and preconditions being met, it is important to properly anticipate, catch, and handle errors & exceptions.
Errors & exceptions in FMTC come in two primary forms:
Errors: usually generated by FMTC & descended from FMTCBackendError
These indicate incorrect usage on the user's part - for example, an incorrect assumption such as usage of a method on a store before initialisation. These may also be non-specfic types, such as ArgumentError
.
Exceptions: usually generated by the depdenencies of backends, such as databases
These indicate some sort of unexpected failure, such as a database reaching its maximum size limit during a write operation. These are usually not generated/typed by FMTC, and so are often of types from other libraries, such as ObjectBox when using the default FMTCObjectBoxBackend
.
The difference between errors & exceptions extends beyond FMTC, and is a general concept in Dart. See about the difference between Exception
s and Error
s.
Exceptions may be caught using standard try
/catch
blocks (it's not usually recommended to catch FMTCBackendError
s).
FMTC automatically adjusts and changes the thrown StackTrace
to include useful additional info, and ensures that the trace is followed across the many isolates and asynchronous gaps (eg. streams) of the FMTC internals. When creating a bug report, please include the full trace, including debug info at the end, if any is available.
It can usually be assumed that the requested operation did not & will not complete when an exception is thrown from it.
One particular place where exceptions can occur more frequently is during initialisation. The code sample above includes a try
/catch
block to catch these errors. If an exception occurs at this point, it's likely unrecoverable (for example, it might indicate that the underlying database has been corrupted), and the best course of action is often to manually delete the FMTC root directory from the filesystem.
The default directory can be found and deleted with the following snippet (which requires 'package:path' and 'package:path_provider':
Stores contain any metadata associated with them, cached statistics, and maintain a reference to all the tiles that belong to it.
They are referenced by name, the single argument of FMTCStore
.
Ensure names of stores are consistent across every access. "Typed"/code-generated stores are not provided, to maintain flexibility.
Construction of an FMTCStore
object does not imply/infer that the underlying store has been created and is ready for use. Therefore, a store will require creation via its StoreManagement
object (accessed via FMTCStore.manage
) before it can be used.
After a store reference is constructed, the following actions can be performed with it:
This package contains a full example application - prebuilt for Android and Windows by GitHub Actions - showcasing the most important features of this package and its modules.
The example app isn't intended for beginners or as a starting point for a project. It is intended for evaluation purposes, to discover FMTC's capabilities, and how it might be implemented into an app.
To start using FMTC in your own app, please check out the guide instead.
The example application pairs perfectly with the testing tile server included in the FMTC project: !
If you can't build from source for your platform, our GitHub Actions CI system compiles the example app to artifacts for Windows and Android, which just require unzipping and installing the .exe or .apk found inside.
Note that these artifacts are built automatically from the , 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.
StoreManagement
, accessed via FMTCStore().manage
, allows control over the store and its contents.
Stores also have the method getTileProvider()
. This is the point of integration with flutter_map, providing browse caching through a custom image provider. This TileProvider
can then be passed to the TileLayer.tileProvider
parameter.
Avoid getting the TileProvider
from within the build method, especially if the widget is rebuilt frequently.
It can cause unnecessary errors and worsened performance.
This method (and others) optionally take a FMTCTileProviderSettings
. These configure the behaviour of the tile provider. Defaults to the settings specified in the , or the package default (see table below) if that is not specified.
FMTCTileProviderSettings
can take the following arguments:
This enumerable contains 3 values, which are used to dictate which logic should be used to store and retrieve tiles from the store.
cacheFirst
Get tiles from the local cache if possible.
Only uses the Internet if it doesn't exist, or to update it if it has expired.
onlineFirst
Get tiles from the Internet if possible.
Updates every cached tile every time it is fetched (ignores expiry).
cacheOnly
Only get tiles from the local cache, and throw an error if not found.
Recommended for dedicated offline modes.
Since v3, FMTC relies on URL equality to find tiles within a store during browsing. This method is therefore necessary in some cases where the URL contains query parameters.
If the URL's query parameters (the key-value pairs list found after the '?') contains a value that may change between fetches, such as an API key, use obscuredQueryParams
.
This method strips specified keys and values from the query parameters, and avoids storing them in the database.
Pass it the list of query keys who's values need to be omitted from storage. For example, 'api_key' would remove the 'api_key', and any other characters until the next key-value pair, or the end of the URL, as seen below:
Do not depend on this method to remove secret information from a URL.
Prefer sending any information (as discussed above) through the HTTP headers. This may improve performance and reliability, and can be considered good practise anyhow.
◉ 📲
Integrated Caching × Bulk Downloading
Get both dynamic browse caching that works automatically as the user browses the map, and bulk downloading to preload regions onto the user's device, all in one convenient, integrated API!
with
◉ 🏃
Ultra-fast & Performant
No need to bore your users to death anymore! Bulk downloading is super-fast, and can even reach speeds of over 1000 tiles per second. Existing cached tiles can be displayed on the map almost instantly.
Multi-threaded setup to minimize load on main thread, even when browse caching
Streamlined internals to reduce memory consumption
Successfully downloaded tiles aren't redownloaded when an unexpectedly failed download is recovered
◉ 🧩
Import & Export
Export and share stores, then import them later, or on other devices! You could even remote control your organization's devices, by pushing tiles to them, keeping your tile requests (& costs) low!
◉ 💖
Quick To Implement & Easy To Experiment
A basic caching implementation can be setup in four quick steps, and shouldn't even take 5 minutes to set-up. Check out our Quickstart instructions.
Ready to experiment with bulk downloading, but don't want to make costly and slow tile requests? Check out the testing tile server included in the FMTC project: Testing Tile Server!
...and many more!
StoreStats
, accessed via FMTCStore().stats
, allows access to cached statistics, as well as retrieval of a recent tile (as an image), and the watching over changes in the store.
StoreMetadata
, accessed via FMTCStore().metadata
, allows access and control over a simple peristent storage mechanism, designed for use with custom data/properties/fields tied to the store (such as a CacheBehavior
or URL template).
Data is interpreted in key-value pair form, where both the key and value are String
s. Internally, the default backend stores it as a flat JSON structure. The metadata is stored directly on the store: if the store is deleted, it is deleted, and an exported store will retain its metadata. More advanced requirements will require use of a seperate persistance mechanism.
Remember that metadata
does not have any effect on internal logic: it is simply an auxiliary method of storing any data that might need to be kept alongside a store.
RootRecovery
, accessed via FMTCRoot.recovery
, allows access to the bulk download recovery system, which is designed to allow rescue (salvation and restarting) of failed downloads when they crashed due to an unexpected event.
Once a RecoveredRegion
has been retreived, it can be converted to a standard region:
either a DownloadableRegion
, using toDownloadable
The start
tile will be adjusted from the original to reflect the progress of the download before it failed, meaning that tiles already successfully cached (excluding buffered) will not be downloaded again, saving time and data!
The end
tile will be either the original, or the maximum number of tiles normally in the region (which will have no resulting difference than null
, but allows for a quick estimate of the number of remaining tiles to be made without needing to recheck
the entire region).
or a BaseRegion
(with the correct subtype), using toRegion
Roots contain cached statistics and recovery information.
Because there is only a single root at any time, the root is not named. Instead, it is accessed via FMTCRoot
- and all members are static.
After the root reference is constructed, the following actions can be performed with it:
Management of the root is not provided by FMTCRoot
. Instead, the backend should implement any necessary methods.
RootStats
, accessed via FMTCRoot.stats
, allows access to cached statistics, as well as listing of all existing stores, and the watching over changes in multiple/all stores and the bulk download recovery system.
Remember that the size
and length
statistics in the root may not be equal to the sum of the same statistics of all available stores, because tiles may belong to many stores, and these statistics do not count any tile multiple times.
FMTC also provides the ability to bulk download areas of maps in one-shot, known as 'regions'. There are multiple different types/shapes of regions available: .
Before using FMTC, especially to bulk download or import/export, ensure you 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.
For example, OpenStreetMap's tile server forbids bulk downloading: . And Mapbox has restrictions on importing/exporting from outside of the user's own device.
For testing purposes, check out the testing tile server included in the FMTC project: .
Downloading is extremely efficient and fast, and uses multiple threads and isolates to achieve write speeds of hundreds of tiles per second (if the network/server speed allows). After downloading, no extra setup is needed to use them in a map (other than the usual ).
It is also simple to understand and implement:
↳ Optionally, before downloading
↳ Optionally, when testing,
events to update your user
Optionally,
A miniature tile server, intended to test and calibrate FMTC, has been included in the project.
Avoid making too many costly and slow requests to your chosen tile server during development by using this miniature tile server!
For internal testing and development purposes, it also doubles down as a handy way to test your application without making too many costly and slow requests to your chosen tile server. When in use with the example application, it can handle over 2000 tiles/second.
It is a very simple web HTTP server written in Dart, that responds to all* requests with a tile. There is a theoretically 90% chance that this tile will be a specific land tile, and a 10% chance that it will be a sea tile - designed to test the sea tile skipping functionality. There are only these two tiles - it is not a full tile server.
To use this tile server:
The tile server is hardcoded to use standard HTTP port 7070 to serve content, which is usually free. Other programs must not be using this port.
Download/compile & start the tile server (no permanent installation required)
Use the following URL to connect to it
From the local device: http://localhost:7070/{z}/{x}/{y}.png
Control the tile server using keyboard key presses in the console window
q
: Release port 7070 and quit the executable
UP arrow: Increase the artificial delay between request and response by 2ms
DOWN arrow: Decrease the artificial delay between request and response by 2ms
Regions (BaseRegion
s) are geographical areas that do not yet have any of the necessary extra information to start a download (this is the responsibility ofDownloadableRegion
).
There are 4 types of BaseRegion
:
RectangleRegion
s are defined by a LatLngBounds
: two opposite LatLng
s.
This is usually all you get from most apps, so why not give your user a unique experience with some of our other region types...
CircleRegion
s are defined by a center LatLng
and radius in kilometers.
If you instead have two coordinates, one in the center, and one on the edge, you can use method, as below:
LineRegion
s are defined by a list of LatLng
s, and a radius in meters.
This could be used to download tiles along a planned travel route, for example hiking or long-distance driving. Import coordinates from a routing engine, or from a GPX/KML file for maximum integration!
This region may generate more tiles than strictly necessary to cover the specified region. This is due to an internal limitation with the region generation algorithm, which uses rectangles to approximate the actual desired shape.
This type of region may consume more memory/RAM when generating tiles than other region types.
Polygons should not contain self-intersections. These may produce unexpected results.
Holes are not supported.
RecoverableRegion
s aren't technically the same type of region as the others, and is the odd one out.
However, it can be converted to a downloadable region in the same way as the others, or the original BaseRegion
extracted using toRegion
.
must be converted to DownloadableRegion
s before they can be used to download tiles.
These contain the original BaseRegion
, but also some other information necessary for downloading, such as zoom levels and URL templates.
Before continuing to downloading the region, you can use check()
to count the number of tiles it will attempt to download. This is accessible through FMTCStore().download
.
The method takes the DownloadableRegion
generated above, and will return an int
number of tiles. For larger regions, this may take a few seconds.
This figure will not take into account any skipped sea tiles or skipped existing tiles, as those are handled at the time of download.
Now that you have constructed a DownloadableRegion
, you're almost ready to go.
Before you call startForeground
(via FMTCStore().download
) to start the download, check out the customization parameters:
parallelThreads
(defaults to 5)
The number of simultaneous download threads to run
maxBufferLength
(defaults to 200)
The number of tiles to temporarily persist in memory before writing to the cache
skipExistingTiles
(defaults to false
)
Whether to avoid re-downloading tiles that have already been cached
skipSeaTiles
(defaults to true
)
Whether to avoid caching tiles that are entirely sea (based on whether they have the same pixels as the tile at z17, x0, y0, which is assumed to be sea)
rateLimit
The maximum number of tiles that can be attempted per second
maxReportInterval
(defaults to 1 second)
The duration in which to emit at least one DownloadProgress
event
Ensure skipSeaTiles
is disabled when downloading from a server where the tile at z17, x0, y0 is not a consitently colored sea tile, or where different sea tiles look different, such as with satellite imagery. FMTC cannot yet skip sea tiles that match these conditions.
Therefore, where speed is significant and the download is unlikely to be unexpectedly interrupted, consider disabling download recovery.
Before using FMTC, especially to bulk download or import/export, ensure you 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.
Whilst not recommended, it is possible to start and control multiple downloads simultaneously, by using a unique Object
as the instanceId
argument. This 'key' can then later be used to control its respective download instance.
Note that this option may be unstable.
The startForeground
method returns a (non-broadcast) Stream
of DownloadProgress
events, which contain information about the overall progress of the download, as well as the state of the latest tile attempt.
In addition to display each individual event to your user, you may also need to keep track of information from across multiple DownloadProgress
events. In this case, you'll likely need to use the latestTileEvent
getter to access the latest TileEvent
object, and keep track of its properties.
For example, you may wish to keep a list of all the failed tiles' URLs.
However, there are 3 important things to keep in mind when doing this:
On Windows or Linux Download a copy of the latest '<platform>-ts' artifact from GitHub Actions, and run the executable inside:
On other platforms Clone the to your device, then run '/tile_server/bin/tile_server.dart' manually
From the same network (on another device): http://<your-local-ip>:7070/{z}/{x}/{y}.png
To find your local IP address, follow the
CustomPolygonRegion
s are defined by a list of LatLng
s defining the outline of a .
For more info, see .
The TileLayer
passed to the options
parameter must include both a urlTemplate
(or WMS configuration) and a userAgentPackageName
, unless it is only being used to .
disableRecovery
(defaults to false
)
Whether to avoid registering this download with the system for safe recovery if the download fails
The system can slow a download, as it must be regularly updated with the latest progress of the download, and this data is not currently batched (so it occurs for every downloaded tile). This is an optimization planned for later implementation.
For example, OpenStreetMap's tile server forbids bulk downloading: . And Mapbox has restrictions on importing/exporting from outside of the user's own device.
For testing purposes, check out the testing tile server included in the FMTC project: .
To reflect the information from a single event back to the user, use a StreamBuilder
, and build the UI dependent on the 'snapshots' of the stream. If you need to keep track of information from across multiple events, see below.
behavior
: CacheBehavior
Determine the logic used during handling storage and retrieval of browse caching
CacheBehavior.cacheFirst
cachedValidDuration
: Duration
Length of time a tile remains valid, after which it must be fetched again (ignored in onlineFirst
mode)
const Duration(days: 16)
maxStoreLength
: int
Maximum number of tiles allowed in a cache store (deletes oldest tile)
0
: disabled
obscuredQueryParams
: List<String>
[]
: empty
Statistics: stats
Retrieve information about the store and its contents
Recovery: recovery
Recovery from unexpectedly failed bulk downloads
External: external
Export & import store archives
Before using FMTC, especially to bulk download or import/export, ensure you 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.
For example, OpenStreetMap's tile server forbids bulk downloading: https://operations.osmfoundation.org/policies/tiles. And Mapbox has restrictions on importing/exporting from outside of the user's own device.
For testing purposes, check out the testing tile server included in the FMTC project: Testing Tile Server.
FMTC allows stores (including all necessary tiles and metadata) to be exported to an 'archive'/a standalone file, then imported on the same or a different device!
FMTC does not support exporting tiles to a raw Z/X/Y directory structure that can be read by other programs.
For example, this can be used to create backup systems to allow user's to store maps for later off-device, sharing/distribution systems, or to distribute a preset package of tiles to all users without worrying about managing IO or managing assets, and still allowing users to update their cache afterward!
External functionality is accessed via FMTCRoot.external('~/path/to/file.fmtc')
.
All functionalities require the path to an archive file. The file may not necessarily exist: for the export
method only, the file will be created if it does not exist, and overwritten if it does. Other methods require the file to exist, and be in a valid format.
Archives are backend specific. They cannot necessarily be imported by a backend different to the one that exported it.
The export()
method copies the stores, along with all necessary tiles, to a seperate archive at the specified location (creating it if non-existent, overwriting it otherwise), in the FMTC (.fmtc) format.
There are 3 methods available to control a download, and 1 method to check the download's current state.
It may take some time to perform these operations if the download has a low TPS, as each thread checks the current state/awaits a signal between each of their tiles.
pause
& cancel
are asynchronous for this reason, and will complete when all threads are paused or cancelled. resume
returns immediately.
If your user needs to temporarily pause the download, with the ability to resume it at some point later (within the same app session), use pause
and resume
.
pause
allows all threads to finish the tile they're currently downloading, then forces them to wait for a signal sent by resume
before they can download their next tile. It does not write any buffered tiles to the store.
Use isPaused
to check whether the download is currently paused.
If your user needs to stop the download entirely, use cancel
.
cancel
allows all threads to finish the tile they're currently downloading, then forces them to cleanup and 'kill' themselves. Any buffered tiles are written to the store before the future returned by cancel
is completed.
It is safe to use cancel
after pause
without resume
ing first.
The import()
method copies the specified archive to a temporary location, then opens it and extracts the specified stores (or all stores if none are specified) & all necessary tiles, merging them into the in-use database. The specified archive must exist, must be valid, and should contain all the specified stores, if applicable.
There is no support for directly overwriting the in-use database with the archived database, but this may be performed manually while FMTC is uninitialised.
There must be enough storage space available on the device to duplicate the entire archive, and to potentially grow the in-use database.
This is done to preserve the original archive, as this operation writes to the temporary archive. The temporary archive is deleted after the import has completed.
The returned value is complex. See the API documentation for more details:
If an importing store has the same name as an existing store, a conflict has occurred, because stores must have unique names. FMTC provides 4 resolution strategies:
skip
Skips importing the store
replace
Deletes the existing store, replacing it entirely with the importing store
rename
Appends the current date and time to the name of the importing store, to make it unique
merge
Merges the two stores' tiles and metadata together
In any case, a conflict between tiles will result in the newer (most recently modified) tile winning (it is assumed it is more up-to-date).
If the user must be given a choice as to which stores to import (or it is helpful to know), and it is unknown what the stores within the archive are, the listStores
getter will list the available store names without performing an import.
The same storage pitfalls as import
exist. listStores
must also duplicate the entire archive.