Key Concepts

In the following use case sections, we will guide you through the most common usage scenarios and reveal tips and easy-to-understand guidelines to help you get the most out of the HERE SDK for Flutter.

How to use this Guide?

You can read this guide in any order. All sections are independent from each other, making it easy to skip any section and to dive straight into the topics which you are most interested in.

  • In the example section, you can find the example apps accompanying this user guide.
  • If you are interested in building your first app showing a HERE map, take a look at the Get Started section to guide you through the first simple steps.
  • See an overview of the available use cases covered in this Developer's Guide.

Code Snippets

The shown code snippets cover best practice example code ready to be used for your own applications. However, for the sake of simplicity and to not shadow the educational approach of this guide, not all edge scenarios may be handled, especially when it comes to error handling or robust threading. In some cases, the obvious code is left out, but it can be found in the accompanying example apps that can be built and deployed instantly on any supported device with a set of valid HERE credentials.

Design Principles

The accompanying example apps follow the same structure. As much as possible the HERE SDK example code is decoupled from the surrounding platform code. We hope this makes it easier to see the relevant parts of the shown APIs. Each example app follows the same entry point from which the HERE SDK is initialized. Since each app is focusing on a different aspect of the HERE SDK, that code can be found in a single class postfixed with "...Example.dart" in its class name. Most often this class gets a reference to a MapView to start its work.

Despite the popular phrase that "everything is a widget", the example code is kept free of most Flutter dependencies - instead it's mostly pure Dart code that shows how the HERE SDK can be used.

Dispose Objects

All HERE SDK classes will be garbage collected by Flutter if the instance is no longer referenced or set to null.

For the SDKNativeEngine, which can be initialized automatically or programmatically, you can free resources by calling SDKNativeEngine.sharedInstance?.dispose(), for example, when the hosting widget's lifetime has ended. Calling dispose() will stop pending requests and close open files and databases that are still running on the main thread. After calling dispose() any related HERE SDK feature should no longer be used - unless you create and set a new shared instance: If you have created engines like SearchEngine() or RoutingEngine() using the default constructor, then these instances need to be recreated as well. Basically, all engines that used the same shared instance of the SDKNativeEngine need to be recreated after it was disposed.

Callbacks and Listeners

  • The HERE SDK exposes callbacks for single event notification such as for search results.
  • For reoccurring event notifications such as for gesture events, listeners are used. When multiple listeners can be set, then the method pattern add_x() and remove_x() is used as naming convention. If only one listener can be set at a time, the set_x() pattern is used that can be set to null to stop listening.
  • It is the responsibility of the developer to handle errors inside the scope of a callback gracefully: As a guideline, code that can throw an exception should be handled.

Debug Logs

You can use the LogAppender interface to insert your own log class into the SDKNativeEngine. This way you can log HERE SDK messages for various predefined log levels even for release builds of your app.

When running an iOS simulator, you can obtain logs without running Xcode by executing the following command from the terminal:

xcrun simctl spawn booted log stream --level debug

Dependency Management

Currently, dependency management, for example, via https://pub.dev/, is not yet supported. This means that the HERE SDK plugin must be copied locally to an application project as described in the Get Started section.

Is the HERE SDK Thread Safe?

The HERE SDK is not guaranteed to be thread safe and it is recommended to make calls to the SDK from the main thread. Internally, the HERE SDK will offload most of its work to a background thread, but callbacks to your code will always occur on the main thread. In general, thread safety is the responsibility of the caller. For example, it is unsafe to reuse an engine on different threads unless your code is synchronized.

Use TaskHandles to Cancel Asynchronous Operations

Most asynchronous methods provide a TaskHandle as immediate return value, while the final result of the operation will be returned in a completion handler with a delay. The TaskHandle provides status information and it allows to abort an ongoing operation.

Unit Tests

It's easy to write unit tests for your app logic that uses the HERE SDK as all classes are fully mockable. Below you can see an example using Mockito. This will also work with most other frameworks that allow to create mocked objects:

([Angle])
void main() {
  group('Angle', () {
    test('test Angle', () {
      var mockAngle = MockAngle();

      when(mockAngle.degrees).thenReturn(10.0);

      expect(mockAngle.degrees, 10.0);
      verify(mockAngle.degrees).called(1);
    });
  });
}

Note that above we add the Mockito annotation @GenerateMocks([Angle]) to create a mock of the HERE SDK class Angle. In order to access the automatically created mock named MockAngle you need to import the created file, for example: import 'main_test.mocks.dart'.

For more information on unit testing for Flutter in general, refer to this introduction.

Check the UnitTesting example app to find more use case examples.

Coverage

Consult the coverage page to get detailed information on supported countries and languages per feature.

Defining a Scope

Optionally, you can define several scopes for an application - for example, to define a debugScope for testing your app. For a production version of your app, you may decide to leave the scope just empty, which is the default value. See the IAM Guide for more details on how to set up a project ID.

Each app belongs at least to one project. In the Projects Manager via your HERE platform account, select Projects: This allows you to see the available project IDs. You can also add more. Each project ID shows a HRN value.

Set the HRN value as scope in your plist file as follows:

<key>HERECredentials</key>
<dict>
    <key>AccessKeyId</key>
    <string>YOUR_ACCESS_KEY_ID</string>
    <key>AccessKeySecret</key>
    <string>YOUR_ACCESS_KEY_SECRET</string>
    <!-- Optionally, set a project scope. -->
    <key>Scope</key>
    <string>YOUR_PROJECT_SCOPE</string>
</dict>

For the AndroidManifest, do:

<!-- Optionally, set a project scope. -->
<meta-data
   android:name="com.here.sdk.access_scope"
   android:value="YOUR_PROJECT_SCOPE" />

Follow this guide to see how you can manage your projects.

Note that a project ID must be unique and cannot be changed for the lifetime of the organization's account. Project IDs must be between 4 and 16 characters in length. They affect also the HRN value. If the scope/HRN is set by an application, it will be used for authentication and the internal token creation of an application. If an unknown scope is set, any authentication attempt would fail and the application logs would indicate this.

Engines

The HERE SDK contains several modules - or engines as we call them - to execute specific tasks such as calculating a route with the RoutingEngine or requesting search results via the SearchEngine. There are many more engines you can use with the HERE SDK and you can read more about them in the dedicated chapters below. However, most engines share common concepts that makes it easier to use them. For example:

  • All engines execute their tasks asynchronously and receive their results on the main thread.
  • All engines share similar interfaces, callbacks and error handling.
  • It is possible to start multiple instances of an engine in parallel.
  • An online connection is required.

Android Permissions

The HERE SDK for Flutter automatically merges all required permissions to the AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Note

These permissions are not sensitive and are immediately granted upon installation by the system - they are always needed, as the HERE SDK needs to have a working internet connection. It is not a requirement to handle these permissions and there are no HERE SDK specific requirements on how to handle permissions.

However, be aware that a user can deny any permission after installation via the device's app settings. You can use a convenience class to notify the user upon app launch like shown here. Note that Flutter itself does not provide any mechanism to handle permissions - and the accompanying Flutter example apps do not utilize any specific Android permission handling. If no internet connection is available, most HERE SDK services will indicate this with an appropriate error message.

Initialize the HERE SDK

The HERE SDK is automatically initialized when Flutter registers the HERE SDK plugin for the first time at application start, but you can manually initialize the HERE SDK again. This allows to inject credentials programmatically - together with other initialization options.

You can also use native code to defer the initialization on Android and iOS.

Note: Depending on your contractual details, the time of initialization may have an impact on how transactions and monthly active users (MAU) are counted.

Use SDKOptions to Set HERE Credentials and Cache Path

All HERE SDK engines, except for SDKNativeEngine, can operate independently from each other and require HERE credentials to request data. The credentials can be set as shown in the Get Started guide - or programmatically. This can be useful, for example, to inject credentials at runtime from a web service.

By default, the HERE SDK is initialized automatically and it is reading the credentials from the Info.plist or AndroidManifest file. In addition, a default cache path is used for caching map data.

When you want to set the credentials programmatically, you need to create your own instance of the SDKNativeEngine, which can then be used to set or to change your HERE SDK credentials at runtime:

// Note that this does not init the HERE SDK. The HERE SDK is already initialized at this point in time.
SdkContext.init(IsolateOrigin.main);

// Optionally, clear a previous instance (if any).
await SDKNativeEngine.sharedInstance?.dispose();

SDKOptions sdkOptions = SDKOptions.withAccessKeySecretAndCachePath("YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET", "");

SDKNativeEngine sdkNativeEngine;
try {
  sdkNativeEngine = SDKNativeEngine(sdkOptions);  
  SDKNativeEngine.sharedInstance = sdkNativeEngine;
} on InstantiationException {
  // Handle exception.
}

try {
  _searchEngine = SearchEngine();
} on InstantiationException {
  // Handle exception.
}

This allows you to set a shared instance that will be used for all engines under the hood. Above, we initialize the SearchEngine as an example. Note that a shared instance is also required when you add a map view.

Make sure to call SdkContext.init(IsolateOrigin.main) only once, since it initializes the Isolate and sets up other resources that are need to initialize the HERE SDK. If you have different Isolates, it must be initialized for each of those Isolates with a different init parameter. Usually, it is enough to call init() in the main() function in main.dart. Note that, for example, during a hot restart all existing Isolates are destroyed and the HERE SDK runs SdkContext.init(IsolateOrigin.main) again. However, in such a case you would still need to call init() again if you want to launch a new custom Isolate in which the HERE SDK should run.

Note

In addition, when setting credentials programmatically, keep the tags holding dummy values for id and secret in your AndroidManifest.xml and Info.plist file. Empty values will lead to an exception.

By setting an empty string as cache path when initializing SDKOptions, you keep the default cache path.

Note

It is also possible to specify the cache path from the AndroidManifest file (in android folder of your app) or the Info.plist (in ios folder of your app). Consult the API Reference for the SDKNativeEngine to see an example.

Multiple SDKNativeEngine instances can’t have the same access key id and the cache path is ensured to be unique per access key id. If a 2nd instance is created with an access key id that is already in use, then an InstantiationException is thrown: As a rule of thumb, when you need multiple instances, you also need a distinct set of credentials for each.

Note

The access key id part of your credentials is tied to the cache path. When credentials are changed at runtime, it may look like that the previous cache is gone, but it still exists and it is not cleared automatically after disposing the SDKNativeEngine or after setting new credentials. Since new credentials have been set, the cache path has just been changed. For example, when the previous credentials are restored, the previous cache can be used again.

In general, it should not be necessary to initialize the HERE SDK multiple times - and only one sharedInstance can be set at a time: If not set, each engine, like for example, the SearchEngine needs to be created with an instance of a SDKNativeEngine as constructor parameter.

Make sure to init the SDKContext before creating a new SDKNativeEngine instance, like shown above. In addition, it is recommended to dispose() any previous instance - as at the time of running this code, Flutter may have already created an instance. For example, the above code snippet can be called in the main() method of your widget.

Alternatively, in rare use cases it may be useful to set an individual SDKNativeEngine instance for each of the feature engines:

SearchEngine searchEngine = SearchEngine.withSdkEngine(sdkNativeEngine);

If you don't set your credentials programmatically, the HERE SDK will be initialized automatically using the values found in the manifest or plist. Either way, invalid credentials will not block execution until these credentials are used to authenticate your app when you start to use an engine to request data - or when you want to show a map view.

Tip: One option to keep credentials secure is to store them on a secure server and retrieve them by requests using SSL connections. Credentials stored in AndroidManifest or Info.plist are easy to unveil, a better option can be to use data protection mechanisms such as Keychain data protection.

For best practice, consider:

  • To avoid keeping sensitive data in plain text.
  • To transfer credentials using a secure communication channel.
  • To store credentials using encryption such as device security and strong encryption ciphers.
  • To add anti-tampering and anti-debugging code, so that a potential attacker cannot intercept data during dynamic runtime execution.
  • Track the application usage to detect anomalies.

Note

The HERE SDK does not verify credentials at instantiation time. This only happens once a feature is used that requires authentification. In case of a failure, you will get with a dedicated error message. For example, the SearchEngine will reply with a SearchError.authentificationFailed error. Other engines provide similar error codes.

Freemium credentials will work for most features, but not for features like offline maps which are exclusive for editions such as the Navigate Edition. If the credentials are invalid, the logs contain "[ERROR]" messages with a text similar to "Failed to get the authentication token: Authentication failed, error code: 1". This indicates that the credentials are not accepted when using a service. If you want to ensure that your credentials are set up correctly, use a premium feature with a specialized engine and check if the engine replies with an error value.

Manually Initialize the HERE SDK on Android devices

On Android devices you can add native steps to add an entry to the AndroidManifest file to defer initialization.

  1. Prevent the InitProvider of the HERE SDK to be created automatically by adding this to the AndroidManifest.xml:
<provider
    android:name="com.here.sdk.engine.InitProvider"
    android:authorities="com.here.sdk.engine.InitProvider"
    android:exported="false"
    tools:node="remove" />

Note: By default, the InitProvider is created automatically at application start and the HERE SDK will be initialized. This can be deferred by creating the InitProvider manually - as shown below.

  1. You may also need to bind the tools namespace declaration to the manifest tag:
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.your_domain.your_app">
  1. Now you have to manually initialize the HERE SDK and create an InitProvider. This needs to be done before any of the HERE SDK functionality is used and it can only be done with native code. Below we show how it looks like in Java:
// Optionally, clear a previous instance (if any).
SDKNativeEngine.getSharedInstance().dispose();

// Manually initialize the HERE SDK.
InitProvider.initialize(appContext);

// Specify credentials programmatically.
SDKOptions sdkOptions = new SDKOptions("YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET");

// Create the SDKNativeEngine.
SDKNativeEngine sdkNativeEngine = null;
try {
    sdkNativeEngine = new SDKNativeEngine(sdkOptions);
} catch (InstantiationErrorException e) {
    // Handle exception.
}

SDKNativeEngine.setSharedInstance(sdkNativeEngine);

Note that you can create the SDKNativeEngine also with Dart - as shown in the previous section above. But it is not possible to initialize the InitProvider without native code. For this, you need to establish a method channel. Note that the native HERE SDK for Android is included as AAR in the Flutter plugin.

Manually Initialize the HERE SDK on iOS devices

By default, all plugins including the HERE SDK will be registered by calling GeneratedPluginRegistrant.register(with: self) in your AppDelegate.swift.

In order to manually initialize the HERE SDK, you can change the AppDelegate.swift to skip the HERE SDK plugin. Then you can manually load the plugin with a method channel that registers the plugin. Note that the native HERE SDK for iOS is included as XCFW in the Flutter plugin. Inside the method channel you need to execute the native code shown below:

do {
    try SDKInitializer.initializeIfNecessary()
} catch {
    fatalError("Failed to initialize HERE SDK. Cause: \(error)")
}

The initializeIfNecessary() class method will initialize the HERE SDK when it is not already initialized. As a positive side effect: If a map view is loaded at a later point in time, then the initialization is already done and the map view is shown faster.

If the HERE SDK is already initialized, then initializeIfNecessary() will do nothing.

More information is shown in the native guide for the HERE SDK for iOS.

Use Engines with or without a Map View

Engines do not need a map view to operate. Therefore it is possible to run an engine as a stand-alone, without any map view added to your application. This way, you can build an app solely around a specific engine. With or without a map view - the procedure to create a new engine is exactly the same:

try {
  _searchEngine = SearchEngine();
} on InstantiationException {
  // Handle exception.
}

When you use the default constructor to initialize an engine for a stand-alone usage, the HERE SDK will use a shared SDKNativeEngine under the hood to take the credentials as found in the AndroidManifest or plist file. Alternatively, you can provide the credentials programmatically as shown in the previous section.

results matching ""

    No results matching ""