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 caching, 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, there are two main concerns that apply to most people:
Can you abide by the GPL v3 license, or do you need an alternative proprietary license? See #proprietary-licensing for more information about this
Do you require all the functionality and control that FMTC offers?
Answering the second question is the more difficult than answering the first one, but it will tell you whether it's worth your while getting an alternative proprietary license if you need it.
---
You'll want to consider using FMTC over a custom DIY solution if you need any of the features below. These take a long time to reproduce and get right, but we've already done the hard work for you!
You need to provide bulk downloading or import/export functionality to your users
You or your users need a lot of fine-grain control over the cached tiles
However, if you match all of the following, you may find that a DIY/custom solution will work better for you.
You are developing a proprietary application, and we can't reach an agreement for an alternative license I aim to agree a price or deal that works for the both of us, so please do get in touch even if you're unsure if you can afford a license
You need only very basic caching
You need only browse caching
If you're still not sure, please get in touch: #get-help. I'm always happy to offer guidance :)
flutter_map
Installation & SetupYou must make sure you follow flutter_map
's installation and additional setup instructions.
fmtc_plus_background_downloading
Installation & SetupThis module is only supported on Android.
To install this module, follow the Installation instructions for this package.
Add the following to 'android\app\src\main\AndroidManifest.xml' and any other manifests:
This will allow the application to acquire the necessary permissions (should the user allow them at runtime) to a background process.
FOREGROUND_SERVICE
: allows the application to start a foreground service - a type of Android service that can run in the background, as long as the application isn't force stopped.
WAKE_LOCK
: allows the background process (technically foreground service) to run even when the device is locked/asleep. Also allows the acquisition of a WiFi lock.
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
(must be requested at runtime): assists with the background process not being killed by the system.
Background downloading needs to show notifications, which requires a 3rd party package. See it's installation/setup instructions:
fmtc_plus_sharing
Installation & SetupTo install this module, follow the Installation instructions for this package.
Please follow these additional instructions for supporting Android versions above 11 and building for release:
Unfortunately, I do not have the hardware to test this library on Apple platforms. If you find issues, please report them!
It is unknown whether this setup is needed in all cases, so it is recommended to follow these only when you receive errors during building your app.
Some developers may have issues when releasing the app or uploading to TestFlight - see issue #69 for the first report of this problem. This is due to some of this library's dependencies on platform plugins.
Annotate that access is not needed to the Media, Audio, and Documents directories - this package uses only custom file types. Add these lines to your Podfile just before target 'Runner' do
.
Add UIBackgroundModes
capability with the fetch
and remote-notifications
keys to Xcode, to describe why your app needs to access background tasks - in this case to bulk download maps in the background.
FMTC uses Roots and Stores to structure it's data. Previously, these represented actual directories (hence the reference to directories within the codebase), but now they just represent Isar databases.
There is usually only one root (formed of a directory and some miscellaneous databases) per application, which contains multiple stores (formed of a single database holding a descriptor, multiple tiles, and metadata).
Once you can access FlutterMapTileCaching.instance
after Initialisation, chaining of methods and accessors is used to access functionality.
Base Chains are used as an intermediate step to access functionality on Roots and Stores
Additional Chains are added to Base Chains to reach the actual functionality.
To get the Root, chain on rootDirectory
(the name of the accessor is a remnant leftover from previous versions).
To get a Store, there are two possible methods. FMTC does not use code generation, so store names are flexible, and so use String
s to access the Store.
This is the recommended method. Always use this method where possible.
call()
/()
gets a StoreDirectory
by the name inside the parentheses.
Note that the store will not be automatically created once accessed, as this requires asynchronous tasks, so it is important to create the store manually (if necessary).
Examples in this documentation will usually assume that the stores are already created/ready.
This method is not recommended, for the reasons listed below. Prefer using #without-automatic-creation wherever possible.
It is synchronous and therefore blocks the main thread
It results in hard to trace code, as the creation calls are no longer obvious
It encourages minimizing accesses to increase performance, which is against the philosophy of the chaining strategy
[]
gets a StoreDirectory
by the name inside the parenthesis.
Note that the store will be automatically created once accessed, although it will be done synchronously in a way that blocks the main thread.
After this, you can chain any of the following members/accessors (each will be accessible on a Root, a Store, or both).
Prefer using asynchronous versions of sub-methods where possible, as these won't block the UI thread.
If running inside an isolate, or blocking the UI thread doesn't matter, use the synchronous versions wherever possible, as they have slightly better performance.
Looking to modify the underlying databases youself?
This is not recommended, as they are carefully crafted to work with FMTC internal state. But, if you've read through the FMTC code and understand it, and you need to, you may import the internal APIs usually intended for modules.
Import 'fmtc_module_api.dart' from the same package
Install Isar
manage
Control, modify, and configure an actual structure ('physical' directory or database) itself
stats
Retrieve statistics about a structure (Root or Store) itself
recovery
Recover failed bulk downloads, and prepare them to restart
import
Import and prepare a store from a previously exported archive file
download
Prepare, start, and manage a store's bulk downloads
metadata
A simple key-value pair store designed for storing simple, store related information
export
Export a store to an archive file, for future importing
Applies only to Stores
This library provides a very simple persistent key-value pair storage system, designed to store any custom information about the store. These are stored alongside tiles in the tile database.
For example, your application may use one store per urlTemplate
, in which case, the URL can be stored in the metadata.
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.
Both asynchronous and synchronous versions of the below methods are available.
Add a new key-value pair to the store. For example:
Read all the key-value pairs from the store, and return them in a Map<String, String>
. For example:
Remove a key-value pair from the store. For example:
Remove all the key-value pairs from the store. For example:
FMTC is licensed under GPL-v3.
If you're developing a proprietary (non open-source) application, this affects you and your application's legal right to distribution. For more information, please see #proprietary-licensing.
Before using FMTC, ensure you comply with the appropriate rules and ToS set by your tile server. Failure to do so may lead to a permenant ban, or any other punishment.
This library and/or the creator(s) are not responsible for any violations you make using this package.
OpenStreetMap's can be found here: specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.
This page guides you through a simple, fast setup of FMTC that just enables basic browse caching, without any of the bells and whistles 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 connect to the underlying systems.
Create an isolated space to store tiles and other information to be accessed by the map and other methods.
Enable your FlutterMap
widget to use the caching and underlying systems of FMTC.
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 management to bulk downloading, and from statistics to exporting/importing.
Creating regions is designed to be easy for the user and you (the developer).
The Example Application contains a great way you might want to allow your users to choose a region to download, and it shows how to use Provider to share a created region and the number of approximate tiles it has to a download screen.
All regions (before conversion to DownloadableRegion
) implement BaseRegion
.
The most basic type of region, defined by two North West and South East coordinates that create a LatLngBounds
.
Skewed parallelograms (rectangles with a 3rd control point) or rotated rectangles are not currently supported
A more advanced type of region, defined by a center coordinate and radius (in kilometers).
If you have two coordinates, one center, and one on the edge of the circle you want, you can use 'latlong2's Distance.distance()
method, as below:
The most advanced type of region, defined by a list of coordinates and radius (in meters).
After you've created your region, you can convert it to a drawable polygon (below), or convert it to a DownloadableRegion
ready for downloading.
All BaseRegions
can be drawn on a map with minimal effort from you or the user, using toDrawable()
.
Internally, this uses the toOutline(s)
method to generate the points forming the Polygon
, then it places this/these polygons into a PolygonLayer
.
FMTC is licensed under GPL-v3.
If you're developing a proprietary (non open-source) application, this affects you and your application's legal right to distribution. For more information, please see #proprietary-licensing.
Before using FMTC, ensure you comply with the appropriate rules and ToS set by your tile server. Failure to do so may lead to a permenant ban, or any other punishment.
This library and/or the creator(s) are not responsible for any violations you make using this package.
OpenStreetMap's can be found here: specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.
The main basis of this package is the FlutterMapTileCaching
object, which exposes the majority of FMTC's APIs (through FMTC.instance
), and also contains most of the state needed to connect and communicate with the underlying systems.
Therefore, it must be asynchronously initialised on every app startup, usually in the main
method that runs directly after the Flutter environment starts.
FMTC
is a shorthand type alias for FlutterMapTileCaching
, and works in exactly the same way. Often, documentation will use the shortened version to save space, and you should do so in your code as well.
You must call initialise()
before trying to use instance.
Failure to do so will throw a StateError
.
In summary, in the extremely unlikely event of database corruption, FMTC can recover automatically (in most circumstances), with minimal data loss, given enough fatal app crashes - corruption cannot be caught in any other way.
Whilst it is (almost) impossible for one of the underlying Isar databases to become corrupted during normal usage, or even due to a bug, it can happen if the database file is modified manually.
In this case, it is currently impossible to avoid a fatal crash during initialisation, as the database reader/parser crashes all Dart threads & isolates without warning. Please see this issue I opened:
However, FMTC has a workaround.
By using a basic temporary text file, the IDs/filenames of successfully opened databases can be recorded. Once all databases have been opened, the file is deleted, meaning that the safety system will not intervene on the next app launch. However, if a database open attempt crashes the app, the file will not be deleted, and it will contain the list of safe databases.
Because the order in which databases are opened is determinate (alphabetical), the app can open every database one by one, (metaphorically) crossing it off the list. When there are no more safe databases, but more database files, the next database is deleted instead of being opened. The initialisation then continues as normal, still using the file appropriatley.
Therefore, one fatal crash is enough to detect one faulty database, and delete it, allowing the app to initialise normally on the next launch.
If there are multiple corrupted databases (n
), one fatal crash is needed per database, so the app will successfully open after n + 1
.
All of this means that FMTC can usually recover from a database corruption with minimal data loss. However, if the database filenames/IDs are also changed, the behaviour is unspecified, especially if the initialisation file already exists.
This package provides the ability to download areas of maps, known as 'regions' throughout this documentation. There are built in region shapes that even large apps, such as Google Maps, don't have!
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, tiles are stored in the same place as when Browse Caching, meaning that no extra setup is needed to use them in a map (other than the usual flutter_map Integration).
Before using FMTC, ensure you comply with the appropriate rules and ToS set by your tile server. Failure to do so may lead to a permenant ban, or any other punishment.
This library and/or the creator(s) are not responsible for any violations you make using this package.
OpenStreetMap's can be found here: specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.
Start downloading that region, either in the foreground or background
All APIs listed in this section are children of the download
getter.
Start a download in the foreground
Start a download in the background
Check the number of tiles in a certain region
Cancel any ongoing downloads
The recovery system is designed to support bulk downloading, and provide some form of recovery if the download fails unexpectedly - this might happen if the app crashes, for example.
Read more about the recovery system here:
Applies only to Roots
The recovery system is designed to rescue failed bulk downloads, in the event of an unexpected error - such as a fatal crash or other external event.
A plugin for 'flutter_map' providing advanced offline functionality
You're viewing documentation for an older version of FMTC (v8).
For the latest documentation, see .
FMTC currently has some stability issues on certain platforms, especially iOS. These stability issues can cause unexpected behaviours.
I am working to resolve these issues in v9, benefiting from the improved stability of Isar v4.
For more information about what this means, see #isar-stability-issues.
I work on all of my projects in my spare time, including maintaining (along with a team) Flutter's № 1 (non-commercially maintained) mapping library 'flutter_map', bringing it back from the brink of abandonment, as well as my own plugin for it ('flutter_map_tile_caching') that extends it with advanced caching and downloading. Additionally, I also own the Dart encoder/decoder for the QOI image format ('dqoi') - and I am slowly working on 'flutter_osrm', a wrapper for the Open Source Routing Machine.
Sponsorships & donations allow me to continue my projects and upgrade my hardware/setup, as well as allowing me to have some starting amount for further education and such-like. And of course, a small amount will find its way into my Jaffa Cakes fund (https://en.wikipedia.org/wiki/Jaffa_Cakes) - why do you think my username has "Jaffa" in it?
Many thanks for any amount you can spare, it means a lot to me!
I am not a lawyer, and this information is to the best of my understanding. 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 on request (under the same GPL v3 license) to anyone who uses your program.
However, I am willing to sell custom alternative proprietary licenses on a case-by-case basis and on request.
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 or financial position. I'm not sure it's fair for commercial proprietary applications to use software made by people for free out of generosity. On the other hand, I am also trying to make a small amount of money from my projects, by donations or by selling licenses. And I recognise that commercial businesses may want to use my projects for their own proprietary applications.
Therefore, if you would like a license to use this software within a proprietary, I am willing to sell a (preferably yearly or usage based) license for a reasonable price. If this seems like what you want/need, please do not hesitate to get in touch at fmtc@jaffaketchup.dev.
Not quite sure about something? No problem. Please get in touch via any of these methods, and I'll be with you as soon as possible:
For bug reports & feature requests: GitHub Issues
For implementation/general support: The #plugin channel on the flutter_map Discord server
For other inquires: fmtc@jaffaketchup.dev
Applies to Roots & Stores
This package contains a full example application - prebuilt for Android and Windows - showcasing the most important features of this package and it's 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 Quickstart guide instead.
There are prebuilt applications for Android and Windows available on GitHub.
These are automatically built (using GitHub Actions) from the latest available commits every time the source files change, so may include functionality not yet available via pub.dev installation.
You can verify that these applications are built directly from the source code, and have not been maliciously modified to included malware, because the committer is always the GitHub Actions Bot, which can be verified by the profile icon linking to a non-profile page.
To run the prebuilt Android application on most devices, download the .apk package (from #prebuilt-example-applications) to your device, then execute it to install it.
After installation, it will appear in the launcher like any other application.
The operating system may request permissions to install applications from unknown sources: you must allow this.
To run the prebuilt Windows application on most devices, download the .exe package (from #prebuilt-example-applications) to your device, then execute it to install it.
It will require a very simple installation with no administrator privileges required, then it will appear in the Start Menu and search bars like any other application. You can optionally choose to create a desktop shortcut.
You may receive security warnings depending on your system setup: these are false positives and occur due to the package being unsigned.
For other platforms, there are no prebuilt applications.
You'll have to clone this project, open the 'example' directory, and then build for your desired platform using Dart and Flutter as normal.
Settings change the functionality of FMTC throughout a project, and can be useful to setup customizations that are needed across multiple calls.
For example, a project with multiple getTileProvider()
calls can configure the #tile-provider-settings here, to reduce duplication and improve maintainability.
Stores also have the method getTileProvider()
. This is the point of integration with flutter_map, providing browse caching through a custom image provider, and can be used as so:
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.
If you've got a value (such as a token or a key) in the URL's query parameters (the key-value pairs list found after the '?') that you need to keep secret or that changes frequently, make use of obscuredQueryParams
.
Pass it the list of query keys who's values need to be removed/omitted/obscured in 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:
Since v3, FMTC relies on URL equality to find tiles within a store during browsing. This method is therefore necessary in cases where a token changes periodically.
Applies to Roots & Stores
Before using FMTC, ensure you comply with the appropriate rules and ToS set by your tile server. Failure to do so may lead to a permenant ban, or any other punishment.
This library and/or the creator(s) are not responsible for any violations you make using this package.
OpenStreetMap's can be : specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.
To start downloading tiles, you must , even if you do not plan to use the .
fmtc_plus_background_downloading Module
Unfortunately, background downloading is available on Android only, due to the strict limitations imposed by iOS. This is unlikely to change in the future, especially as I am currently unable to develop for iOS.
In addition, there is no planned support for other platforms.
There is some confusion about the way background process handling works on Android, so let me clear it up for you: it is confusing.
Each vendor (eg. Samsung, Huawei, Motorola) has their own methods of handling background processes.
Some manage it by providing the bare minimum user-end management, resulting in process that drain battery because they can't be stopped easily; others manage it by altogether banning/strictly limiting background processes, resulting in weird problems and buggy apps; many manage it by some layer of control on top of Android's original controls, making things more confusing for everyone.
Therefore there is no guaranteed behaviour when using this functionality. You can see how many vendors will treat background processes here: ; you may wish to link your users to this site so they can properly configure your app to run in the background.
To try and help your users get to the right settings quicker, use the requestIgnoreBatteryOptimizations()
method before starting a background download. This will interrupt the app with either a dialog or a settings page where they can opt-in to reduced throttling. There is no guarantee that this will work, but it should help: this is not required and the background download will still try to run even if the user denies the permissions.
Internally, a foreground service is actually used. This allows the service to run as long as the app hasn't been force stopped.
The effectiveness of the system is reduced by background downloading.
If the user leaves the application, then the recovery system may report the ongoing background download as failed, as it has no way of knowing about it. If the user tries to retry the download, both downloads may then fail, and the recovery system may fail also.
There is no way of resolving this situation. You may prefer to disable recovery on background downloads.
A backport of this functionality to v6 is also available - see , and install it through GitHub: .
Unlike foreground downloading, where you can , background downloading does not provide any way to do this, so it is much less customisable.
The download progress notification only displays the percentage progress and number of tiles attempted/max number of tiles (see ).
is not supported by background downloads.
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.
Available since v7
Buffering reduces the total download time, at the expense of increased memory usage.
Without buffering, every tile is written directly to the database before continuing to the next one (within each simultaneous thread). This requires a write transaction for every tile, which are relatively slow. Without buffering, the database write speed is usually the limiting factor in the download speed.
To avoid this problem, buffering can be used. Tiles are written to an intermediate buffer before being written to the database, in bulk. This means a transaction is only needed for every bulk write operation, which can lead to huge speed improvements (>2x is possible). However, there are two major cons that you should consider before implementing this in your application:
Memory usage increases significantly When in use, the Memory usage graph within DevTools will likely look like a sawtooth wave. The peak memory usage may be too high for some devices, so you should consider your audience.
An app crash can lead to data loss Tiles in the buffer will be lost in the event of an app crash, meaning their download will have been wasted. Tiles that have been written previously will not be lost.
It may be appropriate to leave the decision up to each user individually. In this case, ensure you thoroughly explain these cons to the user, to allow them to make an informed decision. Alternatively, you might make a decision based on the platform. Desktop platforms are likely to have enough RAM capable of holding the buffer, whereas some older mobile devices may struggle.
Buffering is disabled by default, and can be enabled in the startForeground
method call (the property is not part of the DownloadableRegion
).
Buffering is not supported by background downloading, as maximizing speed isn't usually a priority for background downloads.
Buffering can be defined by one of two types of limit, shown below. Neither has a disadvantage in terms of performance, but memory allows finer grain control, at the expense of being less obvious to the user.
Memory (buffer bytes size)
Tiles (buffer length)
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
◉ 📲
Caching × Downloading
Get both dynamic 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 package!
◉ 🏃
Ultra-fast & Performant
No need to bore your users to death anymore! Bulk downloading is super-fast, and can even reach speeds of over 600 tiles per second. Existing cached tiles can be displayed on the map almost instantly. Don't even mention memory consumption: you won't realise there is any.
Multi-threaded downloads
Tile buffering to reduce database writes
Streamlined behind-the-scenes to reduce memory consumption
◉ 🧩
Import & Export
Using one of our official extension modules, allow your users to share and backup their cached tiles! You could even remote control your organization's devices, by pushing tiles to them, keeping your tile requests (& costs) low!
◉ 💖
Quick To Implement (& Quicker To Love)
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.
fmtc_plus_sharing Module
It is possible to re-import a store generated by an Exporting.
Attempting to import a damaged/corrupted store may result in a fatal app crash, or major errors at least.
See #initialisation-safety for more information about how this may be handled in future.
withGUI
)File
(manual
)In the event that a store with the same name already exists as the store that is trying to be imported, FMTC has only simple conflict resolution behaviour.
When a collision is detected, the defined callback collisionHandler
is called asynchronously, with the filename of the import file in addition to the real name of the contained store. It can return either:
true
: Override the entire existing store and its contents
false
: Skip/cancel the import
Looking to start using FMTC in your project? Check out the guide!
FMTC is currently somewhat unstable for applications with a wide public reach, due to some issues with the Isar dependency.
v8 is much more stable than v7.
For more information about what this means, see .
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.
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:
To listen for progress events, you can listen to the Stream
of DownloadProgress
events returned by the startForeground()
method, which contains useful statistics about the download.
Listening can be done through any method, such as listen()
or the await for
loop.
Before using FMTC, ensure you comply with the appropriate rules and ToS set by your tile server. Failure to do so may lead to a permenant ban, or any other punishment.
This library and/or the creator(s) are not responsible for any violations you make using this package.
OpenStreetMap's can be : specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.
must be converted to
DownloadableRegions
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.
See this basic example:
By not storing pure tiles of sea, we can save a bunch of space on the user's device with every download. But how to do this?
Well, this package does it by analysing the bytes of the tile and checking if it's identical to a sample taken at lat/lng 0, 0 (Null Island) with zoom level 17. This tile should always be sea, and therefore any matching tile must also be sea.
In this way, we can delete tiles after we've checked them, if they are indeed sea. This method also means that tiles with ferry paths and other markings remain safe.
Sea Tile Removal does not reduce time or data consumption. Every tile must still be downloaded to check it.
Before downloading the region, you can count the number of tiles it will attempt to download. This is done by the check()
method.
The method takes the DownloadableRegion
generated above, and will return an int
number of tiles.
v7 was due to a package upgrading its version without following semantic versioning, meaning that the pub package resolver could never successfully resolve a working v7 package.
v8 contains all functionality from v7. Therefore, follow these migrations, then the instructions to upgrade to v8 (retaining the StoreManagement.migrator
) method.
v7 documentation is no longer available.
v6 and v7 have significantly different underlying storage systems, and therefore different APIs. Pre-v6 uses a multi-directory filesystem-based structure, whereas v7 uses a multi-database structure based on .
This page highlights the biggest changes made that will affect the most users - feature additions are not included. Smaller changes are described by in-code documentation or should be self explanatory.
Note that the code samples representing the new API do not always include all (new) properties, only those that have changed.
Don't forget to add and configure the migrator method (StoreManagement.migrator
) before publishing your app.
Due to the change in the underlying storage system, initialisation has changed to be asynchronous itself, meaning that the previous method of defining the rootDirectory
was now overly complicated. So the RootDirectory
system has also been simplified.
Some methods have been removed from store management due to limitations in Isar. The deprecation documentation in-code suggests replacements.
Changes have been made, which has improved cross-platform stability and performance. Some properties have changed: consult the documentation for more information. Migration should be self explanatory.
Validation options have been removed, as any store name (within UTF8) is now acceptable, because the limitations of the filesystem have been removed.
Some Isar database options have been added to manage the database files themselves. These have sensible defaults, although you should check them to make sure they fit your use-case.
persistedTiles
and persistedSize
now report the number of tiles and size that has actually been written to the database.
Handling collisions with existing stores during imports has gotten easier, with a built-in callback parameter.
These functionalities have been separated into their own modules, in order to simplify the installation of this package.
Methods and fields related to statistic caching have been removed, along with the underlying statistic cache system. This is because the new Isar databases are fast enough to calculate statistics, to the point where caching would likely result in reduced performance.
v8 brings major performance & stability improvements, along with support for Isar v3.1 and 'flutter_map' v4.
Some migrations are necessary for a small number of users. These migrations are listed below, theoretically in decreasing number of affected users.
The underlying storage structure is directly compatible with v7, and so migration for that is not required.
The module is required to use the import/export functionality.
See the instructions to add this module.
Return type is now ImportResult
This contains both the real store name (may be different to the filename) and whether the import was successful.
Collision handlers are now called with an additional argument The filename and real store name are now both passed to the collision handler. See .
FMTCInitialisationException
's fields have changed to be more useful in debugging initialisation issues, both internally and externally. If processing these issues manually, you'll need to migrate. See the in-code documentation for more information.
HttpClient
UsageFMTC now supports HTTP/2, through ! HTTP/2 support is enabled by default, with a fallback to HTTP/1.1 (both with a timeout of 5 seconds).
In many of the places where HttpClient
s where previously accepted as arguments, BaseClient
subtypes are now required. To continue using a custom HttpClient
, wrap it with an IOClient
.
behavior
:
See
When you've done this, you'll realise how great your app is with FMTC
With the introduction of for bulk downloading, there are now two additional statistics. The existing statistics successfulTiles
and successfulSize
will remain work, but with altered functionality: they now report the number of downloaded, not-necessarily persisted (still in buffer), tiles and size respectivley.
To add this functionality again, see and .
If you don't use this functionality in your app, you can undo the instructions related to these modules.
If you need to stop a bulk download early, you can use the cancel()
method to safely exit. No more tiles will be downloaded, and any tiles still within the buffer (see Buffering) will be written.
There is no support to pause downloads, nor is there planned support.
fmtc_plus_sharing Module
The fmtc_plus_sharing
module is required to use the import/export functionality.
See the #fmtc_plus_sharing-installation-and-setup instructions to add this module.
Note that some tile servers, such as Mapbox, forbid the sharing of their cached tiles, but this should still be acceptable as long as a user only imports their own exports (for example, backup purposes).
It is possible to export an entire store (including tiles and metadata) to a standalone file that can be easily shared and distributed between devices, then imported on any device.
For example, they can be used to create backup systems, sharing systems, or to distribute a preset package of tiles to all users without worrying about managing IO or managing assets!
fmtc_plus_background_downloading Module
The fmtc_plus_background_downloading
module is required to use the background bulk downloading functionality.
Note this functionality is only available on Android.
See the #fmtc_plus_background_downloading-installation-and-setup instructions to add this module.
You should read about the limitations and tradeoffs of background downloading before you start using it.
Before using FMTC, ensure you comply with the appropriate rules and ToS set by your tile server. Failure to do so may lead to a permenant ban, or any other punishment.
This library and/or the creator(s) are not responsible for any violations you make using this package.
OpenStreetMap's can be found here: specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.
You should also wrap your application's root widget (such as a Scaffold
) with the FMTCBackgroundDownload
widget.
This is designed to stop the app from terminating when it is taken off the widget tree, such as when the user closes the application. It is safe to leave there even when not downloading: it is intelligent enough to only keep the application alive if there is an ongoing background download.
This project is currently maintained by JaffaKetchup (Luka S). I am currently a maintainer of flutter_map, but this project has no other internal links.
Thanks to all contributors, and 'bugDim88' who originally came up with the idea and created a PR for flutter_map. When that PR was closed (it was decided a plugin would be more suitable), I took over the project. Also thanks to all of the 3rd party dependencies and their maintainers.
Please see for more information about why and how to sponsor me, for any amount you think is suitable
Many thanks to all my sponsors, not matter how much or how little they donated (in no particular order):
+ 3 anonymous or private donors
3rd party cookies are in use by default, for the purpose of internally tracking visits to this site. We use Google Analytics and GitBook's own built-in analytics to perform this. Data collected is kept confidential to the author of this site, and is only used for improvement/insight purposes.
The Google Analytics property in use is not connected to any Ad tracking/provider any more than by default. No ads are shown on this site.
Please get in touch if you have more questions!
fmtc_plus_sharing Module
Exporting a store copies the internal store database, 'compresses' it slightly, then renames it appropriately. The files are in the binary format that uses, so they cannot easily be read or modified.
They can have any file extension applied to them - '.fmtc' is used in the example application. The name of the file dictates the name of the store that will be used when importing (without the 'export_' prefix, if still present).
withGUI
)File
(manual
)Where issues originate in FMTC, I try my hardest to fix them quickly where possible, or provide workarounds if necessary.
Some problems will take longer than others to fix, so please be patient. If you think you can fix the issue, please get in touch and/or create a PR - contributions are always welcome!
Where they don't originate directly in FMTC, I consider donating or creating a bounty to get the bug resolved, if I can't help fix it myself. This money can only come from donations and alternative license payments to FMTC.
FMTC is currently somewhat unstable for applications with a wide public reach, due to some issues with the Isar dependency.
These issues are being worked on behind the scenes, but unfortunately, there is no planned release date for the v4, which is planned to include these fixes.
FMTC should behave correctly on the majority of devices, but it can cause fatal app crashes on some devices. v8 is much more stable than v7, as it depends on Isar 3.1.0.
If you're significantly concerned about stability, consider using the 'v6-backporting' branch instead of v8. v6 has significantly worse performance, and has some other major downsides, but is likely to be more stable across more platforms. The v6 is still available.
migrator
Migrate the incompatible file/directory structure of a previous version