diff --git a/README.md b/README.md index 2400369..677d4b2 100644 --- a/README.md +++ b/README.md @@ -153,10 +153,11 @@ You can refer to the [example](https://github.com/apptreesoftware/flutter_google - [X] Customize Pin color - [X] Polyline support - [X] Polygon support +- [X] Customize pin image +- [X] Remove markers, polylines & polygons. ### Upcoming -- [ ] Customize pin image -- [ ] Remove markers + - [ ] Bounds geometry functions ## Usage examples @@ -192,6 +193,93 @@ mapView.setMarkers([ mapView.addMarker(new Marker("3", "10 Barrel", 45.5259467, -122.687747, color: Colors.purple)); ``` + +#### Edit custom Marker image +First add your assets to a folder in your project directory. The name of the folder could be any but "images" or "assets" are the more common. +It should look like this. + +```dart +- project_name + |-android + |-images + |-flower_vase.png + |-ios + |-lib + # Rest of project folders and files +``` + +Then add asset to the pubspec.yaml under flutter tag. +```dart +flutter: + # Code already existent + + # Added asset. + assets: + - images/flower_vase.png +``` + +Finally use the asset name as icon for your marker. If the width or height is not set or is equals to 0, the image original value of said attribute will be used. + +```dart +new Marker( + "1", + "Something fragile!", + 45.52480841512737, + -122.66201455146073, + color: Colors.blue, + draggable: true, //Allows the user to move the marker. + markerIcon: new MarkerIcon( + "images/flower_vase.png", //Asset to be used as icon + width: 112.0, //New width for the asset + height: 75.0, // New height for the asset + ), + ); +``` +#### Set a Marker draggable and listening to position changes +First set the draggable attribute of a marker to true. +```dart +Marker marker=new Marker( + "1", + "Something fragile!", + 45.52480841512737, + -122.66201455146073, + draggable: true, //Allows the user to move the marker. + ); +``` +Now add listeners for the events. +```dart +// This listener fires when the marker is long pressed and could be moved. +mapView.onAnnotationDragStart.listen((markerMap) { + var marker = markerMap.keys.first; + var location = markerMap[marker]; // The original location of the marker before moving it. Use it if needed. + print("Annotation ${marker.id} dragging started"); + }); +// This listener fires when the user releases the marker. +mapView.onAnnotationDragEnd.listen((markerMap) { + var marker = markerMap.keys.first; + var location = markerMap[marker]; // The actual position of the marker after finishing the dragging. + print("Annotation ${marker.id} dragging ended"); + }); +// This listener fires every time the marker changes position. +mapView.onAnnotationDrag.listen((markerMap) { + var marker = markerMap.keys.first; + var location = markerMap[marker]; // The updated position of the marker. + print("Annotation ${marker.id} moved to ${location.latitude} , ${location + .longitude}"); + }); +``` + +#### Add a single polyline to the map +```dart +mapView.addPolyline(new Polyline( + "12", + [ + new Location(45.519698, -122.674932), + new Location(45.516687, -122.667014), + ], + width: 15.0)); +``` + #### Add multiple polylines to the map ```dart mapView.setPolylines([ @@ -217,16 +305,23 @@ mapView.setPolylines([ ]); ``` -#### Add a single polyline to the map +#### Add a single polygon to the map ```dart -mapView.addPolyline(new Polyline( - "12", - [ - new Location(45.519698, -122.674932), - new Location(45.516687, -122.667014), - ], - width: 15.0)); +mapView.addPolygon(new Polygon( + "111", + [ + new Location(45.5231233, -122.6733130), + new Location(45.5233225, -122.6732969), + new Location(45.5232398, -122.6733506), + new Location(45.5231233, -122.6733130), + ], + jointType: FigureJointType.round, + strokeWidth: 5.0, + strokeColor: Colors.red, + fillColor: Color.fromARGB(75, 255, 0, 0), + )); ``` + #### Add multiple polygons to the map ```dart mapView.setPolygons([ @@ -268,22 +363,12 @@ mapView.addPolyline(new Polyline( ]); ``` -#### Add a single polygon to the map +#### Remove elements from the map ```dart -mapView.addPolygon(new Polygon( - "111", - [ - new Location(45.5231233, -122.6733130), - new Location(45.5233225, -122.6732969), - new Location(45.5232398, -122.6733506), - new Location(45.5231233, -122.6733130), - ], - jointType: FigureJointType.round, - strokeWidth: 5.0, - strokeColor: Colors.red, - fillColor: Color.fromARGB(75, 255, 0, 0), - )); +//Remove all markers +mapView.clearA ``` + #### Zoom to fit all the pins on the map ```dart mapView.zoomToFit(padding: 100); diff --git a/android/build.gradle b/android/build.gradle index 3b7891c..30a4b58 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ group 'com.apptreesoftware.mapview' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.2.41' + ext.kotlin_version = '1.2.50' repositories { google() jcenter() @@ -45,6 +45,6 @@ android { dependencies { implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.41' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.50' implementation 'com.google.android.gms:play-services-maps:15.0.1' } diff --git a/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt b/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt index b1bf484..1d659af 100644 --- a/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt +++ b/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt @@ -3,6 +3,9 @@ package com.apptreesoftware.mapview import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager +import android.content.res.AssetFileDescriptor +import android.graphics.Bitmap +import android.graphics.BitmapFactory import android.os.Bundle import android.support.v4.app.ActivityCompat import android.support.v4.content.ContextCompat @@ -22,7 +25,6 @@ class MapActivity : AppCompatActivity(), var polylineIdLookup = HashMap() var polygonIdLookup = HashMap() val PermissionRequest = 1 - var tapCoordinate: LatLng? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -31,6 +33,8 @@ class MapActivity : AppCompatActivity(), val mapFragment = supportFragmentManager.findFragmentById( R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) + if (MapViewPlugin.hideToolbar) + this.supportActionBar?.hide() MapViewPlugin.mapActivity = this } @@ -38,7 +42,7 @@ class MapActivity : AppCompatActivity(), override fun onMapReady(map: GoogleMap) { googleMap = map map.setMapType(MapViewPlugin.mapViewType) - + map.uiSettings.isCompassEnabled = MapViewPlugin.showCompassButton if (MapViewPlugin.showUserLocation) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { @@ -47,11 +51,31 @@ class MapActivity : AppCompatActivity(), ActivityCompat.requestPermissions(this, array, PermissionRequest) } else { map.isMyLocationEnabled = true - map.uiSettings.isMyLocationButtonEnabled = true - map.uiSettings.isIndoorLevelPickerEnabled = true + map.uiSettings.isMyLocationButtonEnabled = MapViewPlugin.showMyLocationButton } } + map.setOnMarkerDragListener(object : GoogleMap.OnMarkerDragListener { + override fun onMarkerDragEnd(p0: Marker?) { + if (p0 != null) { + val id: String = p0.tag as String + MapViewPlugin.annotationDragEnd(id, p0.position) + } + } + + override fun onMarkerDragStart(p0: Marker?) { + if (p0 != null) { + val id: String = p0.tag as String + MapViewPlugin.annotationDragStart(id, p0.position) + } + } + override fun onMarkerDrag(p0: Marker?) { + if (p0 != null) { + val id: String = p0.tag as String + MapViewPlugin.annotationDrag(id, p0.position) + } + } + }) map.setOnMapClickListener { latLng -> MapViewPlugin.mapTapped(latLng) } @@ -109,9 +133,14 @@ class MapActivity : AppCompatActivity(), get() = googleMap?.cameraPosition?.target ?: LatLng(0.0, 0.0) - fun setCamera(target: LatLng, zoom: Float) { - googleMap?.animateCamera( - CameraUpdateFactory.newLatLngZoom(target, zoom)) + fun setCamera(target: LatLng, zoom: Float, bearing: Float, tilt: Float) { + val cameraPosition = CameraPosition.Builder() + .target(target) + .zoom(zoom) + .bearing(bearing) + .tilt(tilt) + .build() + googleMap?.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)) } fun setAnnotations(annotations: List) { @@ -361,26 +390,39 @@ class MapActivity : AppCompatActivity(), } fun createMarkerForAnnotation(annotation: MapAnnotation, map: GoogleMap): Marker { - val marker: Marker + val markerOptions = MarkerOptions() + .position(annotation.coordinate) + .title(annotation.title) + .draggable(annotation.draggable) + .rotation(annotation.rotation.toFloat()) if (annotation is ClusterAnnotation) { - marker = map - .addMarker(MarkerOptions() - .position(annotation.coordinate) - .title(annotation.title) - .icon( - BitmapDescriptorFactory.defaultMarker( - annotation.colorHue))) - marker.tag = annotation.identifier + markerOptions.snippet(annotation.clusterCount.toString()) + } + var bitmap: Bitmap? = null + if (annotation.icon != null) { + try { + val assetFileDescriptor: AssetFileDescriptor = MapViewPlugin.getAssetFileDecriptor(annotation.icon.asset) + val fd = assetFileDescriptor.createInputStream() + bitmap = BitmapFactory.decodeStream(fd) + var width = annotation.icon.width + var height = annotation.icon.height + if (width == 0.0) + width = bitmap.width.toDouble() + if (height == 0.0) + height = bitmap.height.toDouble() + bitmap = Bitmap.createScaledBitmap(bitmap, width.toInt(), height.toInt(), false) + } catch (exception: Exception) { + exception.printStackTrace() + } + } + if (bitmap != null) { + markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)) } else { - marker = map - .addMarker(MarkerOptions() - .position(annotation.coordinate) - .title(annotation.title) - .icon( - BitmapDescriptorFactory.defaultMarker( - annotation.colorHue))) - marker.tag = annotation.identifier + markerOptions.icon(BitmapDescriptorFactory.defaultMarker( + annotation.colorHue)) } + val marker = map.addMarker(markerOptions) + marker.tag = annotation.identifier return marker } diff --git a/android/src/main/kotlin/com/apptreesoftware/mapview/MapAnnotation.kt b/android/src/main/kotlin/com/apptreesoftware/mapview/MapAnnotation.kt index d5956a9..d3721df 100644 --- a/android/src/main/kotlin/com/apptreesoftware/mapview/MapAnnotation.kt +++ b/android/src/main/kotlin/com/apptreesoftware/mapview/MapAnnotation.kt @@ -3,38 +3,47 @@ package com.apptreesoftware.mapview import android.graphics.Color import com.google.android.gms.maps.model.LatLng -open class MapAnnotation(val identifier: String, val title: String, val coordinate: LatLng, - val color: Int) { +open class MapAnnotation(val identifier: String, val title: String, val coordinate: LatLng, val rotation: Double, + val icon: MarkerIcon?, val color: Int, val draggable: Boolean) { companion object { fun fromMap(map: Map): MapAnnotation? { val type = map["type"] as String? ?: return null val identifier = map["id"] as String val latitude = map["latitude"] as Double val longitude = map["longitude"] as Double + val rotation = map["rotation"] as Double val title = map["title"] as String + var icon: MarkerIcon? = null + if (map.containsKey("markerIcon")) + icon = MarkerIcon.fromMap(map["markerIcon"] as Map) val colorMap = map["color"] as Map val color = colorFromMap(colorMap) + val draggable = map["draggable"] as Boolean if (type == "cluster") { val clusterCount = map["clusterCount"] as Int - return ClusterAnnotation(identifier, title, LatLng(latitude, longitude), color, - clusterCount) + return ClusterAnnotation(identifier, title, LatLng(latitude, longitude), rotation, icon, color, draggable, + clusterCount) } - return MapAnnotation(identifier, title, LatLng(latitude, longitude), color) + return MapAnnotation(identifier, title, LatLng(latitude, longitude), rotation, icon, color, draggable) } } - val colorHue: Float get() { - val hsv = FloatArray(3) - Color.colorToHSV(this.color, hsv) - return hsv[0] - } + val colorHue: Float + get() { + val hsv = FloatArray(3) + Color.colorToHSV(this.color, hsv) + return hsv[0] + } } class ClusterAnnotation(identifier: String, title: String, coordinate: LatLng, + rotation: Double, + icon: MarkerIcon?, color: Int, - val clusterCount: Int) : MapAnnotation(identifier, title, coordinate, color) + draggable: Boolean, + val clusterCount: Int) : MapAnnotation(identifier, title, coordinate, rotation, icon, color, draggable) fun colorFromMap(map: Map): Int { val r = map["r"] ?: 0 @@ -42,4 +51,37 @@ fun colorFromMap(map: Map): Int { val b = map["b"] ?: 0 val a = map["a"] ?: 0 return Color.argb(a, r, g, b) +} + +class MarkerIcon(val asset: String, val width: Double, val height: Double) { + companion object { + fun fromMap(map: Map): MarkerIcon { + val asset = map["asset"] as String + val width = map["width"] as Double + val height = map["height"] as Double + return MarkerIcon(asset, width, height) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is MarkerIcon) return false + + if (asset != other.asset) return false + if (width != other.width) return false + if (height != other.height) return false + + return true + } + + override fun hashCode(): Int { + var result = asset.hashCode() + result = 31 * result + width.hashCode() + result = 31 * result + height.hashCode() + return result + } + + override fun toString(): String { + return "MarkerIcon(asset='$asset', width=$width, height=$height)" + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt b/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt index 857d244..94420ff 100644 --- a/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt +++ b/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt @@ -2,7 +2,9 @@ package com.apptreesoftware.mapview import android.app.Activity import android.content.Intent +import android.content.res.AssetFileDescriptor import android.location.Location +import android.os.Build import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.model.CameraPosition @@ -13,7 +15,7 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.PluginRegistry.Registrar /* -Everytime i reformated the code, this imports were removed so i put them here +Every time i reformatted the code, this imports were removed so i put them here for easier access. import io.flutter.plugin.common.MethodCall @@ -35,17 +37,22 @@ class MapViewPlugin(val activity: Activity) : MethodCallHandler { lateinit var channel: MethodChannel var toolbarActions: List = emptyList() var showUserLocation: Boolean = false + var showMyLocationButton: Boolean = false + var showCompassButton: Boolean = false + var hideToolbar: Boolean = false var mapTitle: String = "" lateinit var initialCameraPosition: CameraPosition var mapActivity: MapActivity? = null val REQUEST_GOOGLE_PLAY_SERVICES = 1000 var mapViewType: Int = GoogleMap.MAP_TYPE_NORMAL + lateinit var registrar: Registrar @JvmStatic fun registerWith(registrar: Registrar): Unit { channel = MethodChannel(registrar.messenger(), "com.apptreesoftware.map_view") val plugin = MapViewPlugin(activity = registrar.activity()) channel.setMethodCallHandler(plugin) + this.registrar = registrar } fun handleToolbarAction(id: Int) { @@ -80,6 +87,30 @@ class MapViewPlugin(val activity: Activity) : MethodCallHandler { this.channel.invokeMethod("annotationTapped", id) } + fun annotationDragStart(id: String, latLng: LatLng) { + this.channel.invokeMethod("annotationDragStart", mapOf( + "id" to id, + "latitude" to latLng.latitude, + "longitude" to latLng.longitude + )) + } + + fun annotationDragEnd(id: String, latLng: LatLng) { + this.channel.invokeMethod("annotationDragEnd", mapOf( + "id" to id, + "latitude" to latLng.latitude, + "longitude" to latLng.longitude + )) + } + + fun annotationDrag(id: String, latLng: LatLng) { + this.channel.invokeMethod("annotationDrag", mapOf( + "id" to id, + "latitude" to latLng.latitude, + "longitude" to latLng.longitude + )) + } + fun polylineTapped(id: String) { this.channel.invokeMethod("polylineTapped", id) } @@ -92,20 +123,37 @@ class MapViewPlugin(val activity: Activity) : MethodCallHandler { this.channel.invokeMethod("cameraPositionChanged", mapOf( "latitude" to pos.target.latitude, "longitude" to pos.target.longitude, - "zoom" to pos.zoom + "zoom" to pos.zoom, + "bearing" to pos.bearing, + "tilt" to pos.tilt )) } fun locationDidUpdate(loc: Location) { + var verticalAccuracy = 0.0f + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + verticalAccuracy = loc.verticalAccuracyMeters this.channel.invokeMethod("locationUpdated", mapOf( "latitude" to loc.latitude, - "longitude" to loc.longitude + "longitude" to loc.longitude, + "time" to loc.time, + "altitude" to loc.altitude, + "speed" to loc.speed, + "bearing" to loc.bearing, + "horizontalAccuracy" to loc.accuracy, + "verticalAccuracy" to verticalAccuracy )) } fun infoWindowTapped(id: String) { this.channel.invokeMethod("infoWindowTapped", id) } + + fun getAssetFileDecriptor(asset: String): AssetFileDescriptor { + val assetManager = registrar.context().getAssets() + val key = registrar.lookupKeyForAsset(asset) + return assetManager.openFd(key) + } } override fun onMethodCall(call: MethodCall, result: Result): Unit { @@ -121,11 +169,14 @@ class MapViewPlugin(val activity: Activity) : MethodCallHandler { initialCameraPosition = getCameraPosition(cameraDict) toolbarActions = getToolbarActions(call.argument>>("actions")) showUserLocation = mapOptions["showUserLocation"] as Boolean + showMyLocationButton = mapOptions["showMyLocationButton"] as Boolean + showCompassButton = mapOptions["showCompassButton"] as Boolean + hideToolbar = mapOptions["hideToolbar"] as Boolean mapTitle = mapOptions["title"] as String if (mapOptions["mapViewType"] != null) { - var mappedMapType: Int? = mapTypeMapping.get(mapOptions["mapViewType"]); - if (mappedMapType != null) mapViewType = mappedMapType as Int; + val mappedMapType: Int? = mapTypeMapping.get(mapOptions["mapViewType"]); + if (mappedMapType != null) mapViewType = mappedMapType; } val intent = Intent(activity, MapActivity::class.java) @@ -229,7 +280,9 @@ class MapViewPlugin(val activity: Activity) : MethodCallHandler { val lat = map["latitude"] as Double val lng = map["longitude"] as Double val zoom = map["zoom"] as Double - mapActivity?.setCamera(LatLng(lat, lng), zoom.toFloat()) + val bearing = map["bearing"] as Double + val tilt = map["tilt"] as Double + mapActivity?.setCamera(LatLng(lat, lng), zoom.toFloat(), bearing.toFloat(), tilt.toFloat()) } fun handleZoomToAnnotations(map: Map) { diff --git a/example/android/app/src/main/gen/com/apptreesoftware/mapviewexample/Manifest.java b/example/android/app/src/main/gen/com/apptreesoftware/mapviewexample/Manifest.java new file mode 100644 index 0000000..1cc4827 --- /dev/null +++ b/example/android/app/src/main/gen/com/apptreesoftware/mapviewexample/Manifest.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.apptreesoftware.mapviewexample; + +/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */ +public final class Manifest { +} \ No newline at end of file diff --git a/example/android/app/src/main/gen/com/apptreesoftware/mapviewexample/R.java b/example/android/app/src/main/gen/com/apptreesoftware/mapviewexample/R.java new file mode 100644 index 0000000..4289324 --- /dev/null +++ b/example/android/app/src/main/gen/com/apptreesoftware/mapviewexample/R.java @@ -0,0 +1,7 @@ +/*___Generated_by_IDEA___*/ + +package com.apptreesoftware.mapviewexample; + +/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */ +public final class R { +} \ No newline at end of file diff --git a/example/images/flower_vase.png b/example/images/flower_vase.png new file mode 100644 index 0000000..5148f22 Binary files /dev/null and b/example/images/flower_vase.png differ diff --git a/example/ios/Flutter/flutter_assets/AssetManifest.json b/example/ios/Flutter/flutter_assets/AssetManifest.json index 9e26dfe..ca13a08 100644 --- a/example/ios/Flutter/flutter_assets/AssetManifest.json +++ b/example/ios/Flutter/flutter_assets/AssetManifest.json @@ -1 +1 @@ -{} \ No newline at end of file +{"images/flower_vase.png":["images/flower_vase.png"]} \ No newline at end of file diff --git a/example/ios/Flutter/flutter_assets/LICENSE b/example/ios/Flutter/flutter_assets/LICENSE index bdb38ea..a505c47 100644 --- a/example/ios/Flutter/flutter_assets/LICENSE +++ b/example/ios/Flutter/flutter_assets/LICENSE @@ -44,6 +44,7 @@ source_map_stack_trace stream_channel typed_data utf +vm_service_client Copyright 2015, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -3121,6 +3122,7 @@ glob http http_multi_server http_parser +json_rpc_2 matcher path pool diff --git a/example/ios/Flutter/flutter_assets/isolate_snapshot_data b/example/ios/Flutter/flutter_assets/isolate_snapshot_data index 61b56fa..6d6eeb1 100644 Binary files a/example/ios/Flutter/flutter_assets/isolate_snapshot_data and b/example/ios/Flutter/flutter_assets/isolate_snapshot_data differ diff --git a/example/ios/Flutter/flutter_assets/kernel_blob.bin b/example/ios/Flutter/flutter_assets/kernel_blob.bin index 584af48..32a8d0c 100644 Binary files a/example/ios/Flutter/flutter_assets/kernel_blob.bin and b/example/ios/Flutter/flutter_assets/kernel_blob.bin differ diff --git a/example/ios/Flutter/flutter_assets/platform.dill b/example/ios/Flutter/flutter_assets/platform.dill index ffb3694..e0a1f14 100644 Binary files a/example/ios/Flutter/flutter_assets/platform.dill and b/example/ios/Flutter/flutter_assets/platform.dill differ diff --git a/example/ios/Flutter/flutter_assets/vm_snapshot_data b/example/ios/Flutter/flutter_assets/vm_snapshot_data index 7c26fe5..0a8fb30 100644 Binary files a/example/ios/Flutter/flutter_assets/vm_snapshot_data and b/example/ios/Flutter/flutter_assets/vm_snapshot_data differ diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3aded58..52ddcf6 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -11,7 +11,7 @@ PODS: DEPENDENCIES: - Flutter (from `/Users/ceisufro/flutter/bin/cache/artifacts/engine/ios`) - - map_view (from `/Users/ceisufro/AndroidStudioProjects/flutter_map_view/ios`) + - map_view (from `/Users/ceisufro/AndroidStudioProjects/flutter_google_map_view/ios`) SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -21,7 +21,7 @@ EXTERNAL SOURCES: Flutter: :path: "/Users/ceisufro/flutter/bin/cache/artifacts/engine/ios" map_view: - :path: "/Users/ceisufro/AndroidStudioProjects/flutter_map_view/ios" + :path: "/Users/ceisufro/AndroidStudioProjects/flutter_google_map_view/ios" SPEC CHECKSUMS: Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296 diff --git a/example/lib/main.dart b/example/lib/main.dart index 67dc487..26a6b4f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -33,8 +33,18 @@ class _MyAppState extends State { //Marker bubble List _markers = [ new Marker( - "1", "Something fragile!", 45.52480841512737, -122.66201455146073, - color: Colors.blue), + "1", + "Something fragile!", + 45.52480841512737, + -122.66201455146073, + color: Colors.blue, + draggable: true, //Allows the user to move the marker. + markerIcon: new MarkerIcon( + "images/flower_vase.png", + width: 112.0, + height: 75.0, + ), + ), ]; //Line @@ -181,17 +191,21 @@ class _MyAppState extends State { new MapOptions( mapViewType: MapViewType.normal, showUserLocation: true, + showMyLocationButton: true, + showCompassButton: true, + initialCameraPosition: new CameraPosition(new Location(45.526607443935724, -122.66731660813093), 15.0), + hideToolbar: false, title: "Recently Visited"), toolbarActions: [new ToolbarAction("Close", 1)]); StreamSubscription sub = mapView.onMapReady.listen((_) { mapView.setMarkers(_markers); mapView.setPolylines(_lines); mapView.setPolygons(_polygons); - mapView.zoomToFit(padding: 100); }); compositeSubscription.add(sub); - sub = mapView.onLocationUpdated - .listen((location) => print("Location updated $location")); + sub = mapView.onLocationUpdated.listen((location) { + print("Location updated $location"); + }); compositeSubscription.add(sub); sub = mapView.onTouchAnnotation .listen((annotation) => print("annotation ${annotation.id} tapped")); @@ -208,6 +222,21 @@ class _MyAppState extends State { sub = mapView.onCameraChanged.listen((cameraPosition) => this.setState(() => this.cameraPosition = cameraPosition)); compositeSubscription.add(sub); + sub = mapView.onAnnotationDragStart.listen((markerMap) { + var marker = markerMap.keys.first; + print("Annotation ${marker.id} dragging started"); + }); + sub = mapView.onAnnotationDragEnd.listen((markerMap) { + var marker = markerMap.keys.first; + print("Annotation ${marker.id} dragging ended"); + }); + sub = mapView.onAnnotationDrag.listen((markerMap) { + var marker = markerMap.keys.first; + var location = markerMap[marker]; + print("Annotation ${marker.id} moved to ${location.latitude} , ${location + .longitude}"); + }); + compositeSubscription.add(sub); sub = mapView.onToolbarAction.listen((id) { print("Toolbar button id = $id"); if (id == 1) { diff --git a/example/pubspec.yaml b/example/pubspec.yaml index c3340d1..f39250b 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -27,6 +27,8 @@ flutter: # the material Icons class. uses-material-design: true + assets: + - images/flower_vase.png # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg diff --git a/flutter_google_map_view.iml b/flutter_google_map_view.iml new file mode 100644 index 0000000..55d90fc --- /dev/null +++ b/flutter_google_map_view.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ios/Classes/MapAnnotation.h b/ios/Classes/MapAnnotation.h index 92b39c7..98790d0 100644 --- a/ios/Classes/MapAnnotation.h +++ b/ios/Classes/MapAnnotation.h @@ -3,14 +3,17 @@ // #import - +#import "MarkerIcon.h" #import @interface MapAnnotation : NSObject @property (nonatomic, retain) NSString *identifier; @property (nonatomic, retain) NSString *title; +@property (nonatomic, retain) MarkerIcon *icon; @property (nonatomic, assign) CLLocationCoordinate2D coordinate; +@property (nonatomic)double rotation; @property (nonatomic, retain) UIColor *color; +@property (nonatomic, assign) BOOL draggable; - (instancetype)initWithDictionary:(NSDictionary *)dictionary; @@ -23,4 +26,4 @@ - (instancetype)initWithDictionary:(NSDictionary *)dictionary; -@end \ No newline at end of file +@end diff --git a/ios/Classes/MapAnnotation.m b/ios/Classes/MapAnnotation.m index 25c3b22..88d116c 100644 --- a/ios/Classes/MapAnnotation.m +++ b/ios/Classes/MapAnnotation.m @@ -4,7 +4,7 @@ #import "MapAnnotation.h" #import "UIColor+Extensions.h" - +#import "MarkerIcon.h" @implementation MapAnnotation { @@ -14,6 +14,11 @@ - (instancetype) initWithDictionary:(NSDictionary *)dictionary { if (self) { self.identifier = dictionary[@"id"]; self.title = dictionary[@"title"]; + self.draggable= [dictionary[@"draggable"] boolValue]; + self.rotation= [dictionary[@"rotation"] doubleValue]; + if([dictionary objectForKey:@"markerIcon"]!=nil){ + self.icon = [MarkerIcon markerIconFromDictionary:dictionary[@"markerIcon"]]; + } self.coordinate = CLLocationCoordinate2DMake([dictionary[@"latitude"] doubleValue], [dictionary[@"longitude"] doubleValue]); self.color = [UIColor colorFromDictionary:dictionary[@"color"]]; } diff --git a/ios/Classes/MapViewController.h b/ios/Classes/MapViewController.h index 5b2bd7e..a50c50d 100644 --- a/ios/Classes/MapViewController.h +++ b/ios/Classes/MapViewController.h @@ -21,7 +21,7 @@ cameraPosition:(GMSCameraPosition *)cameraPosition; - (void)shutdown; -- (void)setCamera:(CLLocationCoordinate2D)location zoom:(float)zoom; +- (void)setCamera:(CLLocationCoordinate2D)location zoom:(float)zoom bearing:(CLLocationDirection)bearing tilt:(double)tilt; - (void)updateAnnotations:(NSArray *)annotations; - (void)clearAnnotations; @@ -45,7 +45,9 @@ - (NSArray *)visiblePolylines; - (NSArray *)visiblePolygons; -- (void)setLocationEnabled:(BOOL) enabled; +- (void)setMapOptions:(BOOL)myLocationEnabled + locationButton:(BOOL)myLocationButton + compassButton:(BOOL)compassButton; @property (readonly) float zoomLevel; @property (readonly) CLLocationCoordinate2D centerLocation; diff --git a/ios/Classes/MapViewController.m b/ios/Classes/MapViewController.m index 543bd41..c20bbf8 100644 --- a/ios/Classes/MapViewController.m +++ b/ios/Classes/MapViewController.m @@ -20,6 +20,8 @@ @interface MapViewController () @property (nonatomic, retain) NSMutableArray *polylines; @property (nonatomic, retain) NSMutableArray *polygons; @property (nonatomic) BOOL _locationEnabled; +@property (nonatomic) BOOL _locationButton; +@property (nonatomic) BOOL _compassButton; @property (nonatomic) BOOL observingLocation; @property (nonatomic, assign) MapViewPlugin *plugin; @property (nonatomic, retain) NSArray *navigationItems; @@ -60,36 +62,40 @@ - (void)viewDidLoad { self.navigationItem.rightBarButtonItems = self.navigationItems; } -- (void)setLocationEnabled:(BOOL)enabled { - self._locationEnabled = enabled; +- (void)setMapOptions:(BOOL)myLocationEnabled + locationButton:(BOOL)myLocationButton + compassButton:(BOOL)compassButton{ + self._locationEnabled = myLocationEnabled; + self._locationButton = myLocationButton; + self._compassButton = compassButton; +} + +- (void) loadMapOptions{ if (self.mapView) { - self.mapView.myLocationEnabled = enabled; - self.mapView.settings.myLocationButton = enabled; - if (enabled) { + self.mapView.settings.compassButton = self._compassButton; + if (self._locationEnabled) { + self.mapView.myLocationEnabled = YES; + self.mapView.settings.myLocationButton = self._locationButton; [self monitorLocationChanges]; } else { [self stopMonitoringLocationChanges]; } } } - - (void)loadView { self.mapView = [GMSMapView mapWithFrame:CGRectZero camera:self.initialCameraPosition]; self.view = self.mapView; // Creates a marker in the center of the map. self.mapView.delegate = self; - self.mapView.myLocationEnabled = self._locationEnabled; - if (self._locationEnabled) { - [self monitorLocationChanges]; - } + [self loadMapOptions]; self.mapView.mapType = self.mapViewType; [self.plugin onMapReady]; } -- (void)setCamera:(CLLocationCoordinate2D)location zoom:(float)zoom { - [self.mapView animateToCameraPosition:[GMSCameraPosition cameraWithTarget:location zoom:zoom]]; +- (void)setCamera:(CLLocationCoordinate2D)location zoom:(float)zoom bearing:(CLLocationDirection)bearing tilt:(double)tilt { + [self.mapView animateToCameraPosition:[GMSCameraPosition cameraWithTarget:location zoom:zoom bearing:bearing viewingAngle:tilt]]; } - (void)updateAnnotations:(NSArray *)annotations { @@ -187,17 +193,35 @@ - (GMSMarker *)createMarkerForAnnotation:(MapAnnotation *)annotation { GMSMarker *marker = [GMSMarker new]; if ([annotation isKindOfClass:[ClusterAnnotation class]]) { ClusterAnnotation *clusterAnnotation = (ClusterAnnotation *)annotation; - marker.position = annotation.coordinate; - marker.title = annotation.title; marker.snippet = [NSString stringWithFormat:@"%i", clusterAnnotation.clusterCount]; + } + UIImage* image; + if(annotation.icon!=nil){ + @try { + NSString* path=[self.plugin getAssetPath:annotation.icon.asset]; + NSData* imagedata=[NSData dataWithContentsOfFile:path]; + image = [UIImage imageWithData:imagedata scale:3.0f]; + double width=annotation.icon.width; + double height=annotation.icon.height; + if(width==0) + width = image.size.width; + if(height==0) + height=image.size.height; + image = [self resizeImage:image scaledToSize:CGSizeMake(width, height)]; + }@catch(NSException* e){ + NSLog(@"Exception: %@",e); + } + } + if(image!=nil){ + marker.icon = image; + }else{ marker.icon = [GMSMarker markerImageWithColor:annotation.color]; - marker.userData = annotation.identifier; - } else { - marker.position = annotation.coordinate; - marker.title = annotation.title; - marker.icon = [GMSMarker markerImageWithColor:annotation.color]; - marker.userData = annotation.identifier; } + marker.position = annotation.coordinate; + marker.title = annotation.title; + marker.rotation = annotation.rotation; + marker.userData = annotation.identifier; + marker.draggable = annotation.draggable; return marker; } @@ -334,6 +358,18 @@ - (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition [self.plugin cameraPositionChanged:position]; } +- (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(nonnull GMSMarker *)marker{ + [self.plugin annotationDragStart:marker.userData position:marker.position]; +} + +- (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(nonnull GMSMarker *)marker{ + [self.plugin annotationDragEnd:marker.userData position:marker.position]; +} + +- (void)mapView:(GMSMapView *)mapView didDragMarker:(nonnull GMSMarker *)marker{ + [self.plugin annotationDrag:marker.userData position:marker.position]; +} + - (CLLocationCoordinate2D) centerLocation { return self.mapView.camera.target; } @@ -385,4 +421,17 @@ - (NSArray *)visiblePolygons { } return visiblePolygons; } + +- (UIImage *)resizeImage:(UIImage*)originalImage scaledToSize:(CGSize)size +{ + if (CGSizeEqualToSize(originalImage.size, size)){ + return originalImage; + } + UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); + [originalImage drawInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + @end diff --git a/ios/Classes/MapViewPlugin.h b/ios/Classes/MapViewPlugin.h index 1c52b62..b36a2be 100644 --- a/ios/Classes/MapViewPlugin.h +++ b/ios/Classes/MapViewPlugin.h @@ -11,13 +11,19 @@ @property (nonatomic, retain) MapViewController *mapViewController; @property (nonatomic, retain) NSString *mapTitle; @property (nonatomic, assign) int mapViewType; +@property (nonatomic, retain) NSObject *registrar; + - (void)onMapReady; - (void)locationDidUpdate:(CLLocation *)location; - (void)annotationTapped:(NSString *)identifier; +- (void)annotationDragStart:(NSString *)identifier position:(CLLocationCoordinate2D)position; +- (void)annotationDragEnd:(NSString *)identifier position:(CLLocationCoordinate2D)position; +- (void)annotationDrag:(NSString *)identifier position:(CLLocationCoordinate2D)position; - (void)polylineTapped:(NSString *)identifier; - (void)polygonTapped:(NSString *)identifier; - (void)infoWindowTapped:(NSString *)identifier; - (void)mapTapped:(CLLocationCoordinate2D)coordinate; - (void)cameraPositionChanged:(GMSCameraPosition *)position; - (int)getMapViewType:(NSString *)mapViewTypeName; +- (NSString *)getAssetPath:(NSString *)iconPath; @end diff --git a/ios/Classes/MapViewPlugin.m b/ios/Classes/MapViewPlugin.m index ccee590..b9cc435 100644 --- a/ios/Classes/MapViewPlugin.m +++ b/ios/Classes/MapViewPlugin.m @@ -12,14 +12,15 @@ + (void)registerWithRegistrar:(NSObject *)registrar { methodChannelWithName:@"com.apptreesoftware.map_view" binaryMessenger:[registrar messenger]]; UIViewController *host = UIApplication.sharedApplication.delegate.window.rootViewController; - MapViewPlugin *instance = [[MapViewPlugin alloc] initWithHost:host channel:channel]; + MapViewPlugin *instance = [[MapViewPlugin alloc] initWithHost:host channel:channel registrar:registrar]; [registrar addMethodCallDelegate:instance channel:channel]; } -- (id)initWithHost:(UIViewController *)host channel:(FlutterMethodChannel *)channel { +- (id)initWithHost:(UIViewController *)host channel:(FlutterMethodChannel *)channel registrar:(NSObject *)registrar { if (self = [super init]) { self.host = host; self.channel = channel; + self.registrar = registrar; } return self; } @@ -44,10 +45,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result navigationItems:[self buttonItemsFromActions:args[@"actions"]] cameraPosition:[self cameraPositionFromDict:cameraDict]]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:vc]; + navController.navigationBar.hidden = [mapOptions[@"hideToolbar"] boolValue]; navController.navigationBar.translucent = NO; [self.host presentViewController:navController animated:true completion:nil]; self.mapViewController = vc; - [self.mapViewController setLocationEnabled:[mapOptions[@"showUserLocation"] boolValue]]; + [self.mapViewController setMapOptions:[mapOptions[@"showUserLocation"] boolValue] locationButton:[mapOptions[@"showMyLocationButton"] boolValue] compassButton:[mapOptions[@"showCompassButton"] boolValue]]; result(@YES); } else if ([@"getVisibleMarkers" isEqualToString:call.method]) { result(self.mapViewController.visibleMarkers); @@ -217,7 +219,7 @@ - (void)handleToolbar:(UIBarButtonItem *)item { - (void)handleSetCamera:(NSDictionary *)cameraUpdate { CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([cameraUpdate[@"latitude"] doubleValue], [cameraUpdate[@"longitude"] doubleValue]); - [self.mapViewController setCamera:coordinate zoom:[cameraUpdate[@"zoom"] floatValue]]; + [self.mapViewController setCamera:coordinate zoom:[cameraUpdate[@"zoom"] floatValue] bearing:[cameraUpdate[@"bearing"] doubleValue] tilt:[cameraUpdate[@"tilt"] doubleValue]]; } - (void)onMapReady { @@ -225,12 +227,31 @@ - (void)onMapReady { } - (void)locationDidUpdate:(CLLocation *)location { - [self.channel invokeMethod:@"locationUpdated" arguments:@{@"latitude": @(location.coordinate.latitude), @"longitude": @(location.coordinate.longitude)}]; + NSInteger time = location.timestamp.timeIntervalSince1970; + time *= 1000; + [self.channel invokeMethod:@"locationUpdated" arguments:@{@"latitude": @(location.coordinate.latitude), + @"longitude": @(location.coordinate.longitude), + @"time":@(time), + @"altitude":@(location.altitude), + @"speed":@(location.speed), + @"bearing":@(location.course), + @"horizontalAccuracy":@(location.horizontalAccuracy), + @"verticalAccuracy":@(location.verticalAccuracy) + }]; } -- (void)annotationTapped:(NSString *)identifier { +- (void)annotationTapped:(NSString *)identifier{ [self.channel invokeMethod:@"annotationTapped" arguments:identifier]; } +- (void)annotationDragStart:(NSString *)identifier position:(CLLocationCoordinate2D)position { + [self.channel invokeMethod:@"annotationDragStart" arguments:@{@"id": identifier, @"latitude": @(position.latitude),@"longitude": @(position.longitude)}]; +} +- (void)annotationDragEnd:(NSString *)identifier position:(CLLocationCoordinate2D)position { + [self.channel invokeMethod:@"annotationDragEnd" arguments:@{@"id": identifier, @"latitude": @(position.latitude),@"longitude": @(position.longitude)}]; +} +- (void)annotationDrag:(NSString *)identifier position:(CLLocationCoordinate2D)position{ + [self.channel invokeMethod:@"annotationDrag" arguments:@{@"id": identifier, @"latitude": @(position.latitude),@"longitude": @(position.longitude)}]; +} - (void)polylineTapped:(NSString *)identifier { [self.channel invokeMethod:@"polylineTapped" arguments:identifier]; } @@ -249,6 +270,8 @@ - (void)cameraPositionChanged:(GMSCameraPosition *)position { [self.channel invokeMethod:@"cameraPositionChanged" arguments:@{ @"latitude": @(position.target.latitude), @"longitude": @(position.target.longitude), + @"bearing": @(position.bearing), + @"tilt": @(position.viewingAngle), @"zoom": @(position.zoom) }]; } @@ -280,4 +303,11 @@ - (int)getMapViewType:(NSString *)mapViewTypeName { } return mapType; } + +-(NSString *) getAssetPath:(NSString *)iconPath{ + NSString* key = [self.registrar lookupKeyForAsset:iconPath]; + NSString* path= [[NSBundle mainBundle] pathForResource:key ofType:nil]; + return path; +} + @end diff --git a/ios/Classes/MarkerIcon.h b/ios/Classes/MarkerIcon.h new file mode 100644 index 0000000..d1319f4 --- /dev/null +++ b/ios/Classes/MarkerIcon.h @@ -0,0 +1,16 @@ +// +// Created by Luis Jara on 06/09/18. +// + +#import + +@interface MarkerIcon : NSObject +@property (nonatomic, retain)NSString* asset; +@property (nonatomic)double width; +@property (nonatomic)double height; + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; + ++ (instancetype)markerIconFromDictionary:(NSDictionary *)dictionary; + +@end diff --git a/ios/Classes/MarkerIcon.m b/ios/Classes/MarkerIcon.m new file mode 100644 index 0000000..c0afd8c --- /dev/null +++ b/ios/Classes/MarkerIcon.m @@ -0,0 +1,24 @@ +// +// Created by Luis Jara on 06/22/18. +// + +#import "MarkerIcon.h" + +@implementation MarkerIcon { +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + self = [super init]; + if (self) { + self.asset = dictionary[@"asset"]; + self.width = [dictionary[@"width"] doubleValue]; + self.height = [dictionary[@"height"] doubleValue]; + } + return self; +} + ++ (instancetype)markerIconFromDictionary:(NSDictionary *)dictionary { + return [[MarkerIcon alloc] initWithDictionary:dictionary]; +} + +@end diff --git a/lib/camera_position.dart b/lib/camera_position.dart index 6aa31dc..98ea3a7 100644 --- a/lib/camera_position.dart +++ b/lib/camera_position.dart @@ -3,16 +3,22 @@ import 'package:map_view/location.dart'; class CameraPosition { final Location center; final double zoom; + final double bearing; + final double tilt; - const CameraPosition(this.center, this.zoom); + const CameraPosition(this.center, this.zoom, + {this.bearing = 0.0, this.tilt = 0.0}); factory CameraPosition.fromMap(Map map) { - return new CameraPosition(new Location.fromMap(map), map["zoom"]); + return new CameraPosition(new Location.fromMap(map), map["zoom"], + bearing: map["bearing"], tilt: map["tilt"]); } Map toMap() { Map map = center.toMap(); map["zoom"] = zoom; + map["bearing"] = bearing; + map["tilt"] = tilt; return map; } } diff --git a/lib/location.dart b/lib/location.dart index b3070af..39e0bf0 100644 --- a/lib/location.dart +++ b/lib/location.dart @@ -2,11 +2,65 @@ class Location { final double latitude; final double longitude; - const Location(this.latitude, this.longitude); + /// Time in milliseconds + final int time; + + /// Altitude in meters. + /// + /// Read platform specification for this value. + final double altitude; + + /// Speed in meters per second + final double speed; + + /// Bearing value in a range from 0.0 to 360.0. + /// + /// This value is called "course" in the iOS platform. Check the CLLocation class reference for more info. + /// + /// This is not the device orientation. For more info, read each platform documentation regarding this value. + final double bearing; + + /// Horizontal accuracy in meters + /// + /// Read platform specification for this value. + final double horizontalAccuracy; + + /// Vertical accuracy in meters. + /// + /// Read platform specification for this value. + /// In Android is required API 26 onwards. + final double verticalAccuracy; + + const Location(latitude, longitude) + : latitude = latitude, + longitude = longitude, + time = 0, + altitude = 0.0, + speed = 0.0, + bearing = 0.0, + horizontalAccuracy = 0.0, + verticalAccuracy = 0.0; + + Location.full(this.latitude, this.longitude, this.time, this.altitude, + this.speed, this.bearing, this.horizontalAccuracy, this.verticalAccuracy); + factory Location.fromMap(Map map) { return new Location(map["latitude"], map["longitude"]); } + factory Location.fromMapFull(Map map) { + return new Location.full( + map["latitude"], + map["longitude"], + map["time"], + map["altitude"], + map["speed"], + map["bearing"], + map["horizontalAccuracy"], + map["verticalAccuracy"], + ); + } + static List> listToMap(List list) { List> result = []; for (var element in list) { @@ -15,12 +69,19 @@ class Location { return result; } - Map toMap() { - return {"latitude": this.latitude, "longitude": this.longitude}; - } + Map toMap() => { + "latitude": latitude, + "longitude": longitude, + "time": time, + "altitude": altitude, + "speed": speed, + "bearing": bearing, + "horizontalAccuracy": horizontalAccuracy, + "verticalAccuracy": verticalAccuracy, + }; @override String toString() { - return 'Location{latitude: $latitude, longitude: $longitude}'; + return 'Location{latitude: $latitude, longitude: $longitude, time: $time, altitude: $altitude, speed: $speed, bearing: $bearing, horizontalAccuracy: $horizontalAccuracy, verticalAccuracy: $verticalAccuracy}'; } } diff --git a/lib/map_options.dart b/lib/map_options.dart index f74f9f0..0cfba0c 100644 --- a/lib/map_options.dart +++ b/lib/map_options.dart @@ -3,7 +3,22 @@ import 'package:map_view/location.dart'; import 'package:map_view/map_view_type.dart'; class MapOptions { + /// Allows the app to receive location updates. final bool showUserLocation; + /// show/hide the button to center the map on the user location. + /// + /// Requires showUserLocation to be true. + final bool showMyLocationButton; + /// show/hide the compass button on the map. + /// + /// Normally is not visible all the time. Becomes visible when the orientation + /// of the map is changes through gesture. + final bool showCompassButton; + /// show/hide the toolbar while on the map activity/view. + /// + /// Actions passed to the MapView.show(MapOptions,[]) function will not work + /// because they will not be visible. + final bool hideToolbar; final CameraPosition initialCameraPosition; final String title; static const CameraPosition _defaultCamera = @@ -12,6 +27,9 @@ class MapOptions { MapOptions( {this.showUserLocation: false, + this.showMyLocationButton: false, + this.showCompassButton: false, + this.hideToolbar = false, this.initialCameraPosition: _defaultCamera, this.title: "", this.mapViewType: MapViewType.normal}); @@ -19,15 +37,18 @@ class MapOptions { Map toMap() { return { "showUserLocation": showUserLocation, + "showMyLocationButton": showMyLocationButton, + "showCompassButton": showCompassButton, + "hideToolbar": hideToolbar, "cameraPosition": initialCameraPosition.toMap(), "title": title, - "mapViewType" : getMapTypeName(mapViewType) + "mapViewType": getMapTypeName(mapViewType) }; } String getMapTypeName(MapViewType mapType) { String mapTypeName = "normal"; - switch(mapType) { + switch (mapType) { case MapViewType.none: mapTypeName = "none"; break; diff --git a/lib/map_view.dart b/lib/map_view.dart index 8d335a3..5bf8e35 100644 --- a/lib/map_view.dart +++ b/lib/map_view.dart @@ -23,6 +23,12 @@ class MapView { MethodChannel _channel = const MethodChannel("com.apptreesoftware.map_view"); StreamController _annotationStreamController = new StreamController.broadcast(); + StreamController> _annotationDragStartController = + new StreamController.broadcast(); + StreamController> _annotationDragEndController = + new StreamController.broadcast(); + StreamController> _annotationDragController = + new StreamController.broadcast(); StreamController _polylineStreamController = new StreamController.broadcast(); StreamController _polygonStreamController = @@ -64,6 +70,7 @@ class MapView { if (toolbarActions != null) { actions = toolbarActions.map((t) => t.toMap).toList(); } + print(mapOptions.toMap()); _channel.invokeMethod( 'show', {"mapOptions": mapOptions.toMap(), "actions": actions}); } @@ -90,6 +97,7 @@ class MapView { void clearAnnotations() { _channel.invokeMethod('clearAnnotations'); + _annotations.clear(); } void addMarker(Marker marker) { @@ -117,6 +125,7 @@ class MapView { void clearPolylines() { _channel.invokeMethod('clearPolylines'); + _polylines.clear(); } void addPolyline(Polyline polyline) { @@ -144,6 +153,7 @@ class MapView { void clearPolygons() { _channel.invokeMethod('clearPolygons'); + _polygons.clear(); } void addPolygon(Polygon polygon) { @@ -181,9 +191,8 @@ class MapView { 'zoomToPolygons', {"polygons": polygonsIds, "padding": padding}); } - void setCameraPosition(double latitude, double longitude, double zoom) { - _channel.invokeMethod("setCamera", - {"latitude": latitude, "longitude": longitude, "zoom": zoom}); + void setCameraPosition(CameraPosition cameraPosition) { + _channel.invokeMethod("setCamera", cameraPosition.toMap()); } Future get centerLocation async { @@ -227,8 +236,16 @@ class MapView { Stream get onTouchAnnotation => _annotationStreamController.stream; - Stream get onTouchPolyline => - _polylineStreamController.stream; + Stream> get onAnnotationDragStart => + _annotationDragStartController.stream; + + Stream> get onAnnotationDragEnd => + _annotationDragEndController.stream; + + Stream> get onAnnotationDrag => + _annotationDragController.stream; + + Stream get onTouchPolyline => _polylineStreamController.stream; Stream get onTouchPolygon => _polygonStreamController.stream; @@ -252,7 +269,7 @@ class MapView { return new Future.value(""); case "locationUpdated": Map args = call.arguments; - _locationChangeStreamController.add(new Location.fromMap(args)); + _locationChangeStreamController.add(new Location.fromMapFull(args)); return new Future.value(""); case "annotationTapped": String id = call.arguments; @@ -261,6 +278,39 @@ class MapView { _annotationStreamController.add(annotation); } return new Future.value(""); + case "annotationDragStart": + String id = call.arguments["id"]; + var annotation = _annotations[id]; + var latitude = call.arguments["latitude"]; + var longitude = call.arguments["longitude"]; + if (annotation != null) { + Map map = new Map(); + map.putIfAbsent(annotation, () => new Location(latitude, longitude)); + _annotationDragStartController.add(map); + } + return new Future.value(""); + case "annotationDragEnd": + String id = call.arguments["id"]; + var annotation = _annotations[id]; + var latitude = call.arguments["latitude"]; + var longitude = call.arguments["longitude"]; + if (annotation != null) { + Map map = new Map(); + map.putIfAbsent(annotation, () => new Location(latitude, longitude)); + _annotationDragEndController.add(map); + } + return new Future.value(""); + case "annotationDrag": + String id = call.arguments["id"]; + var annotation = _annotations[id]; + var latitude = call.arguments["latitude"]; + var longitude = call.arguments["longitude"]; + if (annotation != null) { + Map map = new Map(); + map.putIfAbsent(annotation, () => new Location(latitude, longitude)); + _annotationDragController.add(map); + } + return new Future.value(""); case "polylineTapped": String id = call.arguments; var polyline = _polylines[id]; diff --git a/lib/marker.dart b/lib/marker.dart index a281761..ef6a3fd 100644 --- a/lib/marker.dart +++ b/lib/marker.dart @@ -5,8 +5,22 @@ class Marker { final String title; final double latitude; final double longitude; + + ///Marker Icon representation object. + /// + ///Setting this value replaces the attribute color. + ///If the image can't be set, the color will be used with the default marker. + final MarkerIcon markerIcon; + + ///The rotation of the marker in degrees clockwise from the default position. + final double rotation; + + ///Color of the default marker. final Color color; + ///Enables/disables the marker drag functionality. + final bool draggable; + static const Color _defaultColor = const Color(-769226); Marker( @@ -14,7 +28,10 @@ class Marker { this.title, this.latitude, this.longitude, { + this.rotation: 0.0, + this.markerIcon, this.color: _defaultColor, + this.draggable: false, }); @override @@ -26,12 +43,14 @@ class Marker { int get hashCode => id.hashCode; Map toMap() { - return { + Map map = { "id": id, "title": title, "latitude": latitude, "longitude": longitude, + "rotation": rotation, "type": "pin", + "draggable": draggable, "color": { "r": color.red, "g": color.green, @@ -39,5 +58,55 @@ class Marker { "a": color.alpha } }; + if (markerIcon != null) + map.putIfAbsent("markerIcon", () => markerIcon.toMap()); + return map; + } +} + +class MarkerIcon { + ///Asset image to be set in the marker. + String asset; + + ///Width of the image icon. + /// + ///Should not be 0.0, otherwise the image original width will be used. + /// + ///Sizes behave differently in each platform, so change this value to match + ///the desired look in each platform. + double width; + + ///Height of the image icon. + /// + ///Should not be 0.0, otherwise the image original height will be used. + /// + ///Sizes behave differently in each platform, so change this value to match + ///the desired look in each platform. + double height; + + MarkerIcon( + this.asset, { + this.width = 0.0, + this.height = 0.0, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is MarkerIcon && + runtimeType == other.runtimeType && + asset == other.asset && + width == other.width && + height == other.height; + + @override + int get hashCode => asset.hashCode ^ width.hashCode ^ height.hashCode; + + Map toMap() { + return { + "asset": asset, + "width": width, + "height": height, + }; } }