Skip to content

Multi Map Routing

In this guide, you will learn how to implement multi-map routing functionality using the gem_kit package. This example demonstrates how to manage routes on two maps simultaneously.

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 multi_map_routing 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:

1    allprojects {
2        repositories {
3            google()
4            mavenCentral()
5            maven {
6               url "${rootDir}/../plugins/gem_kit/android/build"
7            }
8        }
9    }

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

 1android {
 2    defaultConfig {
 3        applicationId "com.magiclane.gem_kit.examples.example_pathname"
 4        minSdk 21
 5        targetSdk flutter.targetSdk
 6        versionCode flutterVersionCode.toInteger()
 7        versionName flutterVersionName
 8    }
 9    buildTypes {
10        release {
11            minifyEnabled false
12            shrinkResources false
13
14            // TODO: Add your own signing config for the release build.
15            // Signing with the debug keys for now, so `flutter run --release` works.
16            signingConfig signingConfigs.debug
17        }
18    }
19}

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

How it Works

This example demonstrates how to use the gem_kit package to interact with two maps and manage routes on both.

map_update - example flutter screenshot

Import Necessary Packages

First, import the required packages in your Dart code.

1import 'package:gem_kit/core.dart';
2import 'package:gem_kit/map.dart';
3import 'package:gem_kit/routing.dart';
4import 'package:flutter/material.dart' hide Route;

Initialize GemKit

In the main function, initialize GemKit with your project API token.

1Future<void> main() async {
2  const projectApiToken = String.fromEnvironment('GEM_TOKEN');
3  await GemKit.initialize(appAuthorization: projectApiToken);
4  runApp(const MyApp());
5}

map_update - example flutter screenshot

Build the Main Application

Define the main application widget, MyApp.

 1class MyApp extends StatelessWidget {
 2  const MyApp({super.key});
 3
 4  @override
 5  Widget build(BuildContext context) {
 6    return const MaterialApp(
 7        title: 'Multi Map Routing',
 8        debugShowCheckedModeBanner: false,
 9        home: MyHomePage());
10  }
11}

Handle Maps and Routes in the Stateful Widget

Create the stateful widget, MyHomePage, which will handle two maps and their respective routes.

1class MyHomePage extends StatefulWidget {
2  const MyHomePage({super.key});
3
4  @override
5  State<MyHomePage> createState() => _MyHomePageState();
6}

Define State Variables and Methods

Within _MyHomePageState, define the necessary state variables and methods to interact with the maps and manage routes.

  1class _MyHomePageState extends State<MyHomePage> {
  2  late GemMapController _mapController1;
  3  late GemMapController _mapController2;
  4
  5  TaskHandler? _routingHandler1;
  6  TaskHandler? _routingHandler2;
  7
  8  @override
  9  void dispose() {
 10    GemKit.release();
 11    super.dispose();
 12  }
 13
 14  @override
 15  Widget build(BuildContext context) {
 16    return Scaffold(
 17      appBar: AppBar(
 18        backgroundColor: Colors.deepPurple[900],
 19        title: const Text('Multi Map Routing',
 20            style: TextStyle(color: Colors.white)),
 21        leading: IconButton(
 22            onPressed: _removeRoutes,
 23            icon: const Icon(
 24              Icons.close,
 25              color: Colors.white,
 26            )),
 27        actions: [
 28          IconButton(
 29              onPressed: () => _onBuildRouteButtonPressed(true),
 30              icon: const Icon(
 31                Icons.route,
 32                color: Colors.white,
 33              )),
 34          IconButton(
 35              onPressed: () => _onBuildRouteButtonPressed(false),
 36              icon: const Icon(
 37                Icons.route,
 38                color: Colors.white,
 39              ))
 40        ],
 41      ),
 42      body: Column(
 43        children: [
 44          SizedBox(
 45            height: MediaQuery.of(context).size.height / 2 - 50,
 46            child: Padding(
 47              padding: const EdgeInsets.all(8.0),
 48              child: GemMap(
 49                onMapCreated: _onMap1Created,
 50              ),
 51            ),
 52          ),
 53          SizedBox(
 54            height: MediaQuery.of(context).size.height / 2 - 50,
 55            child: Padding(
 56              padding: const EdgeInsets.all(8.0),
 57              child: GemMap(
 58                onMapCreated: _onMap2Created,
 59              ),
 60            ),
 61          ),
 62        ],
 63      ),
 64    );
 65  }
 66
 67  void _showSnackBar(BuildContext context,
 68      {required String message, Duration duration = const Duration(hours: 1)}) {
 69    final snackBar = SnackBar(
 70      content: Text(message),
 71      duration: duration,
 72    );
 73
 74    ScaffoldMessenger.of(context).showSnackBar(snackBar);
 75  }
 76
 77  void _onMap1Created(GemMapController controller) {
 78    _mapController1 = controller;
 79  }
 80
 81  void _onMap2Created(GemMapController controller) {
 82    _mapController2 = controller;
 83  }
 84
 85  void _onBuildRouteButtonPressed(bool isFirstMap) {
 86    final waypoints = <Landmark>[];
 87    if (isFirstMap) {
 88      final departure =
 89          Landmark.withLatLng(latitude: 37.77903, longitude: -122.41991);
 90      final destination =
 91          Landmark.withLatLng(latitude: 37.33619, longitude: -121.89058);
 92
 93      waypoints.add(departure);
 94      waypoints.add(destination);
 95    } else {
 96      final departure =
 97          Landmark.withLatLng(latitude: 51.50732, longitude: -0.12765);
 98      final destination =
 99          Landmark.withLatLng(latitude: 51.27483, longitude: 0.52316);
100
101      waypoints.add(departure);
102      waypoints.add(destination);
103    }
104
105    final routePreferences = RoutePreferences();
106
107    _showSnackBar(context,
108        message: isFirstMap
109            ? 'The first route is calculating.'
110            : 'The second route is calculating.');
111
112    if (isFirstMap) {
113      _routingHandler1 = RoutingService.calculateRoute(
114          waypoints,
115          routePreferences,
116          (err, routes) => _onRouteBuiltFinished(err, routes, true));
117    } else {
118      _routingHandler2 = RoutingService.calculateRoute(
119          waypoints,
120          routePreferences,
121          (err, routes) => _onRouteBuiltFinished(err, routes, false));
122    }
123  }
124
125  void _onRouteBuiltFinished(
126      GemError err, List<Route>? routes, bool isFirstMap) {
127    if (isFirstMap) {
128      _routingHandler1 = null;
129    } else {
130      _routingHandler2 = null;
131    }
132
133    ScaffoldMessenger.of(context).clearSnackBars();
134    if (_routingHandler1 != null) {
135      _showSnackBar(context, message: 'The first route is calculating.');
136    }
137    if (_routingHandler2 != null) {
138      _showSnackBar(context, message: 'The second route is calculating.');
139    }
140
141    if (err == GemError.success) {
142      final routesMap = (isFirstMap
143              ? _mapController1.preferences
144              : _mapController2.preferences)
145          .routes;
146
147      for (final route in routes!) {
148        routesMap.add(route, route == routes.first,
149            label: route.getMapLabel());
150      }
151
152      if (isFirstMap) {
153        _mapController1.centerOnRoutes(routes);
154      } else {
155        _mapController2.centerOnRoutes(routes);
156      }
157    }
158  }
159
160  void _removeRoutes() {
161    if (_routingHandler1 != null) {
162      RoutingService.cancelRoute(_routingHandler1!);
163      _routingHandler1 = null;
164    }
165
166    if (_routingHandler2 != null) {
167      RoutingService.cancelRoute(_routingHandler2!);
168      _routingHandler2 = null;
169    }
170
171    _mapController1.preferences.routes.clear();
172    _mapController2.preferences.routes.clear();
173  }
174}

Flutter Examples

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