Only this pageAll pages
Powered by GitBook
1 of 35

v8

Loading...

Loading...

Get Started

Loading...

Loading...

Loading...

Loading...

Usage

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Bulk Downloading

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Import & Export

Loading...

Loading...

Loading...

Migration

Loading...

Loading...

Loading...

Loading...

flutter_map_tile_caching

A plugin for 'flutter_map' providing advanced offline functionality

You're viewing documentation for an older version of FMTC (v8).

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.


Supporting Me

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.

Many thanks for any amount you can spare, it means a lot to me!

(Proprietary) Licensing

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.

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.

Get Help

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 the latest documentation, see .

For more information about what this means, see .

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 () - why do you think my username has "Jaffa" in it?

This project is released under GPL v3. For detailed information about this license, see . summarises the license with the following paragraph:

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 .

For bug reports & feature requests:

For implementation/general support: The #plugin channel on the

For other inquires:

https://en.wikipedia.org/wiki/Jaffa_Cakes
https://www.gnu.org/licenses/gpl-3.0.en.html
choosealicense.com
fmtc@jaffaketchup.dev
GitHub Issues
flutter_map Discord server
fmtc@jaffaketchup.dev

◉ 📲

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

◉ 💖

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.

Using one of our , 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!

When you've done this, you'll realise how great your app is with FMTC

😄
official extension modules

Is FMTC Right For Me?

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:

  • 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

Can you abide by the GPL v3 license, or do you need an alternative proprietary license? See for more information about this

If you're still not sure, please get in touch: . I'm always happy to offer guidance :)

(Proprietary) Licensing
Get Help

Quickstart

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.

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.

Console/Terminal
flutter pub add flutter_map_tile_caching
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';

Perform the startup procedure to allow usage of FMTC's APIs and connect to the underlying systems.

main.dart
import 'package:flutter/widgets.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';

Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();   
    await FlutterMapTileCaching.initialise();
    // ...
    // runApp(MyApp());
}

Create an isolated space to store tiles and other information to be accessed by the map and other methods.

main.dart
Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();   
    await FlutterMapTileCaching.initialise();
    await FMTC.instance('mapStore').manage.createAsync();
    // ...
    // runApp(MyApp());
}

Enable your FlutterMap widget to use the caching and underlying systems of FMTC.

import 'package:flutter_map/flutter_map.dart';

TileLayer(
    urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
    userAgentPackageName: 'com.example.app',
    tileProvider: FMTC.instance('mapStore').getTileProvider(),
    // Other parameters as normal
),

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.

Example Application

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.

Prebuilt Example Applications

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.

Android

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.

Windows

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.

Other Platforms

For other platforms, there are no prebuilt applications.

Additional Setup

flutter_map Installation & Setup

You must make sure you follow flutter_map's installation and additional setup instructions.

This module is only supported on Android.

To install this module, follow the Installation instructions for this package.

Background Processes

Add the following to 'android\app\src\main\AndroidManifest.xml' and any other manifests:

AndroidManifest.xml
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="packageName">
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
 <application android:label="appName" android:icon="appIcon">

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.

Notification Support

Background downloading needs to show notifications, which requires a 3rd party package. See it's installation/setup instructions:

To install this module, follow the Installation instructions for this package.

Android (11+)

Please follow these additional instructions for supporting Android versions above 11 and building for release:

iOS

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.

  • 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.

    Podfile
    Pod::PICKER_MEDIA = false
    Pod::PICKER_AUDIO = false
    Pod::PICKER_DOCUMENT = false
  • 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.

JaffaKetchup's list / FMTC ModulesGitHub
Sponsor @JaffaKetchup on GitHub SponsorsGitHub

OpenStreetMap's can be : specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.

1.

2.

3.

4.

You'll have to , open the 'example' directory, and then build for your desired platform using Dart and Flutter as normal.

Installation & Setup

Installation & Setup

Some developers may have issues when releasing the app or uploading to TestFlight - see for the first report of this problem. This is due to some of this library's dependencies on platform plugins.

found here
Install
Initialise
Connect to 'flutter_map'
clone this project
fmtc_plus_background_downloading
fmtc_plus_sharing
issue #69
Create a store

Using Roots & Stores

How It Works

Chaining

Once you can access FlutterMapTileCaching.instance after Initialisation, chaining of methods and accessors is used to access functionality.

  1. Base Chains are used as an intermediate step to access functionality on Roots and Stores

  2. Additional Chains are added to Base Chains to reach the actual functionality.

Base Chains

To get the Root, chain on rootDirectory (the name of the accessor is a remnant leftover from previous versions).

FlutterMapTileCaching.instance.rootDirectory;

To get a Store, there are two possible methods. FMTC does not use code generation, so store names are flexible, and so use Strings 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).

final store = FlutterMapTileCaching.instance('storeName');
await store.manage.create(); // Create the store 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.

final store = FlutterMapTileCaching.instance['storeName'];

Additional Chains

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

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 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 ).

Install

Isar
metadata
flutter_map Integration

manage

RootsStores

Control, modify, and configure an actual structure ('physical' directory or database) itself

stats

RootsStores

Retrieve statistics about a structure (Root or Store) itself

Isar

recovery

Roots

Recover failed bulk downloads, and prepare them to restart

import

RootsModule

Import and prepare a store from a previously exported archive file

download

Stores

Prepare, start, and manage a store's bulk downloads

metadata

Stores

A simple key-value pair store designed for storing simple, store related information

export

StoresModule

Export a store to an archive file, for future importing

Installation

Looking to start using FMTC in your project? Check out the Quickstart 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.

Depend On

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:

flutter pub add flutter_map_tile_caching
flutter pub add fmtc_plus_background_downloading # OPTIONAL
flutter pub add fmtc_plus_sharing # OPTIONAL

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:

pubspec.yaml
dependency_overrides:
    flutter_map_tile_caching:
        git:
            url: https://github.com/JaffaKetchup/flutter_map_tile_caching.git
    fmtc_plus_background_downloading: # OPTIONAL
        git:
            url: https://github.com/JaffaKetchup/fmtc_plus_background_downloading.git
    fmtc_plus_sharing: # OPTIONAL
        git:
            url: https://github.com/JaffaKetchup/fmtc_plus_sharing.git

Import

After installing the package, import it into the necessary files in your project:

import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:fmtc_plus_background_downloading/fmtc_plus_background_downloading.dart'; // OPTIONAL
import 'package:fmtc_plus_sharing/fmtc_plus_sharing.dart'; // OPTIONAL

For more information about what this means, see .

From

From

pub.dev
github.com

Statistics

Applies to Roots & Stores

FlutterMapTileCaching.instance.rootDirectory.stats; // Roots
FlutterMapTileCaching.instance('storeName').stats; // Stores

Watching For Changes

FMTC.instance.rootDirectory.stats.watchChanges(); // Roots
FMTC.instance('storeName').stats.watchChanges(); // Stores

It is possible to watch for changes in structures, which can be useful to create dynamic UIs (using StreamBuilders) that consume the other statistics. There are lots of customization options to increase efficiency and reduce junk/useless rebuilds.

Initialisation

FMTC is licensed under GPL-v3.

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.

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.

Initialisation Safety

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.

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.

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 .

OpenStreetMap's can be : specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.

Whilst it is (almost) impossible for one of the underlying databases to become corrupted during normal usage, or even due to a bug, it can happen if the database file is modified manually.

main.dart
import 'package:flutter/widgets.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';

Future<void> main() async {
       
    
    await FlutterMapTileCaching.initialise();
    // FMTC.instance;
    
    // Run your app and do all of that other stuff
}
https://github.com/JaffaKetchup/flutter_map_tile_caching/tree/main/prebuiltExampleApplicationsgithub.com
(Proprietary) Licensing
found here
Isar

Management

Applies to Roots & Stores

FlutterMapTileCaching.instance.rootDirectory.manage; // Roots
FlutterMapTileCaching.instance('storeName').manage; // Stores
Installationflutter_map Documentation
flutter_local_notifications | Flutter PackageDart packages
Setup · miguelpruivo/flutter_file_picker WikiGitHub

flutter_map Integration

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:

import 'package:flutter_map/flutter_map.dart';

TileLayer(
    // urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
    // userAgentPackageName: 'com.example.app',
    tileProvider: FMTC.instance('storeName').getTileProvider(),
    // Other parameters as normal
),

Tile Provider Settings

This method (and others) optionally take a FMTCTileProviderSettings. These configure the behaviour of the tile provider. Defaults to the settings specified in the Global Settings, or the package default (see table below) if that is not specified.

FMTCTileProviderSettings can take the following arguments:

Cache Behavior

This enumerable contains 3 values, which are used to dictate which logic should be used to store and retrieve tiles from the store.

Value
Explanation

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.

Obscuring Query Parameters

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:

https://tile.myserver.com/{z}/{x}/{y}?api_key=001239876&mode=dark
https://tile.myserver.com/{z}/{x}/{y}?&mode=dark

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.

Check If A Tile Is Cached

Parameter
Description
Default
Parameter
Description
Default
Parameter
Description
Default
Parameter
Description
Default

A backport of this functionality to v6 is also available - see , and install it through GitHub: From github.com.

this branch on GitHub

Metadata

Applies only to Stores

FlutterMapTileCaching.instance('storeName').metadata;

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

Add a new key-value pair to the store. For example:

    add(
        key: String,
        value: String,
    );

Read

Read all the key-value pairs from the store, and return them in a Map<String, String>. For example:

    read; // This is a getter

Remove

Remove a key-value pair from the store. For example:

    remove(key: String);

Reset

Remove all the key-value pairs from the store. For example:

    reset();

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>

See Obscuring Query Parameters

[]: empty

behavior:

CacheBehavior

Global Settings

FlutterMapTileCaching.settings;

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 calls can configure the Tile Provider Settings here, to reduce duplication and improve maintainability.

getTileProvider()
RootStats class - flutter_map_tile_caching library - Dart API
StoreStats class - flutter_map_tile_caching library - Dart API
watchChanges method - RootStats class - flutter_map_tile_caching library - Dart API

Recovery

Applies only to Roots

FlutterMapTileCaching.instance.rootDirectory.recovery;

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.

watchChanges method - StoreStats class - flutter_map_tile_caching library - Dart API
RootManagement class - flutter_map_tile_caching library - Dart API
StoreManagement class - flutter_map_tile_caching library - Dart API

Create A Region

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.

Types Of Region

All regions (before conversion to DownloadableRegion) implement BaseRegion.

Converting To Drawable Polygons

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.

The most basic type of region, defined by two North West and South East coordinates that create a LatLngBounds.

final region = RectangleRegion(
    LatLngBounds(
        LatLng(), // North West
        LatLng(), // South East
    ),
);

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).

final region = CircleRegion(
    LatLng(), // Center
    0, // KM Radius
);

If you have two coordinates, one center, and one on the edge of the circle you want, you can use method, as below:

final region = CircleRegion(
    LatLng(), // Center
    const Distance(roundResult: false).distance(
        LatLng(), // Center
        LatLng(), // Edge Coord
    ) / 1000; // Convert to KM
);

The most advanced type of region, defined by a list of coordinates and radius (in meters).

final region = LineRegion(
    [LatLng(), LatLng(), ...], // Series of coordinates
    0, // M Radius
);

After you've created your region, you can convert it to a drawable polygon (below), or ready for downloading.

'latlong2's Distance.distance()
convert it to a DownloadableRegion

Start In Foreground

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.

FMTC.instance('storeName').download.startForeground();
checkTileCached method - FMTCTileProvider class - flutter_map_tile_caching library - Dart API

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 Listen For Progress, even if you do not plan to use the .

found here

Introduction

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.

4 Steps To Downloading

Available APIs

All APIs listed in this section are children of the download getter.

API Member
Explanation

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

Recovery

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:

OpenStreetMap's can be : specifically bulk downloading is discouraged, and forbidden after zoom level 13. Other servers may have different terms.

Start downloading that region, either in the or

found here
Create a region based on the user's input
Convert that region into a downloadable region
foreground
background
Listen for progress events to update your user
Recovery
startForeground()
startBackground()
cancel()
check()

Prepare For Downloading

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.

These contain the original BaseRegion, but also some other information necessary for downloading, such as zoom levels and URL templates.

See this basic example:

final downloadable = region.toDownloadable(
    1, // Minimum Zoom
    18, // Maximum Zoom
    TileLayer(
        // Use the same `TileLayer` as in the displaying map, but omit the `tileProvider`
        urlTemplate: 'https://api.mapbox.com/styles/v1/jaffaketchup/cle0ehaiz00j101qqr14f8mm3/tiles/256/{z}/{x}/{y}@2x',
        userAgentPackageName: 'com.example.app',
    ),
    // Additional parameters if necessary
),

Additional Parameters

Sea Tile Removal

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.

Checking Number Of Tiles

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.

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.

found here
BaseRegions
startForeground method - DownloadManagement class - flutter_map_tile_caching library - Dart API

Start In Background

fmtc_plus_background_downloading Module

Note this functionality is only available on Android.

See the fmtc_plus_background_downloading Installation & Setup instructions to add this module.

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.

FMTC.instance('storeName').download.startBackground();

Available Parameters

Additional Preparation

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.

The module is required to use the background bulk downloading functionality.

You should read about the before you start using it.

OpenStreetMap's can be : 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 widget.

fmtc_plus_background_downloading
limitations and tradeoffs of background downloading
found here
FMTCBackgroundDownload
#available-statistics

Listen For Progress

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.

toDownloadable method - BaseRegion class - flutter_map_tile_caching library - Dart API

Cancel Download

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.

startBackground method - FMTCBackgroundDownloadingModule extension - fmtc_plus_background_downloading library - Dart API

Importing

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.

With Platform GUI (withGUI)

With A Known File (manual)

Collision/Conflict Resolution

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

Introduction

fmtc_plus_sharing Module

See the fmtc_plus_sharing Installation & 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).

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!

The module is required to use the import/export functionality.

It is possible to an entire store (including tiles and metadata) to a standalone file that can be easily shared and distributed between devices, then on any device.

fmtc_plus_sharing
export
imported

Limitations

fmtc_plus_background_downloading Module

Android Only

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.

Background Processes

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.

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.

Recovery Effectiveness

The effectiveness of the Recovery 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.

Progress Events

Unlike foreground downloading, where you can Listen For Progress, 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 #available-statistics).

No Buffering Support

Buffering is not supported by background downloads.

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.

dontkillmyapp.com
DownloadProgress class - flutter_map_tile_caching library - Dart API
withGUI method - FMTCImportSharingModule extension - fmtc_plus_sharing library - Dart API
manual method - FMTCImportSharingModule extension - fmtc_plus_sharing library - Dart API

v7 -> v8 Migration

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.

Importing

See the fmtc_plus_sharing Installation & Setup 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 Collision/Conflict Resolution.

Initialisation

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.

Custom HttpClient Usage

In many of the places where HttpClients where previously accepted as arguments, BaseClient subtypes are now required. To continue using a custom HttpClient, wrap it with an IOClient.

The module is required to use the import/export functionality.

FMTC 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).

fmtc_plus_sharing
'http_plus'

Credits

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.

Sponsors

Please see Supporting Me 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

Privacy & Cookie Policy

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!

@tonyshkurenko
@Mmisiek
@huulbaek
@andrewames
@ozzy1873
@eidolonFIRE
@weishuhn
@mohammedX6
Isar Stability Issues
Isar Stability Issues

Known Issues

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!

Isar Stability Issues

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.

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 and payments to FMTC.

If you're significantly concerned about stability, consider using the '' 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.

donations
alternative license

Buffering

Available since v7

Buffering reduces the total download time, at the expense of increased memory usage.

Without Buffering

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.

With Buffering

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.

Using Buffering

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)

v6-backporting

Exporting

fmtc_plus_sharing Module

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).

With Platform GUI (withGUI)

With A Known File (manual)

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.

Isar
Logo
Logo
Issues · JaffaKetchup/flutter_map_tile_cachingGitHub
v9
Logo
Logo
withGUI method - FMTCExportSharingModule extension - fmtc_plus_sharing library - Dart API
manual method - FMTCExportSharingModule extension - fmtc_plus_sharing library - Dart API
Logo
initialise method - FlutterMapTileCaching class - flutter_map_tile_caching library - Dart API
[BUG] `MDBX_PANIC` When Opening Corrupted Database · Issue #1011 · isar/isarGitHub
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
FMTCSettings class - flutter_map_tile_caching library - Dart API
RootRecovery class - flutter_map_tile_caching library - Dart API

migrator

Roots

Migrate the incompatible file/directory structure of a previous version

Logo

v6 -> v7 Migration

v8 contains all functionality from v7. Therefore, follow these migrations, then the v7 -> v8 Migration instructions to upgrade to v8 (retaining the StoreManagement.migrator) method.

v7 documentation is no longer available.

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.

Changes

Initialisation & Root Directory System

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.

FlutterMapTileCaching.initialise(
    await RootDirectory.normalCache,
    // ...
);
final bool isReady = await FMTC.instance.rootDirectory.manage.ready;
await FlutterMapTileCaching.initialise(
    rootDirectory: null, // Optional for most applications
    // ...
);
// Safely assume that the root directory is ready, until `.manage.delete()` has been called

Store Management

Some methods have been removed from store management due to limitations in Isar. The deprecation documentation in-code suggests replacements.

Statistic Watching

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.

Global Settings

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.

Download Progress

With the introduction of Buffering 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.

persistedTiles and persistedSize now report the number of tiles and size that has actually been written to the database.

Import Collision Handling

Handling collisions with existing stores during imports has gotten easier, with a built-in callback parameter.

Removals

Import/Export & Background Downloading Functionality From Base

These functionalities have been separated into their own modules, in order to simplify the installation of this package.

To add this functionality again, see Installation and Additional Setup.

If you don't use this functionality in your app, you can undo the Additional Setup instructions related to these modules.

Statistic Caching

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.

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.

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 .

left in an broken state
Isar
Logo
documentation
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo