Route Profile¶
In this guide you will learn how to display a map, calculate routes between multiple points, and show a detailed route profile.
Setup¶
Prerequisites¶
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}
Then run the project:
flutter run --debug
orflutter 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.
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.