Skip to content

Commit f6d93a7

Browse files
[google_maps_flutter] Add Marker drag events (flutter#2838)
1 parent cf40966 commit f6d93a7

File tree

14 files changed

+399
-6
lines changed

14 files changed

+399
-6
lines changed

packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.0.11
2+
3+
* Add additional marker drag events.
4+
15
## 2.0.10
26

37
* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0.

packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,14 @@ public boolean onMarkerClick(Marker marker) {
467467
}
468468

469469
@Override
470-
public void onMarkerDragStart(Marker marker) {}
470+
public void onMarkerDragStart(Marker marker) {
471+
markersController.onMarkerDragStart(marker.getId(), marker.getPosition());
472+
}
471473

472474
@Override
473-
public void onMarkerDrag(Marker marker) {}
475+
public void onMarkerDrag(Marker marker) {
476+
markersController.onMarkerDrag(marker.getId(), marker.getPosition());
477+
}
474478

475479
@Override
476480
public void onMarkerDragEnd(Marker marker) {

packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,28 @@ boolean onMarkerTap(String googleMarkerId) {
105105
return false;
106106
}
107107

108+
void onMarkerDragStart(String googleMarkerId, LatLng latLng) {
109+
String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId);
110+
if (markerId == null) {
111+
return;
112+
}
113+
final Map<String, Object> data = new HashMap<>();
114+
data.put("markerId", markerId);
115+
data.put("position", Convert.latLngToJson(latLng));
116+
methodChannel.invokeMethod("marker#onDragStart", data);
117+
}
118+
119+
void onMarkerDrag(String googleMarkerId, LatLng latLng) {
120+
String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId);
121+
if (markerId == null) {
122+
return;
123+
}
124+
final Map<String, Object> data = new HashMap<>();
125+
data.put("markerId", markerId);
126+
data.put("position", Convert.latLngToJson(latLng));
127+
methodChannel.invokeMethod("marker#onDrag", data);
128+
}
129+
108130
void onMarkerDragEnd(String googleMarkerId, LatLng latLng) {
109131
String markerId = googleMapsMarkerIdToDartMarkerId.get(googleMarkerId);
110132
if (markerId == null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.googlemaps;
6+
7+
import static org.mockito.ArgumentMatchers.any;
8+
import static org.mockito.Mockito.mock;
9+
import static org.mockito.Mockito.spy;
10+
import static org.mockito.Mockito.when;
11+
12+
import com.google.android.gms.internal.maps.zzt;
13+
import com.google.android.gms.maps.GoogleMap;
14+
import com.google.android.gms.maps.model.LatLng;
15+
import com.google.android.gms.maps.model.Marker;
16+
import com.google.android.gms.maps.model.MarkerOptions;
17+
import io.flutter.plugin.common.BinaryMessenger;
18+
import io.flutter.plugin.common.MethodChannel;
19+
import io.flutter.plugin.common.MethodCodec;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.HashMap;
23+
import java.util.List;
24+
import java.util.Map;
25+
import org.junit.Test;
26+
import org.mockito.Mockito;
27+
28+
public class MarkersControllerTest {
29+
30+
@Test
31+
public void controller_OnMarkerDragStart() {
32+
final MethodChannel methodChannel =
33+
spy(new MethodChannel(mock(BinaryMessenger.class), "no-name", mock(MethodCodec.class)));
34+
final MarkersController controller = new MarkersController(methodChannel);
35+
final GoogleMap googleMap = mock(GoogleMap.class);
36+
controller.setGoogleMap(googleMap);
37+
38+
final zzt z = mock(zzt.class);
39+
final Marker marker = new Marker(z);
40+
41+
final String googleMarkerId = "abc123";
42+
43+
when(marker.getId()).thenReturn(googleMarkerId);
44+
when(googleMap.addMarker(any(MarkerOptions.class))).thenReturn(marker);
45+
46+
final LatLng latLng = new LatLng(1.1, 2.2);
47+
final Map<String, String> markerOptions = new HashMap();
48+
markerOptions.put("markerId", googleMarkerId);
49+
50+
final List<Object> markers = Arrays.<Object>asList(markerOptions);
51+
controller.addMarkers(markers);
52+
controller.onMarkerDragStart(googleMarkerId, latLng);
53+
54+
final List<Double> points = new ArrayList();
55+
points.add(latLng.latitude);
56+
points.add(latLng.longitude);
57+
58+
final Map<String, Object> data = new HashMap<>();
59+
data.put("markerId", googleMarkerId);
60+
data.put("position", points);
61+
Mockito.verify(methodChannel).invokeMethod("marker#onDragStart", data);
62+
}
63+
64+
@Test
65+
public void controller_OnMarkerDragEnd() {
66+
final MethodChannel methodChannel =
67+
spy(new MethodChannel(mock(BinaryMessenger.class), "no-name", mock(MethodCodec.class)));
68+
final MarkersController controller = new MarkersController(methodChannel);
69+
final GoogleMap googleMap = mock(GoogleMap.class);
70+
controller.setGoogleMap(googleMap);
71+
72+
final zzt z = mock(zzt.class);
73+
final Marker marker = new Marker(z);
74+
75+
final String googleMarkerId = "abc123";
76+
77+
when(marker.getId()).thenReturn(googleMarkerId);
78+
when(googleMap.addMarker(any(MarkerOptions.class))).thenReturn(marker);
79+
80+
final LatLng latLng = new LatLng(1.1, 2.2);
81+
final Map<String, String> markerOptions = new HashMap();
82+
markerOptions.put("markerId", googleMarkerId);
83+
84+
final List<Object> markers = Arrays.<Object>asList(markerOptions);
85+
controller.addMarkers(markers);
86+
controller.onMarkerDragEnd(googleMarkerId, latLng);
87+
88+
final List<Double> points = new ArrayList();
89+
points.add(latLng.latitude);
90+
points.add(latLng.longitude);
91+
92+
final Map<String, Object> data = new HashMap<>();
93+
data.put("markerId", googleMarkerId);
94+
data.put("position", points);
95+
Mockito.verify(methodChannel).invokeMethod("marker#onDragEnd", data);
96+
}
97+
98+
@Test
99+
public void controller_OnMarkerDrag() {
100+
final MethodChannel methodChannel =
101+
spy(new MethodChannel(mock(BinaryMessenger.class), "no-name", mock(MethodCodec.class)));
102+
final MarkersController controller = new MarkersController(methodChannel);
103+
final GoogleMap googleMap = mock(GoogleMap.class);
104+
controller.setGoogleMap(googleMap);
105+
106+
final zzt z = mock(zzt.class);
107+
final Marker marker = new Marker(z);
108+
109+
final String googleMarkerId = "abc123";
110+
111+
when(marker.getId()).thenReturn(googleMarkerId);
112+
when(googleMap.addMarker(any(MarkerOptions.class))).thenReturn(marker);
113+
114+
final LatLng latLng = new LatLng(1.1, 2.2);
115+
final Map<String, String> markerOptions = new HashMap();
116+
markerOptions.put("markerId", googleMarkerId);
117+
118+
final List<Object> markers = Arrays.<Object>asList(markerOptions);
119+
controller.addMarkers(markers);
120+
controller.onMarkerDrag(googleMarkerId, latLng);
121+
122+
final List<Double> points = new ArrayList();
123+
points.add(latLng.latitude);
124+
points.add(latLng.longitude);
125+
126+
final Map<String, Object> data = new HashMap<>();
127+
data.put("markerId", googleMarkerId);
128+
data.put("position", points);
129+
Mockito.verify(methodChannel).invokeMethod("marker#onDrag", data);
130+
}
131+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// ignore_for_file: public_member_api_docs
6+
7+
import 'dart:math';
8+
9+
import 'package:flutter/material.dart';
10+
import 'package:google_maps_flutter/google_maps_flutter.dart';
11+
12+
import 'page.dart';
13+
14+
class DragMarkerPage extends GoogleMapExampleAppPage {
15+
DragMarkerPage() : super(const Icon(Icons.drag_handle), 'Drag marker');
16+
17+
@override
18+
Widget build(BuildContext context) {
19+
return const DragMarkerBody();
20+
}
21+
}
22+
23+
class DragMarkerBody extends StatefulWidget {
24+
const DragMarkerBody();
25+
26+
@override
27+
State<StatefulWidget> createState() => DragMarkerBodyState();
28+
}
29+
30+
typedef MarkerUpdateAction = Marker Function(Marker marker);
31+
32+
class DragMarkerBodyState extends State<DragMarkerBody> {
33+
DragMarkerBodyState();
34+
static const LatLng center = LatLng(-33.86711, 151.1947171);
35+
36+
GoogleMapController? controller;
37+
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
38+
MarkerId? selectedMarker;
39+
int _markerIdCounter = 1;
40+
LatLng? markerPosition;
41+
42+
void _onMapCreated(GoogleMapController controller) {
43+
this.controller = controller;
44+
}
45+
46+
void _onMarkerTapped(MarkerId markerId) {
47+
final Marker? tappedMarker = markers[markerId];
48+
if (tappedMarker != null) {
49+
setState(() {
50+
if (markers.containsKey(selectedMarker)) {
51+
final Marker resetOld = markers[selectedMarker]!
52+
.copyWith(iconParam: BitmapDescriptor.defaultMarker);
53+
markers[selectedMarker!] = resetOld;
54+
}
55+
selectedMarker = markerId;
56+
final Marker newMarker = tappedMarker.copyWith(
57+
iconParam: BitmapDescriptor.defaultMarkerWithHue(
58+
BitmapDescriptor.hueGreen,
59+
),
60+
);
61+
markers[markerId] = newMarker;
62+
});
63+
}
64+
}
65+
66+
void _onMarkerDrag(MarkerId markerId, LatLng newPosition) async {
67+
setState(() {
68+
this.markerPosition = newPosition;
69+
});
70+
}
71+
72+
void _add() {
73+
final int markerCount = markers.length;
74+
75+
if (markerCount == 12) {
76+
return;
77+
}
78+
79+
final String markerIdVal = 'marker_id_$_markerIdCounter';
80+
_markerIdCounter++;
81+
final MarkerId markerId = MarkerId(markerIdVal);
82+
83+
final Marker marker = Marker(
84+
draggable: true,
85+
markerId: markerId,
86+
position: LatLng(
87+
center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0,
88+
center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0,
89+
),
90+
infoWindow: InfoWindow(title: markerIdVal, snippet: '*'),
91+
onTap: () => _onMarkerTapped(markerId),
92+
onDrag: (LatLng position) => _onMarkerDrag(markerId, position),
93+
);
94+
95+
setState(() {
96+
markers[markerId] = marker;
97+
});
98+
}
99+
100+
void _remove() {
101+
setState(() {
102+
if (markers.containsKey(selectedMarker)) {
103+
markers.remove(selectedMarker);
104+
}
105+
});
106+
}
107+
108+
@override
109+
Widget build(BuildContext context) {
110+
return Column(
111+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
112+
crossAxisAlignment: CrossAxisAlignment.stretch,
113+
children: <Widget>[
114+
Expanded(
115+
child: Center(
116+
child: GoogleMap(
117+
onMapCreated: _onMapCreated,
118+
initialCameraPosition: const CameraPosition(
119+
target: center,
120+
zoom: 11.0,
121+
),
122+
markers: markers.values.toSet(),
123+
),
124+
),
125+
),
126+
Container(
127+
height: 30,
128+
padding: EdgeInsets.only(left: 12, right: 12),
129+
child: Row(
130+
mainAxisSize: MainAxisSize.max,
131+
children: [
132+
markerPosition == null
133+
? Container()
134+
: Expanded(child: Text("lat: ${markerPosition!.latitude}")),
135+
markerPosition == null
136+
? Container()
137+
: Expanded(child: Text("lng: ${markerPosition!.longitude}")),
138+
],
139+
),
140+
),
141+
Row(
142+
children: <Widget>[
143+
TextButton(
144+
child: const Text('add'),
145+
onPressed: _add,
146+
),
147+
TextButton(
148+
child: const Text('remove'),
149+
onPressed: _remove,
150+
),
151+
],
152+
),
153+
],
154+
);
155+
}
156+
}

packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// ignore_for_file: public_member_api_docs
66

77
import 'package:flutter/material.dart';
8+
import 'package:google_maps_flutter_example/drag_marker.dart';
89
import 'package:google_maps_flutter_example/lite_mode.dart';
910
import 'animate_camera.dart';
1011
import 'map_click.dart';
@@ -34,6 +35,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
3435
PlacePolylinePage(),
3536
PlacePolygonPage(),
3637
PlaceCirclePage(),
38+
DragMarkerPage(),
3739
PaddingPage(),
3840
SnapshotPage(),
3941
LiteModePage(),

packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class PlaceMarkerBody extends StatefulWidget {
3030
State<StatefulWidget> createState() => PlaceMarkerBodyState();
3131
}
3232

33-
typedef Marker MarkerUpdateAction(Marker marker);
33+
typedef MarkerUpdateAction = Marker Function(Marker marker);
3434

3535
class PlaceMarkerBodyState extends State<PlaceMarkerBody> {
3636
PlaceMarkerBodyState();

packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,16 @@ - (void)mapView:(GMSMapView*)mapView didEndDraggingMarker:(GMSMarker*)marker {
509509
[_markersController onMarkerDragEnd:markerId coordinate:marker.position];
510510
}
511511

512+
- (void)mapView:(GMSMapView*)mapView didStartDraggingMarker:(GMSMarker*)marker {
513+
NSString* markerId = marker.userData[0];
514+
[_markersController onMarkerDragStart:markerId coordinate:marker.position];
515+
}
516+
517+
- (void)mapView:(GMSMapView*)mapView didDragMarker:(GMSMarker*)marker {
518+
NSString* markerId = marker.userData[0];
519+
[_markersController onMarkerDrag:markerId coordinate:marker.position];
520+
}
521+
512522
- (void)mapView:(GMSMapView*)mapView didTapInfoWindowOfMarker:(GMSMarker*)marker {
513523
NSString* markerId = marker.userData[0];
514524
[_markersController onInfoWindowTap:markerId];

packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ NS_ASSUME_NONNULL_BEGIN
4545
- (void)changeMarkers:(NSArray*)markersToChange;
4646
- (void)removeMarkerIds:(NSArray*)markerIdsToRemove;
4747
- (BOOL)onMarkerTap:(NSString*)markerId;
48+
- (void)onMarkerDragStart:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate;
4849
- (void)onMarkerDragEnd:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate;
50+
- (void)onMarkerDrag:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate;
4951
- (void)onInfoWindowTap:(NSString*)markerId;
5052
- (void)showMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result;
5153
- (void)hideMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result;

0 commit comments

Comments
 (0)