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:

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:

  • Interact with and manage routing functionalities on two separate maps within the same application.

  • Create and calculate routes with specified waypoints and preferences for each map independently.

map_update - example flutter screenshot

map_update - example flutter screenshot

Build the Main Application

Define the main application widget, MyApp.

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

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

Handle Maps and Routes in the Stateful Widget

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

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

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

Define State Variables and Methods

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

class _MyHomePageState extends State<MyHomePage> {
  late Controller _mapController1;
  late Controller _mapController2;

  TaskHandler? _routingHandler1;
  TaskHandler? _routingHandler2;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepPurple[900],
        title: const Text('Multi Map Routing',
            style: TextStyle(color: Colors.white)),
        leading: IconButton(
            onPressed: _removeRoutes,
            icon: const Icon(
              Icons.close,
              color: Colors.white,
            )),
        actions: [
          IconButton(
              onPressed: () => _onBuildRouteButtonPressed(true),
              icon: const Icon(
                Icons.route,
                color: Colors.white,
              )),
          IconButton(
              onPressed: () => _onBuildRouteButtonPressed(false),
              icon: const Icon(
                Icons.route,
                color: Colors.white,
              ))
        ],
      ),
      body: Column(
        children: [
          SizedBox(
            height: MediaQuery.of(context).size.height / 2 - 50,
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: (
                onMapCreated: _onMap1Created,
                appAuthorization: projectApiToken,
              ),
            ),
          ),
          SizedBox(
            height: MediaQuery.of(context).size.height / 2 - 50,
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: (
                onMapCreated: _onMap2Created,
                appAuthorization: projectApiToken,
              ),
            ),
          ),
        ],
      ),
    );
  }

  void _showSnackBar(BuildContext context,
      {required String message, Duration duration = const Duration(hours: 1)}) {
    final snackBar = SnackBar(
      content: Text(message),
      duration: duration,
    );

    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

  void _onMap1Created(GemMapController controller) {
    _mapController1 = controller;
  }

  void _onMap2Created(GemMapController controller) {
    _mapController2 = controller;
  }

  void _onBuildRouteButtonPressed(bool isFirstMap) {
    final waypoints = <Landmark>[];
    if (isFirstMap) {
      final departure =
          Landmark.withLatLng(latitude: 37.77903, longitude: -122.41991);
      final destination =
          Landmark.withLatLng(latitude: 37.33619, longitude: -121.89058);

      waypoints.add(departure);
      waypoints.add(destination);
    } else {
      final departure =
          Landmark.withLatLng(latitude: 51.50732, longitude: -0.12765);
      final destination =
          Landmark.withLatLng(latitude: 51.27483, longitude: 0.52316);

      waypoints.add(departure);
      waypoints.add(destination);
    }

    final routePreferences = RoutePreferences();

    _showSnackBar(context,
        message: isFirstMap
            ? 'The first route is calculating.'
            : 'The second route is calculating.');

    if (isFirstMap) {
      _routingHandler1 = RoutingService.calculateRoute(
          waypoints,
          routePreferences,
          (err, routes) => _onRouteBuiltFinished(err, routes, true));
    } else {
      _routingHandler2 = RoutingService.calculateRoute(
          waypoints,
          routePreferences,
          (err, routes) => _onRouteBuiltFinished(err, routes, false));
    }
  }

  void _onRouteBuiltFinished(
      GemError err, List<Route>? routes, bool isFirstMap) {
    if (isFirstMap) {
      _routingHandler1 = null;
    } else {
      _routingHandler2 = null;
    }

    ScaffoldMessenger.of(context).clearSnackBars();
    if (_routingHandler1 != null) {
      _showSnackBar(context, message: 'The first route is calculating.');
    }
    if (_routingHandler2 != null) {
      _showSnackBar(context, message: 'The second route is calculating.');
    }

    if (err == GemError.success) {
      final routesMap = (isFirstMap
              ? _mapController1.preferences
              : _mapController2.preferences)
          .routes;

      for (final route in routes!) {
        routesMap.add(route, route == routes.first,
            label: route.getMapLabel());
      }

      if (isFirstMap) {
        _mapController1.centerOnRoutes(routes);
      } else {
        _mapController2.centerOnRoutes(routes);
      }
    }
  }

  void _removeRoutes() {
    if (_routingHandler1 != null) {
      RoutingService.cancelRoute(_routingHandler1!);
      _routingHandler1 = null;
    }

    if (_routingHandler2 != null) {
      RoutingService.cancelRoute(_routingHandler2!);
      _routingHandler2 = null;
    }

    _mapController1.preferences.routes.clear();
    _mapController2.preferences.routes.clear();
  }
}

Flutter Examples

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