Skip to content

Route Profile

In this guide you will learn how to display a map, calculate routes between multiple points, and show a detailed route profile.

route_profile - example flutter screenshot

route_profile - 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 route_profile 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 showcases the use of the gem_kit package to display a map, calculate routes, and show a detailed route profile.

route_profile - 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;
5import 'elevation_chart.dart';
6import 'route_profile_panel.dart';

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}

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      debugShowCheckedModeBanner: false,
 8      title: 'Route Profile',
 9      home: MyHomePage(),
10    );
11  }
12}

Handle Maps and Routes in the Stateful Widget

Create the stateful widget, MyHomePage, which will handle the map and routing functionality.

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 map and manage routes.

  1class _MyHomePageState extends State<MyHomePage> {
  2  late GemMapController _mapController;
  3
  4  TaskHandler? _routingHandler;
  5  Route? _focusedRoute;
  6
  7  final LineAreaChartController _chartController = LineAreaChartController();
  8
  9  @override
 10  void dispose() {
 11    GemKit.release();
 12    super.dispose();
 13  }
 14
 15  @override
 16  Widget build(BuildContext context) {
 17    return Scaffold(
 18      appBar: AppBar(
 19        backgroundColor: Colors.deepPurple[900],
 20        title: const Text('Route Profile', style: TextStyle(color: Colors.white)),
 21        actions: [
 22          if (_routingHandler == null && _focusedRoute == null)
 23            IconButton(
 24              onPressed: () => _onBuildRouteButtonPressed(context),
 25              icon: const Icon(Icons.route, color: Colors.white),
 26            ),
 27          if (_routingHandler != null)
 28            IconButton(
 29              onPressed: () => _onCancelRouteButtonPressed(),
 30              icon: const Icon(Icons.stop, color: Colors.white),
 31            ),
 32          if (_focusedRoute != null)
 33            IconButton(
 34              onPressed: () => _onClearRoutesButtonPressed(),
 35              icon: const Icon(Icons.clear, color: Colors.white),
 36            ),
 37        ],
 38      ),
 39      body: Stack(
 40        children: [
 41          GemMap(
 42            onMapCreated: _onMapCreated,
 43          ),
 44          if (_focusedRoute != null)
 45            Align(
 46                alignment: Alignment.bottomCenter,
 47                child: RouteProfilePanel(
 48                  route: _focusedRoute!,
 49                  mapController: _mapController,
 50                  chartController: _chartController,
 51                  centerOnRoute: () => _centerOnRoute([_focusedRoute!]),
 52                ))
 53        ],
 54      ),
 55    );
 56  }
 57
 58  void _onMapCreated(GemMapController controller) {
 59    _mapController = controller;
 60    _registerRouteTapCallback();
 61  }
 62
 63  void _onBuildRouteButtonPressed(BuildContext context) {
 64    final departureLandmark =
 65        Landmark.withLatLng(latitude: 46.59344, longitude: 7.91069);
 66    final destinationLandmark =
 67        Landmark.withLatLng(latitude: 46.55945, longitude: 7.89293);
 68    final routePreferences = RoutePreferences(
 69        buildTerrainProfile: const BuildTerrainProfile(enable: true),
 70        transportMode: RouteTransportMode.pedestrian);
 71
 72    _showSnackBar(context, message: "The route is being calculated.");
 73
 74    _routingHandler = RoutingService.calculateRoute(
 75        [departureLandmark, destinationLandmark], routePreferences,
 76        (err, routes) {
 77      _routingHandler = null;
 78      ScaffoldMessenger.of(context).clearSnackBars();
 79
 80      if (err == GemError.success) {
 81        final routesMap = _mapController.preferences.routes;
 82
 83        for (final route in routes!) {
 84          routesMap.add(route, route == routes.first,
 85              label: route.getMapLabel());
 86        }
 87
 88        _centerOnRoute(routes);
 89        setState(() {
 90          _focusedRoute = routes.first;
 91        });
 92      }
 93    });
 94
 95    setState(() {});
 96  }
 97
 98  void _onClearRoutesButtonPressed() {
 99    _mapController.deactivateAllHighlights();
100    _mapController.preferences.routes.clear();
101
102    setState(() {
103      _focusedRoute = null;
104    });
105  }
106
107  void _onCancelRouteButtonPressed() {
108    if (_routingHandler != null) {
109      RoutingService.cancelRoute(_routingHandler!);
110
111      setState(() {
112        _routingHandler = null;
113      });
114    }
115  }
116
117  void _registerRouteTapCallback() {
118    _mapController.registerTouchCallback((pos) async {
119      _mapController.setCursorScreenPosition(pos);
120      final routes = _mapController.cursorSelectionRoutes();
121
122      if (routes.isNotEmpty) {
123        _mapController.preferences.routes.mainRoute = routes.first;
124
125        if (_chartController.setCurrentHighlight != null) {
126          _chartController.setCurrentHighlight!(0);
127        }
128
129        setState(() {
130          _focusedRoute = routes.first;
131        });
132
133        _centerOnRoute([_focusedRoute!]);
134      }
135    });
136  }
137
138  void _centerOnRoute(List<Route> route) {
139    const appbarHeight = 50;
140    const padding = 20;
141
142    _mapController.centerOnRoutes(route,
143        screenRect: RectType(
144          x: 0,
145          y: (appbarHeight + padding * MediaQuery.of(context).devicePixelRatio)
146              .toInt(),
147          width: (MediaQuery.of(context).size.width *
148                  MediaQuery.of(context).devicePixelRatio)
149              .toInt(),
150          height: ((MediaQuery.of(context).size.height / 2 -
151                    appbarHeight -
152                    2 * padding * MediaQuery.of(context).devicePixelRatio) *
153                  MediaQuery.of(context).devicePixelRatio)
154              .toInt(),
155        ));
156  }
157
158  void _showSnackBar(BuildContext context,
159      {required String message, Duration duration = const Duration(hours: 1)}) {
160    final snackBar = SnackBar(
161      content: Text(message),
162      duration: duration,
163    );
164
165    ScaffoldMessenger.of(context).showSnackBar(snackBar);
166  }
167}

This example guides you through the setup and implementation of a route profile in a Flutter application using the gem_kit package. The focus is on handling maps, user interactions, and route preferences to provide a seamless user experience for calculating and displaying route profiles.

Flutter Examples

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