Skip to main content
GuidesAPI ReferenceExamplesFAQ

Usage guidelines

Estimated reading time: 11 minutes

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.

Working with images

The images are represented in an abstract form which is not directly drawable on the UI. The responsible classes are Img, AbstractGeometryImg, LaneImg, SignpostImg, and RoadInfoImg.

These classes provide the following methods and getters:

  • uid: Gets the unique ID of the image. If two images are the same, then they have the same UID. This allows the user to optimize UI redraws and trigger an update only when the image changes.
  • isValid: Verifies if the image data is valid. If the image is not valid, then getRenderableImageBytes and getRenderableImage will return null.
  • getRenderableImageBytes: Returns a Uint8List which can be displayed on the UI using the Image.memory constructor. It returns null if the image is invalid.
  • getRenderableImage: Returns a RenderableImg which contains the image size and the Uint8List that can be drawn on the UI. It returns null if the image is invalid.

Plain images

The Img class corresponds to plain (usually non-vector) images. These images have a recommended size and aspect ratio but can be resized to any dimension in order to be drawn (with possible loss of quality).

The user can instantiate an Img class:

  • Using the constructor which takes the Uint8List image data and format.
  • Using the async fromAsset factory method which takes the asset key (and optionally a custom bundle).

Special images

The AbstractGeometryImg, LaneImg, SignpostImg, and RoadInfoImg classes correspond to vector images generated by the SDK based on internal data. They provide customizable options for rendering. They do not have a default size or aspect ratio.

The LaneImg, SignpostImg, and RoadInfoImg classes might be best displayed at a particular aspect ratio depending on the image content. To render the images at a suitable size chosen by the SDK based on the user-provided size, set the allowResize parameter of the getRenderableImage/getRenderableImageBytes methods to true. If the allowResize parameter is set to false, then rendering will always be done at the size specified by the user (the resulting image might be distorted).

Tip

Use the getRenderableImage method instead of getRenderableImageBytes when setting the allowResize parameter to true. This way, the size of the rendered image is also provided within the resulting RenderableImg class.

The particularities of each image type are presented in the table below:

ClassSize and aspect ratioCustomizable render optionsThe size of the renderable image returned by the getRenderableImage/getRenderableImageBytes methodsInstantiable by the user
ImgUsually fixed and retrievable via the size and aspectRatio getters.Not availableWill always render at the size specified by the user if provided, or the recommended size for the particular image otherwise.Yes, via the provided constructor and the fromAsset static method. It can be also provided by the SDK.
AbstractGeometryImgGenerated by the SDK. Size and aspect ratio not retrievable.Yes, via AbstractGeometryImageRenderSettingsWill always render at the size specified by the user if provided, or the SDK default image size otherwise.No. Can only be provided by the SDK.
LaneImgGenerated by the SDK. Size and aspect ratio not retrievable.Yes, via LaneImageRenderSettingsWill render at the size specified by the user if the allowResize parameter is false. If allowResize is true, the SDK will render at an aspect ratio suitable for the particular image based on the size provided by the user.No. Can only be provided by the SDK.
SignpostImgGenerated by the SDK. Size and aspect ratio not retrievable.Yes, via SignpostImageRenderSettingsWill render at the size specified by the user if the allowResize parameter is false. If allowResize is true, the SDK will render at an aspect ratio suitable for the particular image based on the size provided by the user.No. Can only be provided by the SDK.
RoadInfoImgGenerated by the SDK. Size and aspect ratio not retrievable.Background color customizable via ColorWill render at the size specified by the user if the allowResize parameter is false. If allowResize is true, the SDK will render at an aspect ratio suitable for the particular image based on the size provided by the user.No. Can only be provided by the SDK.
tip

For debugging purposes, the images in Uint8List format can be encoded as Base64 strings using base64Encode and viewed on a Base64 image decoding website. This approach allows for easy inspection of image data during development.

Approaches

The different approaches regarding getting the images in a Uint8list form from the SDK are the following, ilustrated for the lane image contained within NavigationInstruction. The general principles are the same for all types of images.

Approach 1

Uint8List? imageData = instruction.getLaneImage(
size: const Size(200, 100),
format: ImageFileFormat.png,
);

This approach is simple and straightforward, with minimal boilerplate code. However, it comes with a few limitations:

  • Allowing the engine to provide the image using a suitable size/aspect ratio is not available (for now). All images will be returned at the size explicitly requested by the user.
  • There is no way to retrieve the recommended image size based on the content.
  • The image UID is not accessible to the user.

The getLaneImage method returns null if the image is invalid.

Approach 2

LaneImg laneImage = instruction.laneImg;
Uint8List? imageData = newImage!.getRenderableImageBytes(size: const Size(200, 100), format: ImageFileFormat.png, allowResize : true);

// Get more data about the lane image
int uid = laneImage.uid;

This approach allows the user to configure the allowResize parameter but does not provide metadata about the provided Uint8List image:

  • When allowResize is set to true, the returned Uint8List image may be resized to a more suitable size, which could differ from the requested dimensions. However, the actual size of the returned image is not exposed to the user.
  • When allowResize is set to false, the image will be returned at the exact size requested, though it may have an improper aspect ratio.

The user is able to efficiently use the image UID in order to optimize redraws.

The getRenderableImageBytes method returns null if the image is invalid.

Approach 3

LaneImg laneImage = instruction.laneImg;
RenderableImg? renderableImage = newImage.getRenderableImage(size: const Size(200, 100), format: ImageFileFormat.png, allowResize : true);
Uint8List? imageData = renderableImage?.bytes;

// Get more data about the lane image
int uid = laneImage.uid;

// Get the actual size of the image
int? width = renderableImage?.width;
int? height = renderableImage?.height;

This approach provides greater flexibility by allowing the user to configure the allowResize parameter and allows the user to get metadata about the provided Uint8List image:

  • When allowResize is set to true, the returned Uint8List image may be resized to a more suitable size, which could differ from the one originally requested.
  • The actual dimensions of the returned image can be accessed via the getters in the RenderableImg object.
  • When allowResize is set to false, the image will be returned at the exact size requested, though it may have an unsuitable aspect ratio.

The user can leverage the image UID to optimize redraw operations efficiently.

The getRenderableImage method returns null if the image is invalid.

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.

  • 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

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;

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.