Route Instructions¶
In this guide you will learn how to display a map, calculate routes between multiple points, and show detailed route instructions.
Setup¶
Prerequisites¶
Build and Run¶
Start a terminal/command prompt and navigate to the route_instructions
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 between multiple points, and show detailed route instructions.
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 'route_instructions_page.dart';
5import 'utility.dart';
6import '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 debugShowCheckedModeBanner: false,
8 title: 'Route Instructions',
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 TaskHandler? _routingHandler;
4 bool _areRoutesBuilt = false;
5 List<RouteInstruction>? instructions;
6
7 @override
8 void dispose() {
9 GemKit.release();
10 super.dispose();
11 }
12
13 @override
14 Widget build(BuildContext context) {
15 return Scaffold(
16 appBar: AppBar(
17 backgroundColor: Colors.deepPurple[900],
18 title: const Text("Route Instructions",
19 style: TextStyle(color: Colors.white)),
20 actions: [
21 if (_areRoutesBuilt)
22 IconButton(
23 onPressed: _onRouteCancelButtonPressed,
24 icon: const Icon(Icons.cancel, color: Colors.white),
25 ),
26 if (!_areRoutesBuilt)
27 IconButton(
28 onPressed: () => _onBuildRouteButtonRoute(context),
29 icon: const Icon(Icons.route, color: Colors.white),
30 ),
31 ],
32 leading: Row(
33 children: [
34 if (_areRoutesBuilt)
35 IconButton(
36 onPressed: _onRouteInstructionsButtonPressed,
37 icon: const Icon(Icons.density_medium_sharp, color: Colors.white),
38 ),
39 ],
40 ),
41 ),
42 body: GemMap(
43 onMapCreated: _onMapCreated,
44 ),
45 );
46 }
47
48 void _onMapCreated(GemMapController controller) {
49 _mapController = controller;
50 }
51
52 void _onBuildRouteButtonRoute(BuildContext context) {
53 final departureLandmark =
54 Landmark.withLatLng(latitude: 50.11428, longitude: 8.68133);
55 final intermediaryPointLandmark =
56 Landmark.withLatLng(latitude: 49.0069, longitude: 8.4037);
57 final destinationLandmark =
58 Landmark.withLatLng(latitude: 48.1351, longitude: 11.5820);
59
60 final routePreferences = RoutePreferences();
61 _showSnackBar(context, message: 'The route is calculating.');
62
63 _routingHandler = RoutingService.calculateRoute(
64 [departureLandmark, intermediaryPointLandmark, destinationLandmark],
65 routePreferences, (err, routes) async {
66 _routingHandler = null;
67 ScaffoldMessenger.of(context).clearSnackBars();
68
69 if (err == GemError.success) {
70 final routesMap = _mapController.preferences.routes;
71
72 for (final route in routes!) {
73 routesMap.add(route, route == routes.first,
74 label: route.getMapLabel());
75 }
76
77 _mapController.centerOnRoutes(routes);
78
79 instructions = _getInstructionsFromSegments(routes.first.segments);
80 setState(() {
81 _areRoutesBuilt = true;
82 });
83 }
84 });
85 }
86
87 void _onRouteCancelButtonPressed() async {
88 _mapController.preferences.routes.clear();
89
90 if (_routingHandler != null) {
91 RoutingService.cancelRoute(_routingHandler!);
92 _routingHandler = null;
93 }
94
95 if (instructions != null) {
96 instructions!.clear();
97 }
98
99 setState(() {
100 _areRoutesBuilt = false;
101 });
102 }
103
104 void _onRouteInstructionsButtonPressed() {
105 Navigator.of(context).push(MaterialPageRoute<dynamic>(
106 builder: (context) =>
107 RouteInstructionsPage(instructionList: instructions!)));
108 }
109
110 List<RouteInstruction> _getInstructionsFromSegments(
111 List<RouteSegment> segments) {
112 List<RouteInstruction> instructionsList = [];
113
114 for (final segment in segments) {
115 final segmentInstructions = segment.instructions;
116 instructionsList.addAll(segmentInstructions);
117 }
118 return instructionsList;
119 }
120
121 void _showSnackBar(BuildContext context,
122 {required String message, Duration duration = const Duration(hours: 1)}) {
123 final snackBar = SnackBar(
124 content: Text(message),
125 duration: duration,
126 );
127
128 ScaffoldMessenger.of(context).showSnackBar(snackBar);
129 }
130}
Route Instructions Page¶
The RouteInstructionsPage
displays detailed route instructions. Here is the code for RouteInstructionsPage
and the InstructionsItem
widget.
1import 'package:gem_kit/core.dart';
2import 'utility.dart';
3import 'package:flutter/material.dart';
4
5class RouteInstructionsPage extends StatefulWidget {
6 final List<RouteInstruction> instructionList;
7
8 const RouteInstructionsPage({super.key, required this.instructionList});
9
10 @override
11 State<RouteInstructionsPage> createState() => _RouteInstructionsState();
12}
13
14class _RouteInstructionsState extends State<RouteInstructionsPage> {
15 @override
16 Widget build(BuildContext context) {
17 return Scaffold(
18 appBar: AppBar(
19 automaticallyImplyLeading: true,
20 title: const Text("Route Instructions", style: TextStyle(color: Colors.white)),
21 backgroundColor: Colors.deepPurple[900],
22 foregroundColor: Colors.white,
23 ),
24 body: ListView.separated(
25 padding: EdgeInsets.zero,
26 itemCount: widget.instructionList.length,
27 separatorBuilder: (context, index) => const Divider(
28 indent: 50,
29 height: 0,
30 ),
31 itemBuilder: (contex, index) {
32 final instruction = widget.instructionList.elementAt(index);
33 return InstructionsItem(instruction: instruction);
34 },
35 ),
36 );
37 }
38}
39
40class InstructionsItem extends StatefulWidget {
41 final RouteInstruction instruction;
42 const InstructionsItem({super.key, required this.instruction});
43
44 @override
45 State<InstructionsItem> createState() => _InstructionsItemState();
46}
47
48class _InstructionsItemState extends State<InstructionsItem> {
49 @override
50 Widget build(BuildContext context) {
51 return ListTile(
52 leading: Container(
53 padding: const EdgeInsets.all(8),
54 width: 50,
55 child: Image.memory(widget.instruction.turnDetails
56 .getAbstractGeometryImage(renderSettings: const AbstractGeometryImageRenderSettings())),
57 ),
58 title: Text(
59 widget.instruction.turnInstruction,
60 overflow: TextOverflow.fade,
61 style: const TextStyle(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
62 maxLines: 2,
63 ),
64 subtitle: Text(
65 widget.instruction.followRoadInstruction,
66 overflow: TextOverflow.fade,
67 style: const TextStyle(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
68 maxLines: 2,
69 ),
70 trailing: Text(
71 widget.instruction.getFormattedDistanceUntilInstruction(),
72 overflow: TextOverflow.fade,
73 style: const TextStyle(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
74 ),
75 );
76 }
77}
This example guides you through the setup and implementation of route instructions 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 instructions.