Skip to main content
GuidesAPI ReferenceExamplesFAQ

Usage guidelines

|

This page outlines the recommended usage guidelines for the Maps SDK for Flutter. Adhering to these recommendations can enhance code reliability and help prevent issues that may arise from improper implementation.

Do not extend the classes provided by the SDK

The SDK is designed to deliver all necessary functionalities in an intuitive and straightforward manner. Avoid extending any of the Maps SDK for Flutter classes. Use the callback methods provided by the SDK for seamless integration.

DateTime Parameter Rules

Make sure the DateTime passed to method is a valid positive utc value. Values below the DateTime.utc(0) are not supported.

  • Use the provided register methods or constructor parameters to subscribe to events.
  • Only one callback can be active per event type. If multiple callbacks are registered for the same event type, the most recently registered callback will override the previous one.
  • To unsubscribe from events, register an empty callback.

Avoid using members annotated with @internal.

Members, methods, and fields annotated with @internal are intended for internal use and should not be accessed or used by API users.

Examples of Internal Members and Classes to Avoid:

  • Fields

    • Avoid using the pointerId, mapId fields/getters from classes.
  • Methods

    • Constructors that initialize pointerId or mapId with -1.
    • Do not use fromJson and toJson methods, as the structure of the generated JSON may change across SDK versions.
    • Avoid invoking listener methods such as notify... or handleEvent.
    • Avoid using the init methods from classes.
  • FFI-Related Classes

    • Refrain from interacting with classes related to the foreign function interface (FFI), such as GemKitPlatform, GemSdkNative, GemAutoreleaseObject.
warning

Using these internal elements can lead to unexpected behavior and compatibility issues since they are not part of the public API contract and may change or be removed without notice.

Check the error code

Many methods return error codes that indicate the outcome of the operation. Ensure that all failure cases are handled appropriately to maintain code robustness and prevent unexpected behavior.

The GemError enum defines the possible values corresponding to the outcome of an operation. The following values indicate successful outcomes:

  • success: The operation completed successfully.
  • reducedResult: The operation completed successfully, but only a subset of the results was returned.
  • scheduled: The operation has been scheduled for execution at a later time.

Other SDK methods might result in failure in certain cases (if the system runs out of memory for example), even if they don't return a GemError value. In this cases, the error code can be obtained via the ApiErrorService class. After each operation the error code can be consulted in the following way:

// Any SDK call...
GemError error = ApiErrorService.apiError;
if (error == GemError.success) print("The last operation succeeded.");
else print("The last operation failed with error: $error");
warning

Make sure to check the error code using the ApiErrorService.apiError getter immediately after the operation completes. Delaying this check could result in other SDK operations being executed, which might overwrite the error code.

Get notified about operation error code changes

The ApiErrorService class also provides a way to get notified when a SDK operation error code gets updated. The following snippet will register a listener which will be called each time the ApiErrorService.apiError value updates.

ApiErrorService.registerOnErrorUpdate((error) {
if (error == GemError.success) print("An operation succeeded");
else print("An operation failed with error code $error")
});

Units of measurement

The SDK primarily uses SI units for measurements, including:

  • Meter (distance).
  • Second (time).
  • Kilogram (mass).
  • Meters per second (speed).
  • Watts for power (consumption).

The length value and measurement unit used in TTS (text-to-speech) instructions, such as "After 150 meters, bear left," are determined by the unit system configured through the SDKSettings.unitSystem setter.

warning

In certain cases, other units of measurement are used as they are better suited. For instance, the height, length and width fields of the TruckProfile class are measured in centimeters. These exceptions are clearly indicated in the API reference.

Avoid SDK operations inside isolates

The SDK does not support executing provided methods within isolates. The SDK already leverages multithreading internally where applicable. Doing SDK operations inside isolates may lead to exceptions.

Return the result from a callback inside a method

Most time-consuming operations (such as search, route calculation, etc.) use callbacks to return results. To integrate these results into an API user-defined function, a Completer can be used to convert the callback into a Future:

Future<ResultType?> myFunction() async{
final Completer<ResultType?> completer = Completer<ResultType?>();

SomeGemService.doSomething(
onComplete: (error, result){
if (error == GemError.success && result != null){
completer.complete(result);
}
}
)

return completer.future;
}

This approach simplifies user code by converting callback parameters into a Future return type, making asynchronous handling easier and more readable. For more details about the Completer class, refer to the Dart documentation.

Provide SDK logs when reporting issues

Dart level logs

When reporting issues, please include the SDK logs to help us diagnose and resolve problems more efficiently.

To configure logging and print messages to the console, add the following code:

Debug.logCreateObject = true;
Debug.logCallObjectMethod = true;
Debug.logListenerMethod = true;

Debug.logLevel = GemLoggingLevel.all;

The snippet above enable logs for operations at the Dart code level.

Native level logs

There are also logs at the native code level (C++). These logs are written to a file automatically. To get the path of the file, use the Debug.getSdkLogDumpPath method. The logs granularity can be configured using the Debug.setSdkDumpLevel method.

In order to add a new entry to the native logs, use the Debug.log method:

Debug.log(level: GemDumpSdkLevel.info, message: "This is a log message");

In order to set the native logs level, use the Debug.setSdkDumpLevel method:

await Debug.setSdkDumpLevel(GemDumpSdkLevel.verbose);

On Android, all GemLoggingLevel values are supported. On iOS only the GemDumpSdkLevel.silent and GemDumpSdkLevel.verbose levels are supported.

The file path of the native logs can be obtained using the Debug.getSdkLogDumpPath method:

String logFilePath = await Debug.getSdkLogDumpPath();

Only the logs with a level equal to or higher than the configured level will be recorded in the log file.

Please provide both Dart and native logs when reporting issues.

warning

The logs may contain sensitive information, so review them before sharing publicly.

Other useful information

When reporting issues, please provide the following information to help us diagnose and resolve the problem effectively:

  • Clear and concise description of the bug or issue.
  • Steps to reproduce the issue, including a minimal code sample if possible.
  • Expected behavior versus actual behavior you are experiencing.
  • Screenshots or videos to help visualize the issue (if applicable).
  • SDK version you are using.
  • Platform details (e.g., iOS/Android, OS version, device model).
  • Console logs printed during the issue.
  • Geographical location where the issue occurred, if relevant (for example, routing, navigation, or search-related issues).
  • Any other relevant information that could assist us in diagnosing and resolving the issue.
  • Additionally, ensure that your SDK is up-to-date, as newer versions often include bug fixes and performance improvements.

Changing resources safely

Some features are initialized before GemKit.initialize() is called, meaning certain resources may already be loaded at that point. This improves the startup speed in applications. Manually managing resources (such as maps, styles, icons, etc.) can lead to crashes or cause changes to be ignored, especially on Android devices.

If the resources need to be changed manually, follow one of the following approaches:

Disable early init

Open the android/build.gradle file and add the following configuration inside the buildTypes block:

    buildTypes {
debug {
buildConfigField "boolean", "USE_EARLY_INIT", "false"
}
release {
buildConfigField "boolean", "USE_EARLY_INIT", "false"
}
profile {
buildConfigField "boolean", "USE_EARLY_INIT", "false"
}
}

Do a SDK init and release

Initialize and release the SDK before modifying any resources:

await GemKit.initialize(...);
await GemKit.release();

// Modify the resources

await GemKit.initialize(...);
await GemKit.release();

This ensures that any currently loaded resources are properly unloaded before changes are applied, preventing conflicts or ignored updates.

Avoid name conflicts

The Route class from the SDK may conflict with the Route class from Flutter. To avoid this, import the Flutter material package while hiding the Route class:

import 'package:flutter/material.dart' hide Route;

Some features, such as police and speed camera reports, may be illegal in certain countries. Ensure that the safety overlay and the corresponding entries in socialReports overlay are disabled where applicable. Additionally, restrict the availability of the SocialOverlay features based on current local regulations.

The Magic Lane Flutter SDK utilizes data from OpenStreetMap. Please ensure proper attribution is given in accordance with the OpenStreetMap license.

Any use or display of Wikipedia content must include appropriate attribution, as outlined in the Reusers' rights and obligations section of Wikipedia's copyright guidelines.