For the complete documentation index, see llms.txt. This page is also available as Markdown.

Programmatic Interaction

In addition to the user interacting with the map, for example by using their cursor, trackpad, or touchscreen, the map can be controlled programmatically.

Programmatic interaction consists of two parts: reading and setting information about the map.

To use programmatic interaction, it's important to understand the difference between the map's 'camera' and the map's 'controller', and their relationship.

Camera

The map's camera - or MapCamera - is an object that holds information to be read about the map's current viewport, such as:

  • the coordinates of the geographic location at the center of the map

  • the current rotation of the map (in degrees, where 0° is North)

  • the current zoom level of the map

For example:

Controller

Similarly to other map client projects, the controller - MapController - allows the map's viewport/camera to be modified/set using methods, such as:

  • move the camera to a new location and zoom level, using either:

    • move, which accepts a new center coordinate and zoom level

    • fitCamera, which allows more advanced positioning using coordinate boundaries and screen-space padding

  • rotate the camera to a new number of degrees from North

For example:

Accessing the 'aspects'

Together, the MapCamera and MapController are two aspects of the MapInheritedModel.

MapOptions is also an aspect of the same inherited model, but this is not useful here since it cannot be exposed outside of map layers.

This means that you can get both the camera and the controller from within a layer of the map, given the map's BuildContext.

The MapCamera.of & MapController.of methods accept this context, and return their respective aspect. They also subscribe the layer for further updates: using MapCamera.of means that widget/layer will rebuild every time the MapCamera changes (for example, when the map's location changes).

For example:

If you want to access either aspect from outside of the map - for example, to reposition the map when the user presses a button, or to use the map's location in an API call - you'll need to:

1

Declare an external MapController

The MapController object can be constructed, like in the example below. We recommend using a StatefulWidget.

The MapController works similarly to a ScrollController.

2

Attach the controller to the map

The default controller that FlutterMap uses and passes around internally can be overridden with the external controller using FlutterMap.controller.

For example:

Then, the external controller controls the map, and you can access the MapCamera from outside of the map using MapController.camera. For example:

Pitfalls of an external controller

The controller is not safe to use until attached

Attachment to a map usually takes a single frame and is done in the initial widget build, but it's good practise not to assume this.

The controller definitely can not be used in initState (at least without adding a post-frame callback, which is not a recommended practise).

In most use cases, the controller will be used to respond to user actions, such as the onPressed callback of a button. In this case, it's usually safe to assume the controller is ready to use - unless, for example, there is a FutureBuilder wrapped around the FlutterMap which has not built the map yet.

If you need to hook into when the controller will be attached to the map, use MapOptions.onMapReady. For example:

Construct the controller in the widget tree & don't pass it up the tree

To avoid issues, it's best practise to construct the MapController in the widget tree (for example, as a field on a State), either in a parent of the map-containing widget, or in the same widget.

Therefore, don't construct the controller directly in a state model, such as Provider or Bloc. Construct the controller as far up the widget tree as is necessary to ensure all widgets that need it are children, then use a dependency injection or inheritance to pass it to children (or pass it into a state model when constructing it).

The MapController does not animate the map

When you use a method on the controller, the map state is updated immediately.

If you want to use animations, consider using the community maintained plugin flutter_map_animations.

Reacting to map events

To imperatively react to changes to the map camera, there's multiple methods available.

Remember that when using MapCamera.of, that widget will automatically rebuild when the camera changes.

Simple events

If you prefer a callback-based pattern and need to capture user interaction events with the widget (with limited information about the event or its effect on the map itself), the following callbacks are available through MapOptions:

  • onTap

  • onLongPress

  • onPointerDown/onPointerMove/onPointerUp/onPointerHover/onPointerCancel

These callbacks are also available, which are not strictly caused by user interaction events and give information about the map, but are provided for ease-of-use:

  • onPositionChanged: called when the map moves

  • onMapReady: called when the MapController is attached

For example:

The onTap callback and MapEventTap event may be emitted 250ms after the actual tap occurred, as this is the acceptable delay between the two taps in a double tap zoom gesture.

If your project would benefit from a faster reaction to taps, disable the double tap zoom gesture, which will allow taps to be handled immediately:

Multiple or complex event handling

There's two methods to handle raw MapEvents, which are emitted whenever the MapCamera changes, and contain detailed information, such as the source of the event, and, for some events, the old and new camera.

MapOptions has a callback named onMapEvent. For example:

In addition to controlling the map, the MapController also has a getter called mapEventStream. For example:

Last updated

Was this helpful?