To cause a widget inside FlutterMap
's context to rebuild when an aspect changes, see #id-2.-hooking-into-inherited-state.
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.
There's two methods to catch all emitted MapEvent
s. 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 MapController
'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 MapEvent
s. Instead, MapOptions
has callbacks available for the following events:
onTap
onLongPress
onPositionChanged
onPointerDown
/onPointerUp
/onPointerHover
/onPointerCancel
onMapReady
Primarily used for advanced MapController
#usage-inside-initstate
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
:
This disables the double tap handler, so the MapEventTap
is emitted 'instantly' on tap.
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
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.
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 #id-2.-hooking-into-inherited-state for more information.
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:
MapCamera
To access the MapCamera
outside of a FlutterMap
descendant, first setup an external MapController
, as guided below.
Then use the camera
getter on the MapController
instance.
Avoid using this method to access the camera when MapCamera.of()
is available.
MapController
For more information about correctly setting up an external(ly accessible) MapController
, see:
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.
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 Options. 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.
Changing the state of MapOptions.initial*
will not update the map camera. It may only be updated through a MapController
.
For more information, see:
For more information about what a MapController
is, and when it is necessary to set one up in this way, see:
The FlutterMap.controller
parameter takes an externally intialised MapController
instance, and attaches it to the map.
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.
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.
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.
For example, it is sometimes necessary to use a controller in the initState()
method (for example to attach an event listener). 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.
MapController
methods that change the position of the map should not be used directly (not as a result of another callback) in onMapReady
- see issue #1507. This is an unsupported and usually unnecessary usecase.
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.
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.
Setup the controller as in #basic-setup, where the MapController
is defined & initialised directly adjacent to the FlutterMap
In your state model, create a nullable (and initially uninitialised) MapController
containing field
Follow #usage-before-attachment-eg.-within-initstate to setup an onMapReady
callback. Then within this callback, set the state model 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 MapController
s aren't built-in, the community maintained plugin flutter_map_animations
provides this, and much more!