Map Download
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.
![]() | ![]() |
---|---|
Initial map view | Downloadable 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(),
));
}
}
![]() | ![]() |
---|---|
Map downloading | Downloaded 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
ContentStoreItem
s) - 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.