Offline maps for your Flutter app

Create a new Flutter application

Install Flutter by following the instructions and create an application to verify that your development environment and workspace are setup properly before continuing.

$ flutter create map_app
$ cd map_app
$ flutter run

Register for a MapBox API token

Next we need to register for a MapBox API token. When you sign up you will get a default token which will work for our purposes. The API token is how MapBox tracks your usage and bills you, remember to review the API token settings before launching your application.

Add MapBox to your Flutter application

Add mapbox_gl to your pubspec.yaml file under the dependencies section. In order to get the latest code we will use the source instead of the dart package.

$ cd map_app
$ git clone https://github.com/tobrun/flutter-mapbox-gl.git
dependencies:
...
mapbox_gl:
path: ./flutter-mapbox-gl
$ flutter pub get

Android Changes

Add your API token to the AndroidManifest

  • android/app/src/main/AndroidManifest.xml
<application
android:name="io.flutter.app.FlutterApplication"
android:label="map_app"
android:icon="@mipmap/ic_launcher">
<meta-data android:name="com.mapbox.token"
android:value="YOUR API KEY" />
<activity
...
</application>
  • You will also need to migrate the ./android directory to use androidx by adding the following two lines to ./android/gradle.properties
android.useAndroidX=true
android.enableJetifier=true

iOS Changes

  • ios/Runner/Info.plist
<plist version="1.0">
<dict>
...
<key>MGLMapboxAccessToken</key>
<string>YOUR API KEY</string
<key>io.flutter.embedded_views_preview</key>
<true/>

</dict>
</plist>
  • ios/Podfile
# Uncomment this line to define a global platform for your project
platform :ios, '9.0'
use_frameworks!

The Code

Test Run

We should now be able to run our application and see our map.

flutter run

Downloading Map data at build time.

To support offline maps we need to download the required Map tiles at build time and include them in our app. Mapbox has instructions on how to do this here — but the tl;dr version is below.

Build MapBox tools

$ git clone https://github.com/mapbox/mapbox-gl-native.git
$ cd mapbox-gl-native
$ make

Downloading the map tiles

In order to download all the required map tiles you will need the bounding box of the latitude/longitude you wish to cover as well as the supported zoom levels.

$ ./build/macos/Debug/mbgl-offline \
--north 40.8365 \
--west -119.267 \
--south 40.7413 \
--east -119.1465 \
--minZoom 12 \
--maxZoom 18 \
--style mapbox://styles/mapbox/streets-v11 \
--token 'OBTAINED FROM YOUR MAPBOX.COM ACCOUNT' \
--output mapcache.db

Adding the tiles

With the map tiles downloaded we add them to our assets directory. The db files needs to be named cache.db.

$ mkdir -p map_app/assets/
$ cp mapbox-gl-native/mapcache.db map_app/assets/cache.db
assets:
- assets/cache.db

Copy tiles into place

In order for the mapview to use the cache.db we have to copy into the expected location. Furthermore to prevent the mapview from accessing the network completely we need to make sure the cache.db has been copied to the expected location before the mapview is ever loaded. This usually means we copy the cache.db into place during a “loading” screen.

import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
...class _MapWidgetState extends State<MapWidget> {
...
var _tilesLoaded = false;
...
@override
initState() {
super.initState();
_copyTilesIntoPlace();
}
_copyTilesIntoPlace() async {
try {
await installOfflineMapTiles(join("assets", "cache.db"));
} catch (err) {
print(err);
}
setState(() {
this._tilesLoaded = true;
});
}
... @override
Widget build(BuildContext context) {
if (this._tilesLoaded) {
return Container(
child: _buildMapBox(context),
);
} else {
return Center(
child: new CircularProgressIndicator(),
);
}
}

Run in Airplane mode

If we have done everything correctly, when we run the application in Airplane mode we can see that the map still loads.

Conclusion

With a little bit of research and the MapBox SDK we can now include offline map support in our applications for both iOS and Android.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store