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.
Setup¶
Prerequisites¶
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}
Then run the project:
flutter run --debug
orflutter 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.
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}
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}