Skip to main content
GuidesAPI ReferenceExamplesFAQ

Interact with the map

Estimated reading time: 9 minutes

The Maps SDK for Flutter map view natively supports common gestures like pinch and double-tap for zooming. The table below outlines the available gestures and their default behaviors on the map.

GestureDescription
TapTap the screen with one finger. This gesture does not have a predefined map action.
Double TapTo zoom the map in by a fixed amount, tap the screen twice with one finger.
Long PressPress and hold one finger to the screen. This gesture does not have a predefined map action.
PanTo move the map, press and hold one finger to the screen, and move it in any direction. The map will keep moving with a little momentum after the finger was lifted.
2 Finger Pan / ShoveTo tilt the map, press and hold two fingers to the screen, and move them vertically. No behavior is predefined for other directions.
2 Finger TapTo align map towards north, tap the screen with two fingers.
PinchTo zoom in or out continuously, press and hold two fingers to the screen, and increase or decrease the distance between them. To rotate the map continuously, press and hold two fingers to the screen, and change the angle between them either by rotating them both or by moving one of them.

The SDK provides support in GemMapController, for informing whenever the user performs and action that could be detected. Usually, you will want to add a specific behavior to your application after a gesture was detected, like performing a selection after a tap on map.

  • Tap: registerTouchCallback
  • Double Tap (one finger taps the same area in quick succession): registerDoubleTouchCallback
  • Two Taps (two fingers tap the screen simultaneously): registerTwoTouchesCallback
  • Long Press: registerLongPressCallback
  • Pan: registerMoveCallback obtains the two points between which the movement occurred.
  • Shove: registerShoveCallbackobtains the angle, and gesture specific points
  • Rotate: registerMapAngleUpdateCallback
  • Fling: registerSwipeCallback
  • Pinch: registerPinchCallback

The user can also listen for composite gestures:

  • Tap followed by a pan: registerTouchMoveCallback
  • Pinch followed by a swipe: registerPinchSwipeCallback
  • Tap followed by a pinch: registerTouchPinchCallback
  • Two double touches: registerTwoDoubleTouchesCallback
warning

Keep in mind that only one listener can be active at a time for a specific gesture. If multiple listeners are registered, only the most recently set listener will be invoked when the gesture is detected.

Use registerOnMapViewMoveStateChanged to retrieve the corresponding RectangleGeographicArea currently visible whenever the map starts or stops moving.

mapController.registerMapViewMoveStateChangedCallback((hasStarted, rect) {
if (hasStarted) {
print('Gesture started at: ${rect.topLeft.toString()} , ${rect.bottomRight.toString()}');
} else {
print('Gesture ended at: ${rect.topLeft.toString()} , ${rect.bottomRight.toString()}');
}
});
warning

This callback is triggered when the camera is moved programmatically using methods like centerOnRoutes, followPosition, or centerOnArea, but not when the user performs a panning gesture. For detecting user behaviour, use registerMoveCallback.

Note

The callback function is defined as void Function(bool isCameraMoving, RectangleGeographicArea area), where the isCameraMoving parameter is true when the camera is moving and false when it is stationary.

Enable and disable gestures

Touch gestures can be disabled or enabled by calling enableTouchGestures method like so:

mapController.preferences.enableTouchGestures([TouchGestures.onTouch, TouchGestures.onMove], false);
Note

The desired gestures in the TouchGestures enum list can be enabled or disabled by setting the enabled parameter to true or false, respectively. By default, all gestures are enabled.

The TouchGestures enum supports the following gesture types:

  • Basic Touch: onTouch, onLongDown, onDoubleTouch, onTwoPointersTouch, onTwoPointersDoubleTouch
  • Movement: onMove, onTouchMove, onSwipe
  • Pinch and Rotation: onPinchSwipe, onPinch, onRotate, onShove
  • Combined Gestures: onTouchPinch, onTouchRotate, onTouchShove, onRotatingSwipe
  • Other: internalProcessing

For checking if a gesture is enables the isTouchGestureEnabled method can be used:

bool isTouchEnabled = mapController.preferences.isTouchGestureEnabled(TouchGestures.onTouch);

Implement gesture listeners

Let's see an example of how gesture listeners can be registered. The GemMapController provides specific listeners for each gesture. As soon as you register a listener, it will receive all related events for that gesture via the dedicated callback.

// previous code

// attach the _onMapCreated callback to GemMap
GemMap(
onMapCreated: _onMapCreated,
appAuthorization: projectApiToken,
),

void _onMapCreated(GemMapController mapController) async {
mapController = mapController;

mapController.registerMapAngleUpdateCallback((angle) {
print("Gesture: onMapAngleUpdate $angle");
});

mapController.registerTouchCallback((point) {
print("Gesture: onTouch $point");
});

mapController.registerMoveCallback((point1, point2) {
print('Gesture: onMove from (${point1.x} ${point1.y}) to (${point2.x} ${point2.y})');
});

mapController.registerOnLongPressCallback((point) {
print('Gesture: onLongPress $point');
});

mapController.registerMapViewMoveStateChangedCallback((hasStarted, rect) {
if (hasStarted) {
print('Gesture started at: ${rect.topLeft.toString()} , ${rect.bottomRight.toString()}');
} else {
print('Gesture ended at: ${rect.topLeft.toString()} , ${rect.bottomRight.toString()}');
}
});
}
warning

Executing resource-intensive tasks within map-related callbacks can degrade performance.

Implement map render listeners

The registerViewportResizedCallback method allows you to monitor when the map's viewport dimensions change. This can occur when the user resizes the application window or changes the orientation of the device. In this callback, you receive a Rectangle<int> object representing the new viewport size.

mapController.registerViewportResizedCallback((Rectangle<int> rect) {
print("Viewport resized to: ${rect.width}x${rect.height}");
});

Use cases include:

  • Adjusting overlays or UI elements to fit the new viewport size.
  • Triggering animations or updates based on the map's dimensions.

The registerViewRenderedCallback method is triggered after the map completes a rendering cycle. This listener provides a MapViewRenderInfo object containing details about the rendering process.

mapController.registerViewRenderedCallback((MapViewRenderInfo renderInfo) {
print("View rendered: ${renderInfo.status}");
});

Map selection functionality

After detecting a gesture, such as a tap, usually some specific action like selecting a landmark or a route is performed on GemMap. This selection is made using a map cursor, which is invisible by default. To showcase its functionality, the cursor can be made visible using the MapViewPreferences setting:

void _onMapCreated(GemMapController mapController) {
// Save mapController for further usage.
mapController = mapController;

// Enable cursor (default is true)
mapController.preferences.enableCursor = true;
// Enable cursor to render on screen
mapController.preferences.enableCursorRender = true;
}

Doing this inside maps's onMapCreated callback will result in a crosshair like icon in center of screen.

hello_map
Displaying a cursor

Landmark selection

To get the selected landmarks, you can use the following code (possibly placed in the onMapCreated callback):

mapController.registerTouchCallback((pos) async {
// Set the cursor position.
await mapController.setCursorScreenPosition(pos);

// Get the landmarks at the cursor position.
final landmarks = mapController.cursorSelectionLandmarks();

for(final landmark in landmarks) {
// handle landmark
}
});
Note

At higher zoom levels, landmarks provided by the cursorSelectionLandmarks method may lack some details for optimization purposes. Use SearchService.searchLandmarkDetails to retrieve full landmark details if needed.

To unregister the callback:

mapController.registerTouchCallback(null);
Note

The selected landmarks are returned by the cursorSelectionLandmarks function, which is called after updating the cursor's position. This step is essential because the SDK only detects landmarks that are positioned directly under the cursor.

warning

The cursor screen position is also used for determining the default screen position for centering (unless other values are specified). Modifying the screen position might change the behavior of centering in unexpected ways. Reset the cursor position to the center of the screen using the resetMapSelection method from the GemMapController (needs to be awaited).

Street selection

The following code can be used inside _onMapCreated callback in order to return selected streets under the cursor:

// Register touch callback to set cursor to tapped position
mapController.registerTouchCallback((point) async {
await mapController.setCursorScreenPosition(point);
final streets = mapController.cursorSelectionStreets();

String currentStreetName = streets.isEmpty ? "Unnamed street" : streets.first.name;
});
warning

Setting the cursor screen position is an asynchronous operation and each function call needs to be awaited. Otherwise, the result list may be empty.

Street name can then be displayed on screen. This is the result:

hello_map
Displaying a cursor selected street name
Note

The visibility of the cursor has no impact whatsoever on the selection logic.

Getting the current cursor screen position is done by calling cursorScreenPosition getter of GemMapController. Resetting the cursor position to its default location (the center of the screen) is done by resetMapSelection (needs to be awaited).

List of selection types

To summarize, there are multiple methods used to select different types of elements on the map. You can see all those in the following table.

EntitySelect methodResult typeObservations
LandmarkcursorSelectionLandmarksList<Landmark>
MarkercursorSelectionMarkersList<MarkerMatch>Returns MarkerMatch, not a Marker
OverlayItemcursorSelectionOverlayItemsList<OverlayItem>
StreetcursorSelectionStreetsList<Landmark>Streets are handled as landmarks
RoutecursorSelectionRoutesList<Route>
PathcursorSelectionPathPath?Null is returned if no path is found
MapSceneObjectcursorSelectionMapSceneObjectMapSceneObject?Null is returned if no map scene object is found

As you can see, when selecting markers a list of MarkerMatch elements is returned. The match specifies information about the matched marker (the marker collection in which the marker resides, the index of the marker in the collection, the matched part index, the matched index of the point in the part).

You can also register callbacks that are called when the cursor is placed over elements on the GemMapController class:

  • registerCursorSelectionUpdatedLandmarksCallback for landmarks
  • registerCursorSelectionUpdatedMarkersCallback for markers
  • registerCursorSelectionUpdatedOverlayItemsCallback for overlay items
  • registerCursorSelectionUpdatedRoutesCallback for routes
  • registerCursorSelectionUpdatedPathCallback for paths
  • registerCursorSelectionUpdatedTrafficEventsCallback for traffic events
  • registerCursorSelectionUpdatedMapSceneObjectCallback for map scene objects

These callbacks are triggered whenever the selection changes — for example, when new elements are selected, the selection switches to different elements, or the selection is cleared (in which case the callback is invoked with null or an empty list).

To unregister a callback, simply call the corresponding method with null as the argument.