Learn how to download and display an offline map for a user-defined geographical area of a web map.

Offline maps allow users to continue working when network connectivity is poor or lost. If a web map is enabled for offline use, a user can request that ArcGIS generates an offline map for a specified geographic area of interest.
In this tutorial, you will download an offline map for an area of interest from the web map of the stormwater network within Naperville, IL, USA . You can then use this offline map without a network connection.
Prerequisites
Before starting this tutorial:
-
You need an ArcGIS Location Platform or ArcGIS Online account.
-
Your system meets the system requirements.
-
You need an IDE for Flutter - we recommend VS Code.
Develop or download
You have two options for completing this tutorial:
Option 1: Develop the code
To start the tutorial, complete the Display a map tutorial. This creates a map to display the Santa Monica Mountains in California using the topographic basemap from the ArcGIS Basemap Styles service.
Open Flutter project
-
Open the project you created by completing the Display a map tutorial.
-
Continue with the following instructions to download and display an offline map for a user-defined geographical area of a web map.
Import additional library and package
As part of the offline workflow, you will need to store a downloaded map to your device. You will add the dart
library to gain access to an API to deal with files and directories and the path
package to access local files on a device.
-
In VS Code, double-click
lib\main.dart
to open the file. -
From the menu bar, select View > Terminal to open a new terminal.
-
Add the
path
package to the project as a dependency running the following command:_provider Use dark colors for code blocks Copy flutter pub add path_provider
The
path
package provides a Flutter plugin for finding commonly used locations on the file system. You can learn more about this package on pub.dev._provider -
Import
path
and_provider dart.io
intomain.dart
.main.dartUse dark colors for code blocks import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:flutter/material.dart'; import 'package:arcgis_maps/arcgis_maps.dart'; void main() {
The
dart
library provides API to deal with files and directories. You can read more about the library here.:io
Get the web map item ID
You can use ArcGIS tools to create and view web maps. Use the Map Viewer to identify the web map item ID. This item ID will be used later in the tutorial.
- Go to the Naperville water network in the Map Viewer in ArcGIS Online. This web map displays stormwater network within Naperville, IL, USA .
- Make a note of the item ID at the end of the browser's URL. The item ID should be:
acc027394bc84c2fb04d1ed317aac674
Display the web map
You can display a web map using the web map's item ID. Create a map from the web map portal item, and display it in your app.
-
Replace all the code contained in
on
to instead create a portal item using the item ID from the ArcGIS Online web map.Map View Ready() main.dartUse dark colors for code blocks void onMapViewReady() { // Create an ArcGIS Online portal item using the item ID. final portalItem = PortalItem.withPortalAndItemId( portal: Portal.arcGISOnline(), itemId: 'acc027394bc84c2fb04d1ed317aac674', ); }
-
Define a final
map
variable and set its value to an instance ofArcGISMap
using thePortalItem
.main.dart > onMapViewReady()Use dark colors for code blocks // Create an ArcGIS Online portal item using the item ID. final portalItem = PortalItem.withPortalAndItemId( portal: Portal.arcGISOnline(), itemId: 'acc027394bc84c2fb04d1ed317aac674', ); // Define an ArcGIS map variable to hold on to the web map. final map = ArcGISMap.withItem(portalItem);
-
Set the
map
to theArcGISMapViewController
'sarcGISMap
property.main.dart > onMapViewReady()Use dark colors for code blocks // Define an ArcGIS map variable to hold on to the web map. final map = ArcGISMap.withItem(portalItem); // Set the map to the map view controller's ArcGIS map property. _mapViewController.arcGISMap = map;
-
Make sure you have an Android emulator, iOS simulator or physical device configured and running.
-
In VS Code, select Run > Run Without Debugging.
You should see a map of the stormwater network within Naperville, IL, USA. Pinch, drag, and double-tap the map view to explore the map. Leave the application running while you move into the next section to continue with the tutorial.
Specify an area of the web map to take offline
To specify an area of the web map to take offline, you will use an Envelope
that will be constructed using the EnvelopeBuilder
. You will then display the area on the map using a Graphic
.
-
Create an envelope builder and set its spatial reference to match the web map.
main.dart > onMapViewReady()Use dark colors for code blocks // Set the map to the map view controller's ArcGIS map property. _mapViewController.arcGISMap = map; // Create an envelope builder. final envelopeBuilder = EnvelopeBuilder( // Set the spatial reference. spatialReference: SpatialReference.wgs84, );
-
Use the envelope builder to define an area to take offline.
Geometry builders in the Flutter Maps SDK are useful for constructing geometries in a step-by-step fashion. Although the values are provided in the tutorial, you could use the same builder with user input to allow for a more dynamic area to be defined.
main.dart > onMapViewReady()Use dark colors for code blocks // Create an envelope builder. final envelopeBuilder = EnvelopeBuilder( // Set the spatial reference. spatialReference: SpatialReference.wgs84, ); // Add the lower left (Xmin, Ymin) and upper right (Xmax, Ymax) // coordinates to define the area to take offline. envelopeBuilder.xMin = -88.1526; envelopeBuilder.xMax = -88.1490; envelopeBuilder.yMin = 41.7694; envelopeBuilder.yMax = 41.7714; // Retrieve the new geometry from the builder. final offlineArea = envelopeBuilder.toGeometry();
-
Display a graphic of the area to take offline.
Use
SimpleLineSymbol
andSimpleFillSymbol
to display a newGraphic
of theoffline
with a red outline. Add the graphic to a newArea GraphicsOverlay
and add the new overlay to the_map
property to display it in the map view.View Controller.graphics Overlays main.dart > onMapViewReady()Use dark colors for code blocks // Retrieve the new geometry from the builder. final offlineArea = envelopeBuilder.toGeometry(); // Define a red line symbol to act as the outline // for the area of interest. final lineSymbol = SimpleLineSymbol( style: SimpleLineSymbolStyle.solid, color: Colors.red, width: 2, ); // Define a fill symbol that is transparent, setting the // outline to the line symbol. final fillSymbol = SimpleFillSymbol( style: SimpleFillSymbolStyle.solid, color: Colors.transparent, outline: lineSymbol, ); // Create a graphic for the area of interest using the // built geometry and symbol. final offlineAreaGraphic = Graphic( geometry: offlineArea, symbol: fillSymbol, ); // Create a new graphics overlay. final areaOverlay = GraphicsOverlay(); // Add the graphic to the graphics overlay. areaOverlay.graphics.add(offlineAreaGraphic); // Add the graphics overlay to the map view controller. _mapViewController.graphicsOverlays.add(areaOverlay);
-
Use Flutter's hot restart to load your code changes and restart the app.
You should see a red outline on the stormwater network within Naperville, IL, USA. This indicates the area of the web map that you are going to take offline.
Download and display the offline map
You can generate and download an offline map for an area of interest using an asynchronous task. When complete, it will provide the offline map for display in your map view.
-
Create an
OfflineMapTask
using the online map.main.dart > onMapViewReady()Use dark colors for code blocks // Add the graphics overlay to the map view controller. _mapViewController.graphicsOverlays.add(areaOverlay); // Create an offline map task for the map. final offlineMapTask = OfflineMapTask.withOnlineMap(map);
-
In
on
, change the method signature to denote the method as asynchronous.Map View Ready() main.dartUse dark colors for code blocks void onMapViewReady() async {
-
Get default parameters to generate and download the offline map. Modify them to download a read-only offline map.
This tutorial does not involve editing and updating the contents of the offline map. When an offline map is editable, metadata is stored in ArcGIS to track and synchronize edits. Setting the
GenerateOfflineMapUpdateMode
tono
avoids the overhead of maintaining this metadata in ArcGIS.Updates main.dart > onMapViewReady()Use dark colors for code blocks // Create an offline map task for the map. final offlineMapTask = OfflineMapTask.withOnlineMap(map); // Create parameters specifying the region to take offline // using the graphic's geometry. Set the update mode to // no updates to avoid the overhead of maintaining the // metadata required when synchronizing edits. final parameters = await offlineMapTask .createDefaultGenerateOfflineMapParameters( areaOfInterest: offlineArea, ) ..updateMode = GenerateOfflineMapUpdateMode.noUpdates;
-
Set a download location for the offline map.
main.dart > onMapViewReady()Use dark colors for code blocks // Create parameters specifying the region to take offline // using the graphic's geometry. Set the update mode to // no updates to avoid the overhead of maintaining the // metadata required when synchronizing edits. final parameters = await offlineMapTask .createDefaultGenerateOfflineMapParameters( areaOfInterest: offlineArea, ) ..updateMode = GenerateOfflineMapUpdateMode.noUpdates; // Prepare an empty directory to store the offline map. final documentsUri = (await getApplicationDocumentsDirectory()).uri; final downloadDirectoryUri = documentsUri.resolve('offline_map'); // If the download directory already exists we delete. // If this is undesirable, use a unique identifier for the downloadDirectoryUri // such as appending a timestamp. if (Directory.fromUri(downloadDirectoryUri).existsSync()) { Directory.fromUri(downloadDirectoryUri).deleteSync(recursive: true); } Directory.fromUri(downloadDirectoryUri).createSync();
-
Create a new
GenerateOfflineMapJob
using theparameters
anddownload
.Directory Uri main.dart > onMapViewReady()Use dark colors for code blocks // Prepare an empty directory to store the offline map. final documentsUri = (await getApplicationDocumentsDirectory()).uri; final downloadDirectoryUri = documentsUri.resolve('offline_map'); // If the download directory already exists we delete. // If this is undesirable, use a unique identifier for the downloadDirectoryUri // such as appending a timestamp. if (Directory.fromUri(downloadDirectoryUri).existsSync()) { Directory.fromUri(downloadDirectoryUri).deleteSync(recursive: true); } Directory.fromUri(downloadDirectoryUri).createSync(); // Create a job to generate the offline map passing in // the parameters and download directory. final generateOfflineMapJob = offlineMapTask.generateOfflineMap( parameters: parameters, downloadDirectoryUri: downloadDirectoryUri, );
-
Add code to listen for when the generate offline map job completes or fails and to track the percent complete as the job runs. Add a
try/catch
block to handle exceptions.main.dart > onMapViewReady()Use dark colors for code blocks // Create a job to generate the offline map passing in // the parameters and download directory. final generateOfflineMapJob = offlineMapTask.generateOfflineMap( parameters: parameters, downloadDirectoryUri: downloadDirectoryUri, ); // Listen for status updates as the generate offline map // job runs. generateOfflineMapJob.onStatusChanged.listen((jobStatus) { try { // See next step. } on ArcGISException catch (e) { debugPrint('Error generating offline map: ${e.message}'); } });
-
If the job succeeds, set the
arcgis
property on the map view controller with the offline map result. If it fails, print out a message. If the job is running, print the percent complete.Map Instead of printing messages to the debug console as demonstrated, you could respond to the change in the job status by displaying widgets in the user interface. For example, the Generate offline map sample shows the download progression using the LinearProgressIndicator widget.
main.dart > onMapViewReady() > try{}Use dark colors for code blocks // Listen for status updates as the generate offline map // job runs. generateOfflineMapJob.onStatusChanged.listen((jobStatus) { try { // If the job succeeds, show the offline map. if (generateOfflineMapJob.status == JobStatus.succeeded) { // Retrieve the offline map. final result = generateOfflineMapJob.result; if (result == null) return; // Set the map view controller's ArcGIS map to the offline map. _mapViewController.arcGISMap = result.offlineMap; debugPrint('Generate offline map: Complete'); } else if (generateOfflineMapJob.status == JobStatus.failed) { // If the job fails, print out the error message. debugPrint( 'Unable to generate a map for that area: ${generateOfflineMapJob.error?.message}', ); } else { // Otherwise, get the progress and report it. final percentComplete = generateOfflineMapJob.progress; debugPrint('Percent complete: $percentComplete%'); } } on ArcGISException catch (e) { debugPrint('Error generating offline map: ${e.message}'); } });
-
Start the generate offline map job.
main.dart > onMapViewReady()Use dark colors for code blocks } on ArcGISException catch (e) { debugPrint('Error generating offline map: ${e.message}'); } }); // Start the generate offline map job. generateOfflineMapJob.start(); }
-
Hot restart your app.
You should see an offline map for the specified area of the stormwater network within Naperville, IL, USA. Remove your network connection and you will still be able to pinch, drag, and double-tab the map view to explore this offline map.
Alternatively, you can download the tutorial solution, as follows.
Option 2: Download the solution
-
Click the
Download solution
link underSolution
and unzip the file to a location on your machine. -
Open the project in VS code.
Set developer credentials in the solution
To allow your app users to access ArcGIS location services, use the developer credentials that you created in the Set up authentication step to authenticate requests for resources.
-
In VS Code, open
lib/main.dart
. -
In the
main()
function, set theArcGISEnvironment.apiKey
value to your access token.main.dartUse dark colors for code blocks void main() { ArcGISEnvironment.apiKey = 'YOUR_ACCESS_TOKEN'; runApp(const MainApp()); }
Best Practice: The access token is stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Run the application
Follow these steps to run the application.
-
In VS Code's terminal, run:
Use dark colors for code blocks Copy flutter pub upgrade
-
Run:
Use dark colors for code blocks Copy dart run arcgis_maps install
-
Make sure you have an Android emulator, iOS simulator or physical device configured and running.
-
In VS Code, select Run > Run Without Debugging.
You should see an offline map for the specified area of the stormwater network within Naperville, IL, USA. Remove your network connection and you will still be able to pinch, drag, and double-tab the map view to explore this offline map.