Navigate Route¶
Setup¶
Prerequisites¶
Build and run¶
Go to the navigate_route
directory,
within the flutter examples directory - that is the name of this 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.
Replace example_pathname
with the actual example path name,
such as address_search
Download project dependencies:
flutter upgrade
run the following terminal commands in the project directory,
where the pubspec.yaml
file is located:
flutter clean
flutter pub get
Run the example:
First, verify that the ANDROID_SDK_ROOT
environment variable
is set to the root path of your android SDK.
In android/app/src/main/AndroidManifest.xml
add the location permissions
required for actual navigation, below the top <manifest xmlns:android
line,
as shown:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
In android/build.gradle
add the maven {}
block as shown,
within the allprojects {} block
, for both debug and release builds,
without the line numbers, those are for reference:
1allprojects {
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 example pathname, such as center_coordinates
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
flutter run --release
In the ios/Podfile
configuration text file, at the top, set the minimum ios
platform to 13 like this:
platform :ios, '13.0'
Also in the ios/Podfile
configuration text file, add the location permissions
required for actual navigation, at the end of the file, as shown:
1post_install do |installer|
2 installer.pods_project.targets.each do |target|
3 flutter_additional_ios_build_settings(target)
4
5 target.build_configurations.each do |config|
6 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
7 '$(inherited)',
8 ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
9 'PERMISSION_LOCATION=1',
10 ]
11 end
12 end
13end
In the ios/Runner/Info.plist
configuration text file, add the following
lines inside the <dict> </dict>
block:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Location is needed for map localization and navigation</string>
How it works¶
1import 'package:gem_kit/core.dart';
2import 'package:gem_kit/map.dart';
3import 'package:gem_kit/navigation.dart';
4import 'package:gem_kit/routing.dart';
5import 'package:gem_kit/sense.dart';
6
7import 'bottom_navigation_panel.dart';
8import 'top_navigation_panel.dart';
9import 'utility.dart';
10
11import 'package:permission_handler/permission_handler.dart';
12
13import 'package:flutter/foundation.dart';
14import 'package:flutter/material.dart' hide Route, Animation;
The dart
material package is imported, as well as the
required gem_kit
packages.
The permission handler is to request location (GPS) sensor permission from the user, if it was not previously granted, as this is required for navigation.
1void _onMapCreated(GemMapController controller) {
2 // Save controller for further usage.
3 _mapController = controller;
4}
This callback function is called when the interactive map is initialized and ready to use.
1void _onBuildRouteButtonPressed(BuildContext context) {
2 if (_currentLocation == null) {
3 _showSnackBar(context,
4 message: 'Current location is needed to compute the route.', duration: const Duration(seconds: 3));
5 return;
6 }
7
8 // Define the departure
9 final departureLandmark = Landmark.withCoordinates(_currentLocation!);
10
11 // Define the destination.
12 final destinationLandmark = Landmark.withLatLng(latitude: 52.51614, longitude: 13.37748);
13
14 // Define the route preferences.
15 final routePreferences = RoutePreferences();
16 _showSnackBar(context, message: 'The route is calculating.');
17
18 // Calling the calculateRoute SDK method.
19 // (err, results) - is a callback function that gets called when the route computing is finished.
20 // err is an error enum, results is a list of routes.
21 _routingHandler =
22 RoutingService.calculateRoute([departureLandmark, destinationLandmark], routePreferences, (err, routes) {
23 // If the route calculation is finished, we don't have a progress listener anymore.
24 _routingHandler = null;
25
26 ScaffoldMessenger.of(context).clearSnackBars();
27
28 if (err == GemError.routeTooLong) {
29 print('The destination is too far from your current location. Change the coordinates of the destination.');
30 return;
31 }
32
33 // If there aren't any errors, we display the routes.
34 if (err == GemError.success) {
35 // Get the routes collection from map preferences.
36 final routesMap = _mapController.preferences.routes;
37
38 // Display the routes on map.
39 for (final route in routes!) {
40 routesMap.add(route, route == routes.first, label: route.getMapLabel());
41 }
42
43 // Center the camera on routes.
44 _mapController.centerOnRoutes(routes);
45 setState(() {
46 _areRoutesBuilt = true;
47 });
48 }
49 });
50}
When the route button in the upper right corner is pressed, a route is computed from the current position to a preset location in Europe.
A route must have at least 2 waypoints, one for the departure, and one for the destination. Optionally, 0 or more intermediate waypoints may be specified, through which the route will pass, in order from departure to destination.
Landmark
and has latitude and longitude coordinates.final departureLandmark = Landmark.withCoordinates(_currentLocation!);
final destinationLandmark = Landmark.withLatLng(latitude: 52.51614, longitude: 13.37748);
RoutingService.calculateRoute()
routesMap.add()
_mapController.centerOnRoutes(routes);
1void _startNavigation() {
2 final routes = _mapController.preferences.routes;
3
4 _navigationHandler = NavigationService.startNavigation(routes.mainRoute, (type, instruction) async {
5 if (type == NavigationEventType.destinationReached || type == NavigationEventType.error) {
6 // If the navigation has ended or if and error occured while navigating, remove routes.
7 setState(() {
8 _isNavigationActive = false;
9 _cancelRoute();
10 });
11 return;
12 }
13 _isNavigationActive = true;
14
15 if (instruction != null) {
16 setState(() => currentInstruction = instruction);
17 }
18 });
19
20 // Set the camera to follow position.
21 _mapController.startFollowingPosition();
22}
1void _onFollowPositionButtonPressed() async {
2 if (kIsWeb) {
3 // On web platform permission are handled differently than other platforms.
4 // The SDK handles the request of permission for location.
5 _locationPermissionStatus = PermissionStatus.granted;
6 } else {
7 // For Android & iOS platforms, permission_handler package is used to ask for permissions.
8 _locationPermissionStatus = await Permission.locationWhenInUse.request();
9 }
10
11 if (_locationPermissionStatus == PermissionStatus.granted) {
12 // After the permission was granted, we can set the live data source (in most cases the GPS).
13 // The data source should be set only once, otherwise we'll get -5 error.
14 if (!_hasLiveDataSource) {
15 PositionService.instance.setLiveDataSource();
16 _getCurrentLocation();
17 _hasLiveDataSource = true;
18 }
19
20 // After data source is set, startFollowingPosition can be safely called.
21 // Optionally, we can set an animation
22 final animation = GemAnimation(type: AnimationType.linear);
23
24 // Calling the start following position SDK method.
25 _mapController.startFollowingPosition(animation: animation);
26 }
27 setState(() {});
28}
_mapController.startFollowingPosition();
_locationPermissionStatus = await Permission.locationWhenInUse.request();
PositionService.instance.setLiveDataSource();