Skip to content

Commit

Permalink
[google_maps_flutter] Add Marker drag events (flutter#2838)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesMcIntosh authored and amantoux committed Dec 11, 2021
1 parent e69b50b commit 588e89b
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 6 deletions.
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);
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(),
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

0 comments on commit 588e89b

Please sign in to comment.