Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Google maps marker drag events impl #2838

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.0.11

* Add additional marker drag events.

## 2.0.10

* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,10 +467,14 @@ public boolean onMarkerClick(Marker marker) {
}

@Override
public void onMarkerDragStart(Marker marker) {}
public void onMarkerDragStart(Marker marker) {
markersController.onMarkerDragStart(marker.getId(), marker.getPosition());
}

@Override
public void onMarkerDrag(Marker marker) {}
public void onMarkerDrag(Marker marker) {
markersController.onMarkerDrag(marker.getId(), marker.getPosition());
}

@Override
public void onMarkerDragEnd(Marker marker) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,28 @@ boolean onMarkerTap(String googleMarkerId) {
return false;
}

void onMarkerDragStart(String googleMarkerId, LatLng latLng) {
String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId);
if (markerId == null) {
return;
}
final Map<String, Object> data = new HashMap<>();
data.put("markerId", markerId);
data.put("position", Convert.latLngToJson(latLng));
methodChannel.invokeMethod("marker#onDragStart", data);
}

void onMarkerDrag(String googleMarkerId, LatLng latLng) {
String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId);
if (markerId == null) {
return;
}
final Map<String, Object> data = new HashMap<>();
data.put("markerId", markerId);
data.put("position", Convert.latLngToJson(latLng));
methodChannel.invokeMethod("marker#onDrag", data);
}

void onMarkerDragEnd(String googleMarkerId, LatLng latLng) {
String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId);
if (markerId == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.googlemaps;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import com.google.android.gms.internal.maps.zzt;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodCodec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.mockito.Mockito;

public class MarkersControllerTest {

@Test
public void controller_OnMarkerDragStart() {
final MethodChannel methodChannel =
spy(new MethodChannel(mock(BinaryMessenger.class), "no-name", mock(MethodCodec.class)));
final MarkersController controller = new MarkersController(methodChannel);
final GoogleMap googleMap = mock(GoogleMap.class);
controller.setGoogleMap(googleMap);

final zzt z = mock(zzt.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is class zzt? It's being imported from internal, will it keep its name in the future?

Why not mock Marker instead of zzt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may actually be very fragile if it's actually obfuscated code (with a name like that it could very well be).
I think that zzt might be something to do with the map projection but don't quote me, it's been a year and a half since I wrote that so I'm not totally sure without digging back into the source code as creation of Markers is hidden behind a factory pattern.
I just tested mocking only Marker and the test work fine, I'm not not sure why I didn't do that in the first place.
I'll create a new PR with this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for coming back to this one, I'll take a look at the new PR!!

final Marker marker = new Marker(z);

final String googleMarkerId = "abc123";

when(marker.getId()).thenReturn(googleMarkerId);
when(googleMap.addMarker(any(MarkerOptions.class))).thenReturn(marker);

final LatLng latLng = new LatLng(1.1, 2.2);
final Map<String, String> markerOptions = new HashMap();
markerOptions.put("markerId", googleMarkerId);

final List<Object> markers = Arrays.<Object>asList(markerOptions);
controller.addMarkers(markers);
controller.onMarkerDragStart(googleMarkerId, latLng);

final List<Double> points = new ArrayList();
points.add(latLng.latitude);
points.add(latLng.longitude);

final Map<String, Object> data = new HashMap<>();
data.put("markerId", googleMarkerId);
data.put("position", points);
Mockito.verify(methodChannel).invokeMethod("marker#onDragStart", data);
}

@Test
public void controller_OnMarkerDragEnd() {
final MethodChannel methodChannel =
spy(new MethodChannel(mock(BinaryMessenger.class), "no-name", mock(MethodCodec.class)));
final MarkersController controller = new MarkersController(methodChannel);
final GoogleMap googleMap = mock(GoogleMap.class);
controller.setGoogleMap(googleMap);

final zzt z = mock(zzt.class);
final Marker marker = new Marker(z);

final String googleMarkerId = "abc123";

when(marker.getId()).thenReturn(googleMarkerId);
when(googleMap.addMarker(any(MarkerOptions.class))).thenReturn(marker);

final LatLng latLng = new LatLng(1.1, 2.2);
final Map<String, String> markerOptions = new HashMap();
markerOptions.put("markerId", googleMarkerId);

final List<Object> markers = Arrays.<Object>asList(markerOptions);
controller.addMarkers(markers);
controller.onMarkerDragEnd(googleMarkerId, latLng);

final List<Double> points = new ArrayList();
points.add(latLng.latitude);
points.add(latLng.longitude);

final Map<String, Object> data = new HashMap<>();
data.put("markerId", googleMarkerId);
data.put("position", points);
Mockito.verify(methodChannel).invokeMethod("marker#onDragEnd", data);
}

@Test
public void controller_OnMarkerDrag() {
final MethodChannel methodChannel =
spy(new MethodChannel(mock(BinaryMessenger.class), "no-name", mock(MethodCodec.class)));
final MarkersController controller = new MarkersController(methodChannel);
final GoogleMap googleMap = mock(GoogleMap.class);
controller.setGoogleMap(googleMap);

final zzt z = mock(zzt.class);
final Marker marker = new Marker(z);

final String googleMarkerId = "abc123";

when(marker.getId()).thenReturn(googleMarkerId);
when(googleMap.addMarker(any(MarkerOptions.class))).thenReturn(marker);

final LatLng latLng = new LatLng(1.1, 2.2);
final Map<String, String> markerOptions = new HashMap();
markerOptions.put("markerId", googleMarkerId);

final List<Object> markers = Arrays.<Object>asList(markerOptions);
controller.addMarkers(markers);
controller.onMarkerDrag(googleMarkerId, latLng);

final List<Double> points = new ArrayList();
points.add(latLng.latitude);
points.add(latLng.longitude);

final Map<String, Object> data = new HashMap<>();
data.put("markerId", googleMarkerId);
data.put("position", points);
Mockito.verify(methodChannel).invokeMethod("marker#onDrag", data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'page.dart';

class DragMarkerPage extends GoogleMapExampleAppPage {
DragMarkerPage() : super(const Icon(Icons.drag_handle), 'Drag marker');

@override
Widget build(BuildContext context) {
return const DragMarkerBody();
}
}

class DragMarkerBody extends StatefulWidget {
const DragMarkerBody();

@override
State<StatefulWidget> createState() => DragMarkerBodyState();
}

typedef MarkerUpdateAction = Marker Function(Marker marker);

class DragMarkerBodyState extends State<DragMarkerBody> {
DragMarkerBodyState();
static const LatLng center = LatLng(-33.86711, 151.1947171);

GoogleMapController? controller;
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
MarkerId? selectedMarker;
int _markerIdCounter = 1;
LatLng? markerPosition;

void _onMapCreated(GoogleMapController controller) {
this.controller = controller;
}

void _onMarkerTapped(MarkerId markerId) {
final Marker? tappedMarker = markers[markerId];
if (tappedMarker != null) {
setState(() {
if (markers.containsKey(selectedMarker)) {
final Marker resetOld = markers[selectedMarker]!
.copyWith(iconParam: BitmapDescriptor.defaultMarker);
markers[selectedMarker!] = resetOld;
}
selectedMarker = markerId;
final Marker newMarker = tappedMarker.copyWith(
iconParam: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueGreen,
),
);
markers[markerId] = newMarker;
});
}
}

void _onMarkerDrag(MarkerId markerId, LatLng newPosition) async {
setState(() {
this.markerPosition = newPosition;
});
}

void _add() {
final int markerCount = markers.length;

if (markerCount == 12) {
return;
}

final String markerIdVal = 'marker_id_$_markerIdCounter';
_markerIdCounter++;
final MarkerId markerId = MarkerId(markerIdVal);

final Marker marker = Marker(
draggable: true,
markerId: markerId,
position: LatLng(
center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0,
center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0,
),
infoWindow: InfoWindow(title: markerIdVal, snippet: '*'),
onTap: () => _onMarkerTapped(markerId),
onDrag: (LatLng position) => _onMarkerDrag(markerId, position),
);

setState(() {
markers[markerId] = marker;
});
}

void _remove() {
setState(() {
if (markers.containsKey(selectedMarker)) {
markers.remove(selectedMarker);
}
});
}

@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Center(
child: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: center,
zoom: 11.0,
),
markers: markers.values.toSet(),
),
),
),
Container(
height: 30,
padding: EdgeInsets.only(left: 12, right: 12),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
markerPosition == null
? Container()
: Expanded(child: Text("lat: ${markerPosition!.latitude}")),
markerPosition == null
? Container()
: Expanded(child: Text("lng: ${markerPosition!.longitude}")),
],
),
),
Row(
children: <Widget>[
TextButton(
child: const Text('add'),
onPressed: _add,
),
TextButton(
child: const Text('remove'),
onPressed: _remove,
),
],
),
],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// ignore_for_file: public_member_api_docs

import 'package:flutter/material.dart';
import 'package:google_maps_flutter_example/drag_marker.dart';
import 'package:google_maps_flutter_example/lite_mode.dart';
import 'animate_camera.dart';
import 'map_click.dart';
Expand Down Expand Up @@ -34,6 +35,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
PlacePolylinePage(),
PlacePolygonPage(),
PlaceCirclePage(),
DragMarkerPage(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's another demo that deals with dragging markers, maybe this one can be merged into the PlaceMarkerPage (or the dragging demo of the PlaceMarkerPage can be moved to this one)

Copy link
Contributor Author

@JamesMcIntosh JamesMcIntosh Oct 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've merged them and add it to the PR #4400

PaddingPage(),
SnapshotPage(),
LiteModePage(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PlaceMarkerBody extends StatefulWidget {
State<StatefulWidget> createState() => PlaceMarkerBodyState();
}

typedef Marker MarkerUpdateAction(Marker marker);
typedef MarkerUpdateAction = Marker Function(Marker marker);

class PlaceMarkerBodyState extends State<PlaceMarkerBody> {
PlaceMarkerBodyState();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,16 @@ - (void)mapView:(GMSMapView*)mapView didEndDraggingMarker:(GMSMarker*)marker {
[_markersController onMarkerDragEnd:markerId coordinate:marker.position];
}

- (void)mapView:(GMSMapView*)mapView didStartDraggingMarker:(GMSMarker*)marker {
NSString* markerId = marker.userData[0];
[_markersController onMarkerDragStart:markerId coordinate:marker.position];
}

- (void)mapView:(GMSMapView*)mapView didDragMarker:(GMSMarker*)marker {
NSString* markerId = marker.userData[0];
[_markersController onMarkerDrag:markerId coordinate:marker.position];
}

- (void)mapView:(GMSMapView*)mapView didTapInfoWindowOfMarker:(GMSMarker*)marker {
NSString* markerId = marker.userData[0];
[_markersController onInfoWindowTap:markerId];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)changeMarkers:(NSArray*)markersToChange;
- (void)removeMarkerIds:(NSArray*)markerIdsToRemove;
- (BOOL)onMarkerTap:(NSString*)markerId;
- (void)onMarkerDragStart:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate;
- (void)onMarkerDragEnd:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate;
- (void)onMarkerDrag:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate;
- (void)onInfoWindowTap:(NSString*)markerId;
- (void)showMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result;
- (void)hideMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result;
Expand Down
Loading