All pages
Powered by GitBook
1 of 4

Loading...

Loading...

Loading...

Loading...

Controllers & Cameras

flutter_map makes use of InheritedModel to share 3 'aspects' with its built children:

  • MapController: use to programatically control the map camera & access some helper functionality - control camera

  • MapCamera: use to read the current state/position of the map camera & access some helper functionality that depends on the camera (such as latlngToPoint) - read camera

All 3 aspects can be retrieved from within the context of a FlutterMap, which all built descendants should have access to. This usually means from within a layer: anywhere where there is at least one 'visible' builder method between the FlutterMap and the invocation.

Use the static of (or null-safe maybeOf) method to access the inherited aspect. For example, to access the MapCamera:

This will attach the widget to the state of the map, causing it to rebuild whenever the depended-on aspects change. See for more information.

To access the MapCamera outside of a FlutterMap descendant, first .

Then use the camera getter on the MapController instance.

For more information about correctly setting up an external(ly accessible) MapController, see:

final inheritedCamera = MapCamera.of(context);

MapOptions is also an aspect, which reflects the MapOptions defined on the FlutterMap.options parameter.

However, it is mostly irrelevant, except for when Creating New Layers.

Accessing Aspects Within Descendants

Using this method directly in the children list (not inside another widget), and in any MapOptions callback, is not possible: there is no* builder method between the FlutterMap and the children or callback.

Instead, follow Accessing Aspects Elsewhere, or, wrap the necessary layers with a Builder widget. For example, the code snippet below hides a TileLayer when above zoom level 13:

Accessing Aspects Elsewhere

MapCamera

Avoid using this method to access the camera when MapCamera.of() is available.

MapController

MapOptions

It is not possible to access the MapOptions in this way outside of FlutterMap descendants.

This is because it is not changed by FlutterMap, and so that would be unnecessary.

setup an external MapController, as guided below
External Custom Controllers
children: [
    Builder(
        builder: (context) {
            if (MapCamera.of(context).zoom < 13) return SizedBox.shrink();
            return TileLayer();
        },
    ),
],

Programmatic Interaction

There's two ways to interact with the map - that is to control it, as well as receive data from it - and it's current viewport, aka. 'camera'.

The first way is through user interaction, where they perform gestures (such as drags/pans), and the map reacts automatically to those gestures to change the camera view of the map.

These are usually restricted by . It is possible to disable all input, either by disabling all gestures, or by wrapping the map with something like IgnorePointer.

However, the map camera can also be controlled by calling methods on a controller, and its state read by getting values from an exposed camera.

For more information, see:

via User Gestures

via Programmatic Interation

Changing the state of MapOptions.initial* will not update the map camera. It may only be updated through a MapController.

Options
Controllers & Cameras

External Custom Controllers

For more information about what a MapController is, and when it is necessary to set one up in this way, see:

Controllers & Cameras

Basic Setup

The FlutterMap.controller parameter takes an externally intialised MapController instance, and attaches it to the map.

// Within a widget
final mapController = MapController();

@override
Widget build(BuildContext context) =>
    FlutterMap(
        mapController: mapController,
        // ...
    );

An externally attached controller will be accurately reflected when depending on the MapController aspect.

It is not safe to assume that the MapController is ready to use as soon as an instance has been initialised, for example within initState.

See below for more information.

Usage Before Attachment (eg. Within initState)

It is not safe to assume that the MapController is ready to use as soon as an instance has been initialised, for example within initState.

It must first be attached to the FlutterMap, which could take up to multiple frames to occur (similar to the way a ScrollController is attached to a scrollable view). It helps to avoid errors by thinking of the controller in this way.

For example, it is sometimes necessary to use a controller in the initState() method (for example to ). However, because this method executes before the widget has been built, a controller defined here will not be ready for use.

Instead, use the MapOptions.onMapReady callback. At this point, it is guaranteed that the controller will have been attached. You could also use this method to complete a Completer (and await its Future elsewhere) if you need to use it elsewhere.

Instead, some extra care should be taken, which may feel a little backwards at first. The state model should be used AFTER the normal setup.

  1. Setup the controller as in , where the MapController is defined & initialised directly adjacent to the FlutterMap

  2. In your state model, create a nullable (and initially uninitialised) MapController containing field

It may then be beneficial to unset the state model field when the controller is disposed: it should be disposed when the FlutterMap is disposed, which should occur just before the widget building the FlutterMap is disposed. Therefore, you can override the dispose method.

Whilst animated movements through MapControllers aren't built-in, the provides this, and much more!

Follow
to setup an onMapReady callback. Then within this callback, set the state model field.
final mapController = MapController();

@override
void initState() {
    // Cannot use `mapController` safely here
}

@override
Widget build(BuildContext context) {
    return FlutterMap(
        mapController: mapController,
        options: MapOptions(
            onMapReady: () {
                mapController.mapEventStream.listen((evt) {}); // for example
                // Any* other `MapController` dependent methods
            },
        ),
    );
}

Use of the MapController before it has been attached to a FlutterMap will result in an error being thrown, usually a LateInitialisationError.

It is usually safe to use a controller from within a callback manually initiated by the user without further complications.

MapController methods that change the position of the map should not be used directly (not as a result of another callback) in onMapReady - see . This is an unsupported and usually unnecessary usecase.

Usage Within A State System/Model

Don't define/intialise the a MapController within a class or widget that doesn't also contain the FlutterMap, such as a state model (eg. ChangeNotifier), then try to use it by querying the state in the FlutterMap.controller parameter.

Animation

attach an event listener
community maintained plugin flutter_map_animations
Basic Setup
issue #1507
Usage Before Attachment (eg. Within initState)

Listen To Events

To cause a widget inside FlutterMap's context to rebuild when an changes, see .

When the state of a MapCamera changes, because of an update to its position or zoom, for example, a MapEvent, which can be handled by you.

Catching All Events

There's two methods to catch all emitted MapEvents. These methods expose the raw MapEvent, and is recommended in cases where multiple events need to be caught, or there's no more specific callback method available in MapOptions (see Catching Specific Events).

  • Listening to a 's mapEventStream, which exposes events via a Stream

  • Specifying a callback method in MapOptions.onMapEvent

If only a couple of events need to be caught, such as just an onTap handler, it is possible to avoid handling the raw Stream of MapEvents. Instead, MapOptions has callbacks available for the following events:

  • onTap

  • onLongPress

  • onPositionChanged

onPointerDown/onPointerUp/onPointerHover/onPointerCancel

  • onMapReady Primarily used for advanced MapController Controllers & Cameras

  • Catching Specific Events

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

    If this causes noticeable jank or a bad experience (for example, on desktop platforms), disable InteractiveFlag.doubleTapZoom:

    options: MapOptions(
        interactiveFlags: ~InteractiveFlag.doubleTapZoom,
    ),

    This disables the double tap handler, so the MapEventTap is emitted 'instantly' on tap.

    MapController
    aspect
    2. Hooking Into Inherited State
    2. Hooking Into Inherited State