Skip to content

Map Update

In this guide, you will learn how to download and apply updates to maps.
The assets directory includes a world map file and a map for Andorra that are used in this example.

map_update - example flutter screenshot

Setup

First, get an API key token, see the Getting Started guide.

Prerequisites

Make sure you completed the Environment Setup - Flutter Examples guide before starting this guide.

Build and Run

Start a terminal/command prompt and navigate to the map_update directory within the Flutter examples directory. This is the name of the example project.

Note - the gem_kit directory containing the Maps SDK for Flutter should be in the plugins directory of the example, e.g. example_pathname/plugins/gem_kit - see the environment setup guide above.

Run: flutter pub get

Configure the native parts:

First, verify that the ANDROID_SDK_ROOT environment variable is set to the root path of your android SDK.

In android/build.gradle add the maven block as shown, within the allprojects block, for both debug and release builds:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven {
           url "${rootDir}/../plugins/gem_kit/android/build"
        }
    }
}

in android/app/build.gradle within the android block, in the defaultConfig block, the android SDK version minSdk must be set as shown below.

Additionally, for release builds, in android/app/build.gradle, within the android block, add the buildTypes block as shown:

Replace example_pathname with the actual project pathname

android {
    defaultConfig {
        applicationId "com.magiclane.gem_kit.examples.example_pathname"
        minSdk 21
        targetSdk flutter.targetSdk
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    buildTypes {
        release {
            minifyEnabled false
            shrinkResources false

            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

In the ios/Podfile configuration file, at the top, set the minimum ios platform version to 14 like this:

platform :ios, '14.0'

We recommend you to run these commands after you copy the gem_kit into your project: |flutter clean |flutter pub get |and |cd ios |pod install

Then run the project:

flutter run --debug
or
flutter run --release

App entry and initialization

const projectApiToken = String.fromEnvironment('GEM_TOKEN');

void main() {
  runApp(const MyApp());
}

This code initializes the projectApiToken with the required authorization token and launches the app.

How it Works

This example demonstrates the following features:

  • Replace the default map with an older version from the app’s assets to simulate a map update.

  • Apply the necessary logic to update the map to the latest version.

  • Manage and integrate map update functionality seamlessly within the app’s UI.

UI and Map Integration

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Map Update',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

map_update - example flutter screenshot

map_update - example flutter screenshot

map_update - example flutter screenshot

map_update - example flutter screenshot

Handle the map update logic and UI

Within _MyHomePageState, define the necessary state variables and methods to interact with the map and manage updates.

 class _MyHomePageState extends State<MyHomePage> {
int? mapId;

void onMapCreated(GemMapController controller) async {
  mapId = controller.mapId;
}

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

bool showButton = true;

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      backgroundColor: Colors.deepPurple[900],
      title: const Text(
        'Map Update',
        style: TextStyle(color: Colors.white),
      ),
      actions: [
        IconButton(
            onPressed: () => _onMapButtonTap(context),
            icon: const Icon(
              Icons.map_outlined,
              color: Colors.white,
            ))
      ],
    ),
    body: GemMap(
      onMapCreated: onMapCreated,
    ),
    floatingActionButton: showButton
        ? FloatingActionButton(
            onPressed: loadMaps,
            child: const Icon(Icons.file_copy),
          )
        : null,
  );
}

// Method to navigate to the Maps Page.
void _onMapButtonTap(BuildContext context) async {
  if (mapId != null) {
    Navigator.of(context).push(MaterialPageRoute<dynamic>(
      builder: (context) => MapsPage(mapId: mapId!),
    ));
  }
}

Future<bool> loadAsset(String assetName, String destinationDirectoryPath) async {
  final destinationFilePath = path.join(destinationDirectoryPath, assetName);

  File file = File(destinationFilePath);
  if (await file.exists()) {
    return false;
  }
  await file.create();

  final asset = await rootBundle.load('assets/$assetName');
  final buffer = asset.buffer;
  await file.writeAsBytes(buffer.asUint8List(asset.offsetInBytes, asset.lengthInBytes), flush: true);
  print("Wrote file ${file.path}");
  return true;
}

Future<void> loadMaps() async {
  const cmap = 'AndorraOSM_2021Q1.cmap';
  const worldMap = 'WM_7_406.map';

  final dirPath = await getDirPath();
  final resFilePath = path.joinAll([dirPath.path, "Data", "Res"]);
  final mapsFilePath = path.joinAll([dirPath.path, "Data", "Maps"]);

  await deleteAssets(resFilePath, RegExp(r'WM_\d_\d+\.map'));
  await deleteAssets(mapsFilePath, RegExp(r'.+\.cmap'));

  await loadAsset(cmap, mapsFilePath);
  await loadAsset(worldMap, resFilePath);

  ContentStore.refreshContentStore();

  setState(() {
    showButton = false;
  });
}

Future<Directory> getDirPath() async {
  if (Platform.isAndroid) {
    return (await getExternalStorageDirectory())!;
  } else if (Platform.isIOS) {
    return await getApplicationDocumentsDirectory();
  } else {
    throw Exception('Platform not supported');
  }
}

Future<void> deleteAssets(String directoryPath, RegExp pattern) async {
  final directory = Directory(directoryPath);

  for (final file in directory.listSync()) {
    final filename = path.basename(file.path);
    if (pattern.hasMatch(filename)) {
      try {
        print('INFO DELETE ASSETS: deleting file ${file.path}');
        file.deleteSync();
      } catch (e) {
        print('\x1B[31mWARNING: Deleting file ${file.path} failed. Reason:\n${e.toString()}\x1B[0m');
      }
    }
  }
}

}

Flutter Examples

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