# Migrate to 3.1.12

Last updated:

<!-- -->

October 18, 2026

<!-- -->

\| 4 minutes read

<br />

<br />

This guide outlines the breaking changes introduced in SDK version 3.1.12. Required updates may vary depending on your use case.

Additionally, new features and bugfixes have been introduced and are **not** documented here. For a comprehensive list of changes, please refer to the changelog.

This release makes the position listener registration methods nullable, changes the return type of several `RouteBookmarks` methods and extends two enums with new values. Route waypoint bridges are now drawn by default. Most projects only need updates if they store the result of `addPositionListener`, use exhaustive `switch` statements over the affected enums or depend on the previous route rendering.

## The *addPositionListener* and *addImprovedPositionListener* methods of the *PositionService* class now return a nullable listener[​](#the-addpositionlistener-and-addimprovedpositionlistener-methods-of-the-positionservice-class-now-return-a-nullable-listener "Direct link to the-addpositionlistener-and-addimprovedpositionlistener-methods-of-the-positionservice-class-now-return-a-nullable-listener")

Both methods now accept an optional `deliveryPolicy` parameter and their return type changed from `GemPositionListener` to `GemPositionListener?`. The methods return `null` when the registration is refused, which happens when the provided `DataDeliveryPolicy` was created for a different data type or has already been released.

Update the variables and fields holding the result to a nullable type and handle the `null` case.

Before:

```dart
GemPositionListener listener = PositionService.addPositionListener((GemPosition position) {
  // Handle the position update
});

```

After:

```dart
GemPositionListener? listener = PositionService.addPositionListener(
  (GemPosition position) {
    // Handle the position update
  },
);

if (listener == null) {
  // Registration failed, no updates will be delivered
}

```

When no `deliveryPolicy` is passed the registration behaves as before and only fails if the native registration itself fails. The `deliveryPolicy` of `addPositionListener` must be created for `DataType.position` and the one of `addImprovedPositionListener` for `DataType.improvedPosition`.

## The *add*, *update* and *remove* methods of the *RouteBookmarks* class now return a *GemError*[​](#the-add-update-and-remove-methods-of-the-routebookmarks-class-now-return-a-gemerror "Direct link to the-add-update-and-remove-methods-of-the-routebookmarks-class-now-return-a-gemerror")

These methods previously returned `void`, leaving no way to tell whether the operation succeeded. They now return a `GemError`, so failures such as a duplicate route name or an invalid index can be handled.

Existing call sites keep compiling because the returned value can be ignored, but we recommend checking it.

Before:

```dart
bookmarks.add('My route', waypoints);

```

After:

```dart
final GemError error = bookmarks.add('My route', waypoints);

if (error != GemError.success) {
  // Handle the failure, for example GemError.exist when the name is already used
}

```

The `add` method returns `GemError.exist` when the name is already used and `overwrite` is `false`, `GemError.invalidInput` when the name is empty and `GemError.general` when the route could not be stored. The `update` method additionally returns `GemError.notFound` when the index does not identify a route, and `remove` returns `GemError.general` when the route could not be deleted.

## The *cadence*, *activity*, *twoWheelOdometry*, *fourWheelOdometry* and *improvedNmeaChunk* values have been added to the *DataType* enum[​](#the-cadence-activity-twowheelodometry-fourwheelodometry-and-improvednmeachunk-values-have-been-added-to-the-datatype-enum "Direct link to the-cadence-activity-twowheelodometry-fourwheelodometry-and-improvednmeachunk-values-have-been-added-to-the-datatype-enum")

The `DataType` enum now also covers cadence, activity, two-wheel and four-wheel odometry data, together with the NMEA chunk derived from a fused or dead-reckoned position. If you use this enum in an exhaustive `switch` statement, add a case for each new value.

The numeric id of `DataType.unknown` changed from `16` to `20` because the new values were inserted before it. Always use the enum value names instead of hardcoding the numeric ids to ensure compatibility with future changes, and do not persist the ids across SDK versions.

## The *showWaypointBridges* value has been added to the *RouteRenderOptions* enum and is enabled by default[​](#the-showwaypointbridges-value-has-been-added-to-the-routerenderoptions-enum-and-is-enabled-by-default "Direct link to the-showwaypointbridges-value-has-been-added-to-the-routerenderoptions-enum-and-is-enabled-by-default")

The `showWaypointBridges` value was added to the `RouteRenderOptions` enum and is part of the default option set of the `RouteRenderSettings` class. A waypoint bridge is an arc drawn between the map position of a waypoint and its position on the route, displayed only when the distance between the two is relevant. If you use this enum in an exhaustive `switch` statement, add a case for the new `showWaypointBridges` value.

To keep the previous rendering, pass an explicit option set without it.

```dart
RouteRenderSettings settings = RouteRenderSettings(
  options: <RouteRenderOptions>{
    RouteRenderOptions.showTraffic,
    RouteRenderOptions.showTurnArrows,
    RouteRenderOptions.showWaypoints,
    RouteRenderOptions.showHighlights,
  },
);

```

The appearance of the bridges is controlled through the new `waypointBridgeInnerColor`, `waypointBridgeOuterColor`, `waypointBridgeInnerSz`, `waypointBridgeOuterSz` and `waypointBridgeLineType` properties of the `RouteRenderSettings` class.

## The Android plugin build files were updated[​](#the-android-plugin-build-files-were-updated "Direct link to The Android plugin build files were updated")

The plugin's own Android build files and the bundled example were updated to Gradle 9.3.1, Android Gradle Plugin 9.1.0 and Kotlin 2.4.0. Applications keep building the plugin with their own toolchain, so no change is required on your side.

The plugin now applies the Kotlin Android plugin itself whenever the Android Gradle Plugin's built-in Kotlin support is not in effect, which is the case on AGP 8.x and on AGP 9 projects where `android.builtInKotlin=false` is set, as the Flutter app template does. Migrating your project to built-in Kotlin, recommended when upgrading to 3.1.10, is therefore no longer required for the plugin to build.
