diff --git a/packages/google_mobile_maps/android/src/main/java/io/flutter/plugins/googlemobilemaps/GoogleMobileMapsPlugin.java b/packages/google_mobile_maps/android/src/main/java/io/flutter/plugins/googlemobilemaps/GoogleMobileMapsPlugin.java index 0b833d0c0175..e0e044acf5fb 100644 --- a/packages/google_mobile_maps/android/src/main/java/io/flutter/plugins/googlemobilemaps/GoogleMobileMapsPlugin.java +++ b/packages/google_mobile_maps/android/src/main/java/io/flutter/plugins/googlemobilemaps/GoogleMobileMapsPlugin.java @@ -24,15 +24,19 @@ import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.OnMapReadyCallback; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.Registrar; +import io.flutter.view.FlutterMain; import io.flutter.view.TextureRegistry; import java.util.ArrayList; import java.util.HashMap; @@ -91,79 +95,82 @@ public void onMethodCall(MethodCall call, Result result) { } case "moveCamera": { - final long id = ((Number) call.argument("id")).longValue(); + final GoogleMapsEntry entry = mapsEntry(call); final CameraUpdate cameraUpdate = toCameraUpdate(call.argument("cameraUpdate")); - final GoogleMapsEntry entry = googleMaps.get(id); - if (entry == null) { - result.error("unknown", "Unknown ID " + id, null); - } else { - entry.moveCamera(cameraUpdate); - result.success(null); - } + entry.moveCamera(cameraUpdate); + result.success(null); break; } case "animateCamera": { - final long id = ((Number) call.argument("id")).longValue(); + final GoogleMapsEntry entry = mapsEntry(call); final CameraUpdate cameraUpdate = toCameraUpdate(call.argument("cameraUpdate")); - final GoogleMapsEntry entry = googleMaps.get(id); - if (entry == null) { - result.error("unknown", "Unknown ID " + id, null); - } else { - entry.animateCamera(cameraUpdate); - result.success(null); - } + entry.animateCamera(cameraUpdate); + result.success(null); break; } case "addMarker": { - final long id = ((Number) call.argument("id")).longValue(); + final GoogleMapsEntry entry = mapsEntry(call); final MarkerOptions markerOptions = toMarkerOptions(call.argument("markerOptions")); - final GoogleMapsEntry entry = googleMaps.get(id); - if (entry == null) { - result.error("unknown", "Unknown ID " + id, null); - } else { - entry.addMarker(markerOptions); - result.success(null); - } + final String markerId = entry.addMarker(markerOptions); + result.success(markerId); + break; + } + case "marker#remove": + { + final GoogleMapsEntry entry = mapsEntry(call); + final String markerId = call.argument("marker"); + entry.removeMarker(markerId); + result.success(null); + break; + } + case "marker#hideInfoWindow": + { + final GoogleMapsEntry entry = mapsEntry(call); + final String markerId = call.argument("marker"); + entry.hideMarkerInfoWindow(markerId); + result.success(null); + break; + } + case "marker#showInfoWindow": + { + final GoogleMapsEntry entry = mapsEntry(call); + final String markerId = call.argument("marker"); + entry.showMarkerInfoWindow(markerId); + result.success(null); + break; + } + case "marker#update": + { + final GoogleMapsEntry entry = mapsEntry(call); + final String markerId = call.argument("marker"); + final MarkerOptions markerOptions = toMarkerOptions(call.argument("markerOptions")); + entry.updateMarker(markerId, markerOptions); + result.success(null); break; } case "showMapOverlay": { - final long id = ((Number) call.argument("id")).longValue(); + final GoogleMapsEntry entry = mapsEntry(call); final int x = ((Number) call.argument("x")).intValue(); final int y = ((Number) call.argument("y")).intValue(); - final GoogleMapsEntry entry = googleMaps.get(id); - if (entry == null) { - result.error("unknown", "Unknown ID " + id, null); - } else { - entry.showOverlay(x, y); - result.success(null); - } + entry.showOverlay(x, y); + result.success(null); break; } case "hideMapOverlay": { - final long id = ((Number) call.argument("id")).longValue(); - final GoogleMapsEntry entry = googleMaps.get(id); - if (entry == null) { - result.error("unknown", "Unknown ID " + id, null); - } else { - entry.hideOverlay(); - result.success(null); - } + final GoogleMapsEntry entry = mapsEntry(call); + entry.hideOverlay(); + result.success(null); break; } case "disposeMap": { - final long id = ((Number) call.argument("id")).longValue(); - final GoogleMapsEntry entry = googleMaps.get(id); - if (entry == null) { - result.error("unknown", "Unknown ID " + id, null); - } else { - entry.dispose(); - result.success(null); - } + final GoogleMapsEntry entry = mapsEntry(call); + entry.dispose(); + result.success(null); break; } default: @@ -171,42 +178,46 @@ public void onMethodCall(MethodCall call, Result result) { } } - private static LatLng toLatLng(Object o) { - @SuppressWarnings("unchecked") - final List coordinates = (List) o; - return new LatLng(coordinates.get(0), coordinates.get(1)); - } - - private static LatLngBounds toLatLngBounds(Object o) { - final List data = (List) o; - return new LatLngBounds(toLatLng(data.get(0)), toLatLng(data.get(1))); - } - - private static float toFloat(Object o) { - return ((Number) o).floatValue(); - } - - private static int toInt(Object o) { - return ((Number) o).intValue(); + private GoogleMapsEntry mapsEntry(MethodCall call) { + final long id = toLong(call.argument("map")); + final GoogleMapsEntry entry = googleMaps.get(id); + if (entry == null) { + throw new IllegalArgumentException("Unknown map: " + id); + } + return entry; } - private static Point toPoint(Object o) { - @SuppressWarnings("unchecked") - final List data = (List) o; - return new Point(toInt(data.get(0)), toInt(data.get(1))); + private static BitmapDescriptor toBitmapDescriptor(Object o) { + final List data = toList(o); + switch (toString(data.get(0))) { + case "defaultMarker": + if (data.size() == 1) { + return BitmapDescriptorFactory.defaultMarker(); + } else { + return BitmapDescriptorFactory.defaultMarker(toFloat(data.get(1))); + } + case "fromAsset": + if (data.size() == 2) { + return BitmapDescriptorFactory.fromAsset( + FlutterMain.getLookupKeyForAsset(toString(data.get(1)))); + } else { + return BitmapDescriptorFactory.fromAsset( + FlutterMain.getLookupKeyForAsset(toString(data.get(1)), toString(data.get(2)))); + } + case "fromFile": + return BitmapDescriptorFactory.fromFile(toString(data.get(1))); + case "fromPath": + return BitmapDescriptorFactory.fromPath(toString(data.get(1))); + } + throw new IllegalArgumentException("Cannot interpret " + o + " as BitmapDescriptor"); } - private static MarkerOptions toMarkerOptions(Object o) { - @SuppressWarnings("unchecked") - final Map data = (Map) o; - final MarkerOptions markerOptions = new MarkerOptions(); - markerOptions.position(toLatLng(data.get("position"))); - return markerOptions; + private static boolean toBoolean(Object o) { + return (Boolean) o; } private static CameraPosition toCameraPosition(Object o) { - @SuppressWarnings("unchecked") - final Map data = (Map) o; + final Map data = toMap(o); final CameraPosition.Builder builder = CameraPosition.builder(); builder.bearing(toFloat(data.get("bearing"))); builder.target(toLatLng(data.get("target"))); @@ -216,9 +227,8 @@ private static CameraPosition toCameraPosition(Object o) { } private static CameraUpdate toCameraUpdate(Object o) { - @SuppressWarnings("unchecked") - final List data = (List) o; - switch ((String) data.get(0)) { + final List data = toList(o); + switch (toString(data.get(0))) { case "newCameraPosition": return CameraUpdateFactory.newCameraPosition(toCameraPosition(data.get(1))); case "newLatLng": @@ -245,6 +255,68 @@ private static CameraUpdate toCameraUpdate(Object o) { throw new IllegalArgumentException("Cannot interpret " + o + " as CameraUpdate"); } + private static double toDouble(Object o) { + return ((Number) o).doubleValue(); + } + + private static float toFloat(Object o) { + return ((Number) o).floatValue(); + } + + private static int toInt(Object o) { + return ((Number) o).intValue(); + } + + private static LatLng toLatLng(Object o) { + final List data = toList(o); + return new LatLng(toDouble(data.get(0)), toDouble(data.get(1))); + } + + private static LatLngBounds toLatLngBounds(Object o) { + final List data = toList(o); + return new LatLngBounds(toLatLng(data.get(0)), toLatLng(data.get(1))); + } + + private static List toList(Object o) { + return (List) o; + } + + private static long toLong(Object o) { + return ((Number) o).longValue(); + } + + private static Map toMap(Object o) { + return (Map) o; + } + + private static MarkerOptions toMarkerOptions(Object o) { + final Map data = toMap(o); + final List anchor = toList(data.get("anchor")); + final List infoWindowAnchor = toList(data.get("infoWindowAnchor")); + return new MarkerOptions() + .position(toLatLng(data.get("position"))) + .alpha(toFloat(data.get("alpha"))) + .anchor(toFloat(anchor.get(0)), toFloat(anchor.get(1))) + .draggable(toBoolean(data.get("draggable"))) + .flat(toBoolean(data.get("flat"))) + .icon(toBitmapDescriptor(data.get("icon"))) + .infoWindowAnchor(toFloat(infoWindowAnchor.get(0)), toFloat(infoWindowAnchor.get(1))) + .rotation(toFloat(data.get("rotation"))) + .snippet(toString(data.get("snippet"))) + .title(toString(data.get("title"))) + .visible(toBoolean(data.get("visible"))) + .zIndex(toFloat(data.get("zIndex"))); + } + + private static Point toPoint(Object o) { + final List data = toList(o); + return new Point(toInt(data.get(0)), toInt(data.get(1))); + } + + private static String toString(Object o) { + return (String) o; + } + @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { state.set(CREATED); @@ -295,6 +367,7 @@ final class GoogleMapsEntry private final int height; private final Result result; private final Timer timer; + private final Map markers; private GoogleMap googleMap; private Surface surface; private boolean disposed = false; @@ -313,6 +386,7 @@ final class GoogleMapsEntry textureEntry.surfaceTexture().setDefaultBufferSize(width, height); this.mapView = new MapView(registrar.activity()); this.timer = new Timer(); + this.markers = new HashMap<>(); } void init() { @@ -373,16 +447,59 @@ void hideOverlay() { parent.addView(mapView, 0); } - void moveCamera(final CameraUpdate cameraUpdate) { + void moveCamera(CameraUpdate cameraUpdate) { googleMap.moveCamera(cameraUpdate); } - void animateCamera(final CameraUpdate cameraUpdate) { + void animateCamera(CameraUpdate cameraUpdate) { googleMap.animateCamera(cameraUpdate); } - void addMarker(final MarkerOptions options) { - googleMap.addMarker(options); + String addMarker(MarkerOptions options) { + final Marker marker = googleMap.addMarker(options); + markers.put(marker.getId(), marker); + return marker.getId(); + } + + void removeMarker(String markerId) { + final Marker marker = markers.remove(markerId); + if (marker != null) { + marker.remove(); + } + } + + void showMarkerInfoWindow(String markerId) { + final Marker marker = marker(markerId); + marker.showInfoWindow(); + } + + void hideMarkerInfoWindow(String markerId) { + final Marker marker = marker(markerId); + marker.hideInfoWindow(); + } + + void updateMarker(String markerId, MarkerOptions options) { + final Marker marker = marker(markerId); + marker.setPosition(options.getPosition()); + marker.setAlpha(options.getAlpha()); + marker.setAnchor(options.getAnchorU(), options.getAnchorV()); + marker.setDraggable(options.isDraggable()); + marker.setFlat(options.isFlat()); + marker.setIcon(options.getIcon()); + marker.setInfoWindowAnchor(options.getInfoWindowAnchorU(), options.getInfoWindowAnchorV()); + marker.setRotation(options.getRotation()); + marker.setSnippet(options.getSnippet()); + marker.setTitle(options.getTitle()); + marker.setVisible(options.isVisible()); + marker.setZIndex(options.getZIndex()); + } + + private Marker marker(String markerId) { + final Marker marker = markers.get(markerId); + if (marker == null) { + throw new IllegalArgumentException("Unknown marker: " + markerId); + } + return marker; } private void updateTexture() { diff --git a/packages/google_mobile_maps/example/lib/animate_camera.dart b/packages/google_mobile_maps/example/lib/animate_camera.dart index 1218f2f4ef97..e21b3014942b 100644 --- a/packages/google_mobile_maps/example/lib/animate_camera.dart +++ b/packages/google_mobile_maps/example/lib/animate_camera.dart @@ -8,7 +8,7 @@ import 'package:google_mobile_maps/google_mobile_maps.dart'; import 'page.dart'; class AnimateCameraPage extends Page { - AnimateCameraPage() : super("ANIMATE"); + AnimateCameraPage() : super(new Icon(Icons.map), "Camera control", "Animate"); final GoogleMapsOverlayController controller = new GoogleMapsOverlayController.fromSize(300.0, 200.0); @@ -42,7 +42,6 @@ class AnimateCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newCameraPosition'), ), new FlatButton( @@ -53,7 +52,6 @@ class AnimateCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newLatLng'), ), new FlatButton( @@ -68,7 +66,6 @@ class AnimateCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newLatLngBounds'), ), new FlatButton( @@ -80,22 +77,20 @@ class AnimateCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newLatLngZoom'), ), - ], - ), - new Column( - children: [ new FlatButton( onPressed: () { controller.mapsController.animateCamera( CameraUpdate.scrollBy(150.0, -225.0), ); }, - color: Colors.red, child: const Text('scrollBy'), ), + ], + ), + new Column( + children: [ new FlatButton( onPressed: () { controller.mapsController.animateCamera( @@ -105,16 +100,14 @@ class AnimateCameraPage extends Page { ), ); }, - color: Colors.yellow, child: const Text('zoomBy with focus'), ), new FlatButton( onPressed: () { controller.mapsController.animateCamera( - CameraUpdate.zoomBy(0.7), + CameraUpdate.zoomBy(-0.5), ); }, - color: Colors.yellow, child: const Text('zoomBy'), ), new FlatButton( @@ -123,7 +116,6 @@ class AnimateCameraPage extends Page { CameraUpdate.zoomIn(), ); }, - color: Colors.yellow, child: const Text('zoomIn'), ), new FlatButton( @@ -132,7 +124,6 @@ class AnimateCameraPage extends Page { CameraUpdate.zoomOut(), ); }, - color: Colors.yellow, child: const Text('zoomOut'), ), new FlatButton( @@ -141,7 +132,6 @@ class AnimateCameraPage extends Page { CameraUpdate.zoomTo(16.0), ); }, - color: Colors.yellow, child: const Text('zoomTo'), ), ], diff --git a/packages/google_mobile_maps/example/lib/main.dart b/packages/google_mobile_maps/example/lib/main.dart index 0e2378d32e9b..35a394f13ebd 100644 --- a/packages/google_mobile_maps/example/lib/main.dart +++ b/packages/google_mobile_maps/example/lib/main.dart @@ -15,78 +15,39 @@ final List _allPages = [ new PlaceMarkerPage(), ]; -class MapsDemo extends StatefulWidget { - @override - MapsDemoState createState() => new MapsDemoState(); -} - -class MapsDemoState extends State - with SingleTickerProviderStateMixin { - TabController _controller; - PlatformOverlayController _activeOverlayController; - - @override - void initState() { - super.initState(); - _controller = new TabController(vsync: this, length: _allPages.length); - _controller.addListener(() { - if (_controller.indexIsChanging) { - _activeOverlayController?.deactivateOverlay(); - _activeOverlayController = null; - } else { - _activeOverlayController = - _allPages[_controller.index].overlayController; - _activeOverlayController.activateOverlay(); - } - }); - _activeOverlayController = _allPages[_controller.index].overlayController; - _activeOverlayController.activateOverlay(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); +class MapsDemo extends StatelessWidget { + void _pushPage(BuildContext context, Page page) { + Navigator.of(context).push(new MaterialPageRoute( + builder: (_) => new Scaffold( + appBar: new AppBar( + title: new Text('${page.title} - ${page.subtitle}'), + ), + body: page, + ))); } @override Widget build(BuildContext context) { return new Scaffold( - appBar: new AppBar( - title: const Text('Map controls'), - bottom: new TabBar( - controller: _controller, - isScrollable: true, - tabs: _allPages.map((Page page) { - return new Tab(text: page.title); - }).toList(), - ), - ), - body: new NotificationListener( - onNotification: (ScrollNotification scrollNotification) { - if (scrollNotification.depth != 0) { - return; - } - if (scrollNotification is ScrollStartNotification && - _activeOverlayController != null) { - _activeOverlayController.deactivateOverlay(); - _activeOverlayController = null; - } - }, - child: new TabBarView( - controller: _controller, - children: _allPages.map((Page page) { - return new Container( - key: new ObjectKey(page.title), - padding: const EdgeInsets.all(12.0), - child: new Card(child: page), - ); - }).toList()), + appBar: new AppBar(title: const Text('GoogleMaps examples')), + body: new ListView.builder( + itemCount: _allPages.length, + itemBuilder: (_, int index) => new ListTile( + leading: _allPages[index].leading, + title: new Text(_allPages[index].title), + subtitle: new Text(_allPages[index].subtitle), + onTap: () => _pushPage(context, _allPages[index]), + ), ), ); } } void main() { - runApp(new MaterialApp(home: new MapsDemo())); + GoogleMapsController.init(); + final List observers = []; + for (Page p in _allPages) { + observers.add(p.overlayController); + } + runApp(new MaterialApp(home: new MapsDemo(), navigatorObservers: observers)); } diff --git a/packages/google_mobile_maps/example/lib/move_camera.dart b/packages/google_mobile_maps/example/lib/move_camera.dart index 458d04bdd7fb..a4bd5b731376 100644 --- a/packages/google_mobile_maps/example/lib/move_camera.dart +++ b/packages/google_mobile_maps/example/lib/move_camera.dart @@ -8,7 +8,7 @@ import 'package:google_mobile_maps/google_mobile_maps.dart'; import 'page.dart'; class MoveCameraPage extends Page { - MoveCameraPage() : super("MOVE"); + MoveCameraPage() : super(new Icon(Icons.map), "Camera control", "Move"); final GoogleMapsOverlayController controller = new GoogleMapsOverlayController.fromSize(300.0, 200.0); @@ -42,7 +42,6 @@ class MoveCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newCameraPosition'), ), new FlatButton( @@ -53,7 +52,6 @@ class MoveCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newLatLng'), ), new FlatButton( @@ -68,7 +66,6 @@ class MoveCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newLatLngBounds'), ), new FlatButton( @@ -80,22 +77,20 @@ class MoveCameraPage extends Page { ), ); }, - color: Colors.lightBlue, child: const Text('newLatLngZoom'), ), - ], - ), - new Column( - children: [ new FlatButton( onPressed: () { controller.mapsController.moveCamera( CameraUpdate.scrollBy(150.0, -225.0), ); }, - color: Colors.red, child: const Text('scrollBy'), ), + ], + ), + new Column( + children: [ new FlatButton( onPressed: () { controller.mapsController.moveCamera( @@ -105,16 +100,14 @@ class MoveCameraPage extends Page { ), ); }, - color: Colors.yellow, child: const Text('zoomBy with focus'), ), new FlatButton( onPressed: () { controller.mapsController.moveCamera( - CameraUpdate.zoomBy(0.7), + CameraUpdate.zoomBy(-0.5), ); }, - color: Colors.yellow, child: const Text('zoomBy'), ), new FlatButton( @@ -123,7 +116,6 @@ class MoveCameraPage extends Page { CameraUpdate.zoomIn(), ); }, - color: Colors.yellow, child: const Text('zoomIn'), ), new FlatButton( @@ -132,7 +124,6 @@ class MoveCameraPage extends Page { CameraUpdate.zoomOut(), ); }, - color: Colors.yellow, child: const Text('zoomOut'), ), new FlatButton( @@ -141,7 +132,6 @@ class MoveCameraPage extends Page { CameraUpdate.zoomTo(16.0), ); }, - color: Colors.yellow, child: const Text('zoomTo'), ), ], diff --git a/packages/google_mobile_maps/example/lib/page.dart b/packages/google_mobile_maps/example/lib/page.dart index 0948296e46d8..0e598ba9b0f8 100644 --- a/packages/google_mobile_maps/example/lib/page.dart +++ b/packages/google_mobile_maps/example/lib/page.dart @@ -6,9 +6,11 @@ import 'package:flutter/material.dart'; import 'package:google_mobile_maps/google_mobile_maps.dart'; abstract class Page extends StatelessWidget { - const Page(this.title); + const Page(this.leading, this.title, this.subtitle); + final Widget leading; final String title; + final String subtitle; PlatformOverlayController get overlayController; } diff --git a/packages/google_mobile_maps/example/lib/place_marker.dart b/packages/google_mobile_maps/example/lib/place_marker.dart index 28e025aceb03..b6d1f67263cd 100644 --- a/packages/google_mobile_maps/example/lib/place_marker.dart +++ b/packages/google_mobile_maps/example/lib/place_marker.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + // Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,10 +10,10 @@ import 'package:google_mobile_maps/google_mobile_maps.dart'; import 'page.dart'; class PlaceMarkerPage extends Page { - PlaceMarkerPage() : super("MARK"); + PlaceMarkerPage() : super(new Icon(Icons.place), "Place marker", "Single"); final GoogleMapsOverlayController controller = - new GoogleMapsOverlayController.fromSize(200.0, 300.0) + new GoogleMapsOverlayController.fromSize(300.0, 200.0) ..mapsController.moveCamera(CameraUpdate.newLatLngZoom( const LatLng(-33.852, 151.211), 11.0, @@ -21,27 +23,249 @@ class PlaceMarkerPage extends Page { PlatformOverlayController get overlayController => controller.overlayController; + @override + Widget build(BuildContext context) { + return new PlaceMarkerBody(controller); + } +} + +class PlaceMarkerBody extends StatefulWidget { + final GoogleMapsOverlayController controller; + + const PlaceMarkerBody(this.controller); + + @override + State createState() => new PlaceMarkerBodyState(); +} + +class PlaceMarkerBodyState extends State { + static const LatLng center = const LatLng(-33.86711, 151.1947171); + + final List> markers = >[]; + int _nextAlpha = 1; + int _nextHue = 1; + int _nextPosition = 1; + int _nextRotation = 1; + int _nextAnchor = 1; + int _nextInfoWindowAnchor = 7; + int _nextTitle = 1; + int _nextZIndex = 1; + + void _add() { + setState(() { + markers.add( + widget.controller.mapsController.addMarker( + new MarkerOptions( + position: new LatLng( + center.latitude + sin(markers.length * pi / 6.0) / 20.0, + center.longitude + cos(markers.length * pi / 6.0) / 20.0, + ), + zIndex: markers.length.toDouble(), + icon: BitmapDescriptor.defaultMarkerWithHue(30.0 * markers.length), + ), + ), + ); + }); + } + + Future _remove() async { + final Marker marker = await markers.last; + setState(() { + markers.removeLast(); + }); + await marker.remove(); + } + + Future _showInfo() async { + final Marker marker = await markers.last; + await marker.showInfoWindow(); + } + + Future _hideInfo() async { + final Marker marker = await markers.last; + await marker.hideInfoWindow(); + } + + Future _changePosition() async { + final Marker marker = await markers.last; + final LatLng position = new LatLng( + center.latitude + sin(_nextPosition * pi / 6.0) / 20.0, + center.longitude + cos(_nextPosition * pi / 6.0) / 20.0, + ); + setState(() { + _nextPosition = (_nextPosition + 1) % 12; + }); + await marker.update(marker.options.copyWith(position: position)); + } + + Future _changeAnchor() async { + final Marker marker = await markers.last; + final Offset offset = new Offset( + (sin(_nextAnchor * pi / 6.0) + 1.0) / 2.0, + (cos(_nextAnchor * pi / 6.0) + 1.0) / 2.0, + ); + setState(() { + _nextAnchor = (_nextAnchor + 1) % 12; + }); + await marker.update(marker.options.copyWith(anchor: offset)); + } + + Future _changeInfoAnchor() async { + final Marker marker = await markers.last; + final Offset offset = new Offset( + (sin(_nextInfoWindowAnchor * pi / 6.0) + 1.0) / 2.0, + (cos(_nextInfoWindowAnchor * pi / 6.0) + 1.0) / 2.0, + ); + setState(() { + _nextInfoWindowAnchor = (_nextInfoWindowAnchor + 1) % 12; + }); + await marker.update(marker.options.copyWith(infoWindowAnchor: offset)); + } + + Future _toggleDraggable() async { + final Marker marker = await markers.last; + await marker.update( + marker.options.copyWith(draggable: !marker.options.draggable), + ); + } + + Future _toggleFlat() async { + final Marker marker = await markers.last; + await marker.update( + marker.options.copyWith(flat: !marker.options.flat), + ); + } + + Future _changeInfo() async { + final Marker marker = await markers.last; + final String title = _nextTitle == 0 ? null : 'Title $_nextTitle'; + final String snippet = _nextTitle % 2 == 0 ? null : 'Snippet $_nextTitle'; + setState(() { + _nextTitle = (_nextTitle + 1) % 12; + }); + await marker.update( + marker.options.copyWith(title: title, snippet: snippet), + ); + } + + Future _changeIcon() async { + final Marker marker = await markers.last; + final BitmapDescriptor icon = BitmapDescriptor.defaultMarkerWithHue( + _nextHue * 30.0, + ); + setState(() { + _nextHue = (_nextHue + 1) % 12; + }); + await marker.update(marker.options.copyWith(icon: icon)); + } + + Future _changeAlpha() async { + final Marker marker = await markers.last; + final double alpha = 1.0 - _nextAlpha / 12.0; + setState(() { + _nextAlpha = (_nextAlpha + 1) % 12; + }); + await marker.update(marker.options.copyWith(alpha: alpha)); + } + + Future _changeRotation() async { + final Marker marker = await markers.last; + final double rotation = _nextRotation * 30.0; + setState(() { + _nextRotation = (_nextRotation + 1) % 12; + }); + await marker.update( + marker.options.copyWith(rotation: rotation), + ); + } + + Future _changeZIndex() async { + final Marker marker = await markers.last; + final double zIndex = _nextZIndex.toDouble(); + setState(() { + _nextZIndex = (_nextZIndex + 1) % 12; + }); + await marker.update(marker.options.copyWith(zIndex: zIndex)); + } + @override Widget build(BuildContext context) { return new Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - new Center(child: new GoogleMapsOverlay(controller: controller)), + new Center(child: new GoogleMapsOverlay(controller: widget.controller)), new Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - new FlatButton( - onPressed: () { - controller.mapsController.addMarker( - const MarkerOptions( - position: const LatLng(-33.86711, 151.1947171), - ), - ); - }, - color: Colors.blue, - child: const Text('Default'), - ), + new Row( + children: [ + new Column( + children: [ + new FlatButton( + child: const Text('add'), + onPressed: (markers.length == 12) ? null : _add, + ), + new FlatButton( + child: const Text('remove'), + onPressed: (markers.isEmpty) ? null : _remove, + ), + new FlatButton( + child: const Text('show info'), + onPressed: (markers.isEmpty) ? null : _showInfo, + ), + new FlatButton( + child: const Text('hide info'), + onPressed: (markers.isEmpty) ? null : _hideInfo, + ), + ], + ), + new Column( + children: [ + new FlatButton( + child: const Text('change position'), + onPressed: (markers.isEmpty) ? null : _changePosition, + ), + new FlatButton( + child: const Text('change anchor'), + onPressed: (markers.isEmpty) ? null : _changeAnchor, + ), + new FlatButton( + child: const Text('change info anchor'), + onPressed: (markers.isEmpty) ? null : _changeInfoAnchor, + ), + new FlatButton( + child: const Text('toggle draggable'), + onPressed: (markers.isEmpty) ? null : _toggleDraggable, + ), + new FlatButton( + child: const Text('toggle flat'), + onPressed: (markers.isEmpty) ? null : _toggleFlat, + ), + new FlatButton( + child: const Text('change info'), + onPressed: (markers.isEmpty) ? null : _changeInfo, + ), + new FlatButton( + child: const Text('change color'), + onPressed: (markers.isEmpty) ? null : _changeIcon, + ), + new FlatButton( + child: const Text('change alpha'), + onPressed: (markers.isEmpty) ? null : _changeAlpha, + ), + new FlatButton( + child: const Text('change rotation'), + onPressed: (markers.isEmpty) ? null : _changeRotation, + ), + new FlatButton( + child: const Text('change zIndex'), + onPressed: (markers.isEmpty) ? null : _changeZIndex, + ), + ], + ), + ], + ) ], ) ], diff --git a/packages/google_mobile_maps/lib/google_mobile_maps.dart b/packages/google_mobile_maps/lib/google_mobile_maps.dart index 6f6438189ef2..04ea409e29c0 100644 --- a/packages/google_mobile_maps/lib/google_mobile_maps.dart +++ b/packages/google_mobile_maps/lib/google_mobile_maps.dart @@ -14,6 +14,7 @@ import 'package:flutter/services.dart'; export 'dart:async'; +part 'src/bitmap.dart'; part 'src/camera.dart'; part 'src/controller.dart'; part 'src/marker.dart'; diff --git a/packages/google_mobile_maps/lib/src/bitmap.dart b/packages/google_mobile_maps/lib/src/bitmap.dart new file mode 100644 index 000000000000..658d68efb370 --- /dev/null +++ b/packages/google_mobile_maps/lib/src/bitmap.dart @@ -0,0 +1,60 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of google_mobile_maps; + +/// Defines a bitmap image. For a marker, this class can be used to set the +/// image of the marker icon. For a ground overlay, it can be used to set the +/// image to place on the surface of the earth. +class BitmapDescriptor { + static const double hueRed = 0.0; + static const double hueOrange = 30.0; + static const double hueYellow = 60.0; + static const double hueGreen = 120.0; + static const double hueCyan = 180.0; + static const double hueAzure = 210.0; + static const double hueBlue = 240.0; + static const double hueViolet = 270.0; + static const double hueMagenta = 300.0; + static const double hueRose = 330.0; + + const BitmapDescriptor._(this._json); + + /// Creates a BitmapDescriptor that refers to the default marker image. + static const BitmapDescriptor defaultMarker = + const BitmapDescriptor._(['defaultMarker']); + + /// Creates a BitmapDescriptor that refers to a colorization of the default + /// marker image. For convenience, there is a predefined set of hue values. + /// See e.g. [hueYellow]. + static BitmapDescriptor defaultMarkerWithHue(double hue) { + assert(0.0 <= hue && hue < 360.0); + return new BitmapDescriptor._(['defaultMarker', hue]); + } + + /// Creates a BitmapDescriptor using the name of a bitmap image in the assets + /// directory. + static BitmapDescriptor fromAsset(String assetName, {String package}) { + if (package == null) { + return new BitmapDescriptor._(['fromAsset', assetName]); + } else { + return new BitmapDescriptor._(['fromAsset', assetName, package]); + } + } + + /// Creates a BitmapDescriptor using the name of a bitmap image file located + /// in storage private to the app. + static BitmapDescriptor fromFile(String fileName) { + return new BitmapDescriptor._(['fromFile', fileName]); + } + + /// Creates a BitmapDescriptor from the absolute file path of a bitmap image. + static BitmapDescriptor fromPath(String path) { + return new BitmapDescriptor._(['fromPath', path]); + } + + final dynamic _json; + + dynamic _toJson() => _json; +} diff --git a/packages/google_mobile_maps/lib/src/controller.dart b/packages/google_mobile_maps/lib/src/controller.dart index a211d03ee551..b13e116c18ad 100644 --- a/packages/google_mobile_maps/lib/src/controller.dart +++ b/packages/google_mobile_maps/lib/src/controller.dart @@ -5,8 +5,7 @@ part of google_mobile_maps; final MethodChannel _channel = - const MethodChannel('plugins.flutter.io/google_mobile_maps') - ..invokeMethod('init'); + const MethodChannel('plugins.flutter.io/google_mobile_maps'); /// Controller for a single GoogleMaps instance. /// @@ -18,10 +17,14 @@ class GoogleMapsController { GoogleMapsController(this.id); + static Future init() async { + await _channel.invokeMethod('init'); + } + Future animateCamera(CameraUpdate cameraUpdate) async { final int id = await this.id; await _channel.invokeMethod('animateCamera', { - 'id': id, + 'map': id, 'cameraUpdate': cameraUpdate._toJson(), }); } @@ -29,17 +32,21 @@ class GoogleMapsController { Future moveCamera(CameraUpdate cameraUpdate) async { final int id = await this.id; await _channel.invokeMethod('moveCamera', { - 'id': id, + 'map': id, 'cameraUpdate': cameraUpdate._toJson(), }); } - Future addMarker(MarkerOptions markerOptions) async { + Future addMarker(MarkerOptions markerOptions) async { final int id = await this.id; - await _channel.invokeMethod('addMarker', { - 'id': id, - 'markerOptions': markerOptions._toJson(), - }); + final String markerId = await _channel.invokeMethod( + 'addMarker', + { + 'map': id, + 'markerOptions': markerOptions._toJson(), + }, + ); + return new Marker._(id, markerId, markerOptions); } } @@ -89,7 +96,7 @@ class _GoogleMapsPlatformOverlay extends PlatformOverlay { Future show(Offset physicalOffset) async { final int id = await _textureId.future; _channel.invokeMethod('showMapOverlay', { - 'id': id, + 'map': id, 'x': physicalOffset.dx, 'y': physicalOffset.dy, }); @@ -99,7 +106,7 @@ class _GoogleMapsPlatformOverlay extends PlatformOverlay { Future hide() async { final int id = await _textureId.future; _channel.invokeMethod('hideMapOverlay', { - 'id': id, + 'map': id, }); } @@ -107,7 +114,7 @@ class _GoogleMapsPlatformOverlay extends PlatformOverlay { Future dispose() async { final int id = await _textureId.future; _channel.invokeMethod('disposeMap', { - 'id': id, + 'map': id, }); } } diff --git a/packages/google_mobile_maps/lib/src/marker.dart b/packages/google_mobile_maps/lib/src/marker.dart index c22b6670f064..14b9b91ee97c 100644 --- a/packages/google_mobile_maps/lib/src/marker.dart +++ b/packages/google_mobile_maps/lib/src/marker.dart @@ -4,12 +4,127 @@ part of google_mobile_maps; +class Marker { + Marker._(this.mapId, this.id, MarkerOptions options) : _options = options; + + final int mapId; + final String id; + MarkerOptions _options; + + Future update(MarkerOptions options) async { + assert(options != null); + _options = options; + await _channel.invokeMethod('marker#update', { + 'map': mapId, + 'marker': id, + 'markerOptions': options._toJson(), + }); + } + + MarkerOptions get options => _options; + + Future remove() async { + await _channel.invokeMethod('marker#remove', { + 'map': mapId, + 'marker': id, + }); + } + + Future hideInfoWindow() async { + await _channel.invokeMethod('marker#hideInfoWindow', { + 'map': mapId, + 'marker': id, + }); + } + + Future showInfoWindow() async { + await _channel.invokeMethod('marker#showInfoWindow', { + 'map': mapId, + 'marker': id, + }); + } +} + class MarkerOptions { + static const String unspecified = 'Unspecified'; final LatLng position; + final double alpha; + final Offset anchor; + final bool draggable; + final bool flat; + final BitmapDescriptor icon; + final Offset infoWindowAnchor; + final double rotation; + final String snippet; + final String title; + final bool visible; + final double zIndex; + + const MarkerOptions({ + @required this.position, + this.alpha = 1.0, + this.anchor = const Offset(0.5, 1.0), + this.draggable = false, + this.flat = false, + this.icon = BitmapDescriptor.defaultMarker, + this.infoWindowAnchor = const Offset(0.5, 0.0), + this.rotation = 0.0, + this.snippet, + this.title, + this.visible = true, + this.zIndex = 0.0, + }) : assert(position != null), + assert(alpha != null), + assert(anchor != null), + assert(draggable != null), + assert(flat != null), + assert(icon != null), + assert(infoWindowAnchor != null), + assert(rotation != null), + assert(zIndex != null), + assert(visible != null); - const MarkerOptions({@required this.position}) : assert(position != null); + MarkerOptions copyWith({ + LatLng position, + double alpha, + Offset anchor, + bool draggable, + bool flat, + BitmapDescriptor icon, + Offset infoWindowAnchor, + double rotation, + String snippet = unspecified, + String title = unspecified, + double zIndex, + bool visible, + }) => + new MarkerOptions( + position: position ?? this.position, + alpha: alpha ?? this.alpha, + anchor: anchor ?? this.anchor, + draggable: draggable ?? this.draggable, + flat: flat ?? this.flat, + icon: icon ?? this.icon, + infoWindowAnchor: infoWindowAnchor ?? this.infoWindowAnchor, + rotation: rotation ?? this.rotation, + snippet: identical(snippet, unspecified) ? this.snippet : snippet, + title: identical(title, unspecified) ? this.title : title, + zIndex: zIndex ?? this.zIndex, + visible: visible ?? this.visible, + ); dynamic _toJson() => { 'position': position._toJson(), + 'alpha': alpha, + 'anchor': [anchor.dx, anchor.dy], + 'draggable': draggable, + 'flat': flat, + 'icon': icon._toJson(), + 'infoWindowAnchor': [infoWindowAnchor.dx, infoWindowAnchor.dy], + 'rotation': rotation, + 'snippet': snippet, + 'title': title, + 'visible': visible, + 'zIndex': zIndex, }; }