Skip to main content
GuidesAPI ReferenceExamplesFAQ

Map Download

Estimated reading time: 5 minutes

This example demonstrates how to list the road maps available on the server for download, how to download a map while indicating the download progress, and how to display the download’s finished status.

How it works

This example demonstrates the following features:

  • List road maps available on the server for download.
  • Download a map while displaying download progress.
  • Indicate when the download is finished.
hello_maphello_map
Initial map viewDownloadable Maps list

UI and Map Integration

This code defines the main app structure and how it handles the map’s initialization and the navigation to the map download page.

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});


State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

void dispose() {
GemKit.release();
super.dispose();
}


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[900],
title: const Text(
'Map Download',
style: TextStyle(color: Colors.white),
),
actions: [
IconButton(
onPressed: () => _onMapButtonTap(context),
icon: const Icon(
Icons.map_outlined,
color: Colors.white,
),
),
],
),
body: GemMap(
key: ValueKey("GemMap"),
onMapCreated: _onMapCreated,
appAuthorization: projectApiToken,
),
);
}

void _onMapCreated(GemMapController controller) async {
SdkSettings.setAllowOffboardServiceOnExtraChargedNetwork(
ServiceGroupType.contentService, true);
}

void _onMapButtonTap(BuildContext context) async {
Navigator.of(context).push(MaterialPageRoute<dynamic>(
builder: (context) => const MapsPage(),
));
}
}
hello_maphello_map
Map downloadingDownloaded map

Maps page

The MapsPage widget fetches the list of maps available for download and displays them in a scrollable list.

import 'package:gem_kit/content_store.dart';

import 'maps_item.dart';
import 'utils.dart';

import 'package:flutter/material.dart';

class MapsPage extends StatefulWidget {
const MapsPage({super.key});


State<MapsPage> createState() => _MapsPageState();
}

class _MapsPageState extends State<MapsPage> {
final mapsList = <ContentStoreItem>[];


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
foregroundColor: Colors.white,
title: const Text(
"Maps List",
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.deepPurple[900],
),
body: FutureBuilder<List<ContentStoreItem>>(
future: getMaps(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const Center(child: CircularProgressIndicator());
}
return Scrollbar(
child: ListView.separated(
padding: EdgeInsets.zero,
itemCount: snapshot.data!.length,
separatorBuilder: (context, index) =>
const Divider(indent: 50, height: 0),
itemBuilder: (context, index) {
final mapItem = snapshot.data!.elementAt(index);
return MapsItem(mapItem: mapItem);
},
),
);
},
),
);
}
}

Map item

The MapsItem widget represents each map item in the list, allowing users to download or delete maps and see the download progress. The widget also allows pausing and resuming downloads.

class MapsItem extends StatefulWidget {
final ContentStoreItem mapItem;

const MapsItem({super.key, required this.mapItem});


State<MapsItem> createState() => _MapsItemState();
}

class _MapsItemState extends State<MapsItem> {
ContentStoreItem get mapItem => widget.mapItem;


void initState() {
super.initState();

mapItem.restartDownloadIfNecessary(
_onMapDownloadFinished,
onProgressCallback: _onMapDownloadProgressUpdated,
);
}


Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: ListTile(
onTap: _onTileTap,
leading: Container(
padding: const EdgeInsets.all(8),
width: 50,
child: mapItem.imgPreview.isValid
? Image.memory(mapItem.imgPreview.getRenderableImageBytes(format: ImageFileFormat.png)!)
: SizedBox(),
),
title: Text(
mapItem.name,
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
subtitle: Text(
"${(mapItem.totalSize / (1024.0 * 1024.0)).toStringAsFixed(2)} MB",
style: const TextStyle(color: Colors.black, fontSize: 16),
),
trailing: SizedBox.square(
dimension: 50,
child: Builder(
builder: (context) {
if (mapItem.isCompleted) {
return const Icon(Icons.download_done, color: Colors.green);
} else if (mapItem.isDownloadingOrWaiting) {
return SizedBox(
height: 10,
child: CircularProgressIndicator(
value: mapItem.downloadProgress / 100.0,
color: Colors.blue,
backgroundColor: Colors.grey.shade300,
),
);
} else if (mapItem.status == ContentStoreItemStatus.paused) {
return const Icon(Icons.pause);
}
return const SizedBox.shrink();
},
),
),
),
),
if (mapItem.isCompleted)
IconButton(
onPressed: () {
if (mapItem.deleteContent() == GemError.success) {
setState(() {});
}
},
padding: EdgeInsets.zero,
icon: const Icon(Icons.delete),
),
],
);
}

void _onTileTap() {
if (!mapItem.isCompleted) {
if (mapItem.isDownloadingOrWaiting) {
_pauseDownload();
} else {
_downloadMap();
}
}
}

void _downloadMap() {
// Download the map.
mapItem.asyncDownload(
_onMapDownloadFinished,
onProgressCallback: _onMapDownloadProgressUpdated,
allowChargedNetworks: true,
);
}

void _pauseDownload() {
// Pause the download.
mapItem.pauseDownload();
setState(() {});
}

void _onMapDownloadProgressUpdated(int progress) {
if (mounted) {
setState(() {});
}
}

void _onMapDownloadFinished(GemError err) {
// If there is no error, we change the state
if (mounted && err == GemError.success) {
setState(() {});
}
}
}

Util functions

In the file utils.dart there are some helping functions and extensions for:

  • getting the list of online maps
  • pause and restart a download (a trick to re-wire the UI logic to the ContentStoreItems) - Important to mention is that restarting the download is only made after the item is completely paused.
extension ContentStoreItemExtension on ContentStoreItem {
bool get isDownloadingOrWaiting => [
ContentStoreItemStatus.downloadQueued,
ContentStoreItemStatus.downloadRunning,
ContentStoreItemStatus.downloadWaitingNetwork,
ContentStoreItemStatus.downloadWaitingFreeNetwork,
ContentStoreItemStatus.downloadWaitingNetwork,
].contains(status);

// Method that returns the image of a map
Uint8List? get image => MapDetails.getCountryFlag(
countryCode: countryCodes[0],
size: const Size(100, 100),
);

void restartDownloadIfNecessary(
void Function(GemError err) onCompleteCallback,
{void Function(int progress)? onProgressCallback}) {
//If the map is downloading pause and start downloading again
//so the progress indicator updates value from callback
if (isDownloadingOrWaiting) {
_pauseAndRestartDownload(
onCompleteCallback,
onProgressCallback: onProgressCallback,
);
}
}

void _pauseAndRestartDownload(void Function(GemError err) onCompleteCallback,
{void Function(int progress)? onProgressCallback}) {
final errCode = pauseDownload(onComplete: (err) {
if (err == GemError.success) {
// Download the map.
asyncDownload(
onCompleteCallback,
onProgressCallback: onProgressCallback,
allowChargedNetworks: true,
);
} else {
print("Download pause for item $id failed with code $err");
}
});

if (errCode != GemError.success) {
print("Download pause for item $id failed with code $errCode");
}
}
}

// Method to load the maps
Future<List<ContentStoreItem>> getMaps() async {
final mapsListCompleter = Completer<List<ContentStoreItem>>();

ContentStore.asyncGetStoreContentList(ContentType.roadMap, (
err,
items,
isCached,
) {
if (err == GemError.success) {
mapsListCompleter.complete(items);
} else {
mapsListCompleter.complete([]);
}
});

return mapsListCompleter.future;
}

Flutter Examples

Maps SDK for Flutter Examples can be downloaded or cloned with Git.