mapbox-maps-flutter icon indicating copy to clipboard operation
mapbox-maps-flutter copied to clipboard

onMapTap called after onPointAnnotationClick on iOS

Open dko-orion opened this issue 1 year ago • 5 comments

Hi!

In our project we have functionality when you tapping on annotation it's state set to selected, and when you tap outside of annotation on map it unselects it.

The problem is that on iOS after onPointAnnotationClick triggered and finished processing next triggered onMapTapListener that ruins our logic.

Please note that on Android this problem assumably have been solved by this PR natively.

Also I provide debug of iOS native swift code and there are to method channels called one after another (one for point annotation tap, second is map tap).

So question is it possible to harmonise solutions for both platforms and use the same approach as used in Android?

See code example:
import 'package:flutter/services.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MapboxMap(),
    );
  }
}

class MapboxMap extends StatefulWidget {
  const MapboxMap({super.key});

  @override
  State<MapboxMap> createState() => _MapboxMapState();
}

class _MapboxMapState extends State<MapboxMap> {
  @override
  Widget build(BuildContext context) {
    String accessToken = const String.fromEnvironment("PUBLIC_ACCESS_TOKEN");
    var position = Position(-92.9, 35.04);

    return Scaffold(
      body: MapWidget(
        key: const ValueKey("mapWidget"),
        cameraOptions: CameraOptions(
          center: Point(coordinates: position).toJson(),
          zoom: 12.0,
        ),
        resourceOptions: ResourceOptions(accessToken: accessToken),
        onMapCreated: (controller) async {
          final ByteData bytes =
              await rootBundle.load('assets/symbols/custom-icon.png');
          final Uint8List list = bytes.buffer.asUint8List();

          controller.annotations
              .createPointAnnotationManager()
              .then((annotationManager) {
            annotationManager.addOnPointAnnotationClickListener(
              _AnnotationClickListener(
                (annotation) {
                  print('Tapped on annotation at ${annotation.geometry}');
                },
              ),
            );

            annotationManager.create(
              PointAnnotationOptions(
                geometry: Point(coordinates: position).toJson(),
                image: list,
              ),
            );
          });

          controller.setOnMapTapListener(
            (coordinate) {
              print(
                  'onMapTapListener fired at coordinates:[${coordinate.y},${coordinate.x}]');
            },
          );
        },
      ),
    );
  }
}

class _AnnotationClickListener extends OnPointAnnotationClickListener {
  final void Function(PointAnnotation annotation) _callback;

  _AnnotationClickListener(this._callback);

  @override
  void onPointAnnotationClick(PointAnnotation annotation) {
    _callback(annotation);
  }
}
Result output: image

dko-orion avatar Feb 29 '24 12:02 dko-orion

Hi @dko-orion, thank you for writing in. I have confirmed that this is an issue on our side, I have created an internal ticket https://mapbox.atlassian.net/browse/MAPSFLT-192 to track the progress.

maios avatar Mar 15 '24 09:03 maios

Any workarounds in the meantime?

Edit: The workaround for me was to implement a buffer that selects one tap event out of multiple that happen at the same time.

jnorkus avatar Aug 23 '24 09:08 jnorkus

Hey @maios is there any update on this issue? It is currently blocking us using this official plugin.

brandon-watkins-avcrm avatar Oct 01 '24 01:10 brandon-watkins-avcrm

Hey @maios is there any update on this issue? It is currently blocking us using this official plugin.

I don't know what your requirements are but if it's just recognizing annotation tap then it's quite easy to work around and should not block you. Use a StreamController and buffer your events - select only the one that you need at a time.

jnorkus avatar Oct 01 '24 07:10 jnorkus