Layer Interactivity

Layer interactivity is different to map interactivity. See Interaction Options to control map interactivity.

For information about how hit testing behaves in flutter_map, see Hit Testing Behaviour.

It is important to note that hit testing != interactivity, and hit testing is always executed on interactable layers by default.

The following layers are interactable - they have specialised hitTesters and support external hit detection:

These all follow roughly the same pattern to setup hit detection/interactivity, and there's three or four easy steps to setting it up.

1. Attach A Hit Notifier

Direct callbacks, such as onTap,aren't provided on layers or individual elements, to maximize flexibility.

Pass a LayerHitNotifier to the hitNotifier parameter of the layer. The LayerHitNotifier should be created as a ValueNotifier defaulting to null, but strongly typed to LayerHitNotifier.

This notifier will be notified whenever a hit test occurs on the layer, with a LayerHitResult when an element (such as a Polyline or Polygon) within the layer is hit, and with null when an element is not hit (but the layer is).

final LayerHitNotifier hitNotifier = ValueNotifier(null);

// Inside the map build...
PolylineLayer( // Or any other supported layer
  hitNotifier: hitNotifier,
  polylines: [], // Or any other supported elements
);

It is possible to listen to the notifier directly with addListener, if you want to handle all hit events (including, for example, hover events). However, most use cases just need to handle particular gestures (such as taps). This can be done with a wrapper widget to 'filter' the events appropriately: 3. Gesture Detection.

2. Add hitValue To Elements

To identify which particular element was hit (which will be useful when handling the hit events in later steps), supported elements have a hitValue property.

This can be set to any object, but if one layer contains all the same type, type casting can be avoided (if the type is also specified in the LayerHitNotifier's type argument).

The equality of the element depends on the equality of the hitValue.

Therefore, any object passed to the hitValue should have a valid and useful equality method. Objects such as records do this behind the scenes, and can be a good choice to store small amounts of uncomplicated data alongside the element.

3. Gesture Detection

To only handle certain hits based on the type of gesture the user performed (such as a tap), wrap the layer with a gesture/hit responsive widget, such as GestureDetector or MouseRegion.

These widgets are smart enough to delegate whether they detect a hit (and therefore whether they can detect a gesture) to the child - although HitTestBehavior.deferToChild may be required for some widgets to enable this functionality.

This means the layer can report whether it had any form of hit, and the handler widget can detect whether the gesture performed on it actually triggered a hit on the layer below.

// Inside the map build...
MouseRegion(
  hitTestBehavior: HitTestBehavior.deferToChild,
  cursor: SystemMouseCursors.click, // Use a special cursor to indicate interactivity
  child: GestureDetector(
    onTap: () {
      // Handle the hit, which in this case is a tap
      // For example, see the example in Hit Handling (below)
    },
    // And/or any other gesture callback
    child: PolylineLayer(
      hitNotifier: hitNotifier,
      // ...
    ),
  ),
),

4. Hit Handling

Once a LayerHitResult object is obtained, through the hit notifier, you can retrieve:

  • hitValues: the hitValues of all elements that were hit, ordered by their corresponding element, first-to-last, visually top-to-bottom

  • coordinate: the geographic coordinate of the hit location (which may not lie on any element)

  • point: the screen point of the hit location

If all the hitValues in a layer are of the same type, and the created hit notifier specifies that type in the type argument, typing is preserved all the way to retrieval.

Because the HitNotifier is a special type of ValueNotifier, it can be both listened to (like a Stream), and its value instantly retrieved (like a normal variable). Therefore, there are two ways to retrieve a LayerHitResult (or null) from the notifier:

  • Using .value to instantly retrieve the value This is usually done within a gesture handler, such as GestureDetector.onTap, as demonstrated below.

  • Adding a listener (.addListener) to retrieve all hit results This is useful where you want to apply some custom/advanced filtering to the values, and is not a typical usecase.

// Inside a gesture detector/handler

final LayerHitResult? hitResult = hitNotifier.value;
if (hitResult == null) return;

// If running frequently (such as on a hover handler), and heavy work or state changes are performed here, store each result so it can be compared to the newest result, then avoid work if they are equal 

for (final hitValue in hitResult.hitValues) {}

© flutter_map Authors & Maintainers