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.
Run: flutter pub get
Import Necessary Packages¶
import 'package:gem_kit/core.dart';
import 'package:gem_kit/map.dart';
import 'package:gem_kit/navigation.dart';
import 'package:gem_kit/routing.dart';
import 'package:gem_kit/sense.dart';
import 'bottom_navigation_panel.dart';
import 'top_navigation_panel.dart';
import 'utility.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/foundation.dart';
import '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 used to request location (GPS) sensor permission from the user, if it was not previously granted, as this is required for navigation.
App entry and initialization¶
const projectApiToken = String.fromEnvironment('GEM_TOKEN');
void main() {
runApp(const MyApp());
}
This code initializes the projectApiToken with the required authorization token and launches the app.
How It Works¶
This example demonstrates the following features:
Compute routes between a departure and destination.
Display routes on a map and allow for multiple route alternatives.
Begin turn-by-turn navigation along the selected route with real-time positioning.
Map Initialization¶
void _onMapCreated(GemMapController controller) {
// Save controller for further usage.
_mapController = controller;
}
This callback function is called when the interactive map is initialized and ready to use.
Building the Route¶
void _onBuildRouteButtonPressed(BuildContext context) {
if (_currentLocation == null) {
_showSnackBar(context,
message: 'Current location is needed to compute the route.', duration: const Duration(seconds: 3));
return;
}
// Define the departure
final departureLandmark = Landmark.withCoordinates(_currentLocation!);
// Define the destination.
final destinationLandmark = Landmark.withLatLng(latitude: 52.51614, longitude: 13.37748);
// Define the route preferences.
final routePreferences = RoutePreferences();
_showSnackBar(context, message: 'The route is calculating.');
// Calling the calculateRoute SDK method.
_routingHandler =
RoutingService.calculateRoute([departureLandmark, destinationLandmark], routePreferences, (err, routes) {
// If the route calculation is finished, we don't have a progress listener anymore.
_routingHandler = null;
ScaffoldMessenger.of(context).clearSnackBars();
if (err == GemError.routeTooLong) {
print('The destination is too far from your current location. Change the coordinates of the destination.');
return;
}
// If there aren't any errors, we display the routes.
if (err == GemError.success) {
// Get the routes collection from map preferences.
final routesMap = _mapController.preferences.routes;
// Display the routes on map.
for (final route in routes!) {
routesMap.add(route, route == routes.first, label: route.getMapLabel());
}
// Center the camera on routes.
_mapController.centerOnRoutes(routes);
setState(() {
_areRoutesBuilt = true;
});
}
});
}
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 two waypoints, one for the departure, and one for the destination. Optionally, zero 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);
Starting Navigation¶
void _startNavigation() {
final routes = _mapController.preferences.routes;
_navigationHandler = NavigationService.startNavigation(routes.mainRoute, (type, instruction) async {
if (type == NavigationEventType.destinationReached || type == NavigationEventType.error) {
// If the navigation has ended or if an error occurred while navigating, remove routes.
setState(() {
_isNavigationActive = false;
_cancelRoute();
});
return;
}
_isNavigationActive = true;
if (instruction != null) {
setState(() => currentInstruction = instruction);
}
});
// Set the camera to follow position.
_mapController.startFollowingPosition();
}
Following the Position¶
void _onFollowPositionButtonPressed() async {
if (kIsWeb) {
// On web platform permissions are handled differently than other platforms.
// The SDK handles the request for permission for location.
_locationPermissionStatus = PermissionStatus.granted;
} else {
// For Android & iOS platforms, permission_handler package is used to ask for permissions.
_locationPermissionStatus = await Permission.locationWhenInUse.request();
}
if (_locationPermissionStatus == PermissionStatus.granted) {
// After the permission was granted, we can set the live data source (in most cases the GPS).
// The data source should be set only once; otherwise, we'll get -5 error.
if (!_hasLiveDataSource) {
PositionService.instance.setLiveDataSource();
_getCurrentLocation();
_hasLiveDataSource = true;
}
// After the data source is set, startFollowingPosition can be safely called.
// Optionally, we can set an animation
final animation = GemAnimation(type: AnimationType.linear);
// Calling the start following position SDK method.
_mapController.startFollowingPosition(animation: animation);
}
setState(() {});
}
_mapController.startFollowingPosition();
_locationPermissionStatus = await Permission.locationWhenInUse.request();
PositionService.instance.setLiveDataSource();