diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..03c3fb3 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.19.6" +} \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 66cfe30..59204ee 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,27 +6,36 @@ on: pull_request: branches: [ master ] +env: + JAVA_VERSION: "17.x" + JAVA_DISTRIBUTION: "zulu" + jobs: build: runs-on: macos-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: '12.x' - - name: Install Flutter - uses: subosito/flutter-action@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: - flutter-version: '3.19.1' + java-version: ${{ env.JAVA_VERSION }} + distribution: ${{ env.JAVA_DISTRIBUTION }} + - uses: dart-lang/setup-dart@v1 + - name: Install fvm + run: | + dart pub global activate fvm 3.1.4 + fvm install + - name: Install melos + run: dart pub global activate melos 6.0.0 - name: Install dependencies - run: flutter pub get + run: melos bs - name: Run dart analyze - run: dart analyze + run: melos analyze - name: build iOS example project - working-directory: ./example - run: flutter build ios --release --no-codesign + working-directory: ./packages/platform_maps_flutter/example + run: fvm flutter build ios --release --no-codesign - name: build android example project - working-directory: ./example - run: flutter build apk - - name: publish --dry-run - run: flutter pub publish --dry-run + working-directory: ./packages/platform_maps_flutter/example + run: fvm flutter build apk +# disabled for now, because will fail with multi-package setup +# - name: publish --dry-run +# run: flutter pub publish --dry-run diff --git a/.gitignore b/.gitignore index 4655c8a..1dbd46e 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,8 @@ pubspec.lock !**/ios/**/default.mode2v3 !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 + +# FVM Version Cache +.fvm/ +# Melos pubspec overrides +pubspec_overrides.yaml diff --git a/LICENSE b/LICENSE index 51d9daf..67f5463 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ BSD 2-Clause License +Copyright (c) 2024, Albert Heijn Technology Copyright (c) 2019, Luis Thein All rights reserved. diff --git a/README.md b/README.md index 63c8eae..e872fcc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This package combines the `google_maps_flutter` plugin with `apple_maps_flutter` # iOS -To use this plugin on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's Info.plist file, with the key `io.flutter.embedded_views_preview` and the value `YES`. You will also have to add the key `Privacy - Location When In Use Usage Description` with the value of your usage description. +You will have to add the key `Privacy - Location When In Use Usage Description` with the value of your usage description. # Android diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index 448d91f..0000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,2 +0,0 @@ -include: package:flutter_lints/flutter.yaml - diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index 448d91f..0000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -1,2 +0,0 @@ -include: package:flutter_lints/flutter.yaml - diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock deleted file mode 100644 index 2576716..0000000 --- a/example/ios/Podfile.lock +++ /dev/null @@ -1,39 +0,0 @@ -PODS: - - apple_maps_flutter (0.0.1): - - Flutter - - Flutter (1.0.0) - - google_maps_flutter_ios (0.0.1): - - Flutter - - GoogleMaps (< 9.0) - - GoogleMaps (6.2.1): - - GoogleMaps/Maps (= 6.2.1) - - GoogleMaps/Base (6.2.1) - - GoogleMaps/Maps (6.2.1): - - GoogleMaps/Base - -DEPENDENCIES: - - apple_maps_flutter (from `.symlinks/plugins/apple_maps_flutter/ios`) - - Flutter (from `Flutter`) - - google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`) - -SPEC REPOS: - trunk: - - GoogleMaps - -EXTERNAL SOURCES: - apple_maps_flutter: - :path: ".symlinks/plugins/apple_maps_flutter/ios" - Flutter: - :path: Flutter - google_maps_flutter_ios: - :path: ".symlinks/plugins/google_maps_flutter_ios/ios" - -SPEC CHECKSUMS: - apple_maps_flutter: c59725efea39e13e703cde52a1d2b14866ad68a8 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - google_maps_flutter_ios: f135b968a67c05679e0a53538e900b5c174b0d99 - GoogleMaps: 20d7b12be49a14287f797e88e0e31bc4156aaeb4 - -PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 - -COCOAPODS: 1.15.0 diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 25ac057..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async {}); -} diff --git a/lib/platform_maps_flutter.dart b/lib/platform_maps_flutter.dart deleted file mode 100644 index e16f71a..0000000 --- a/lib/platform_maps_flutter.dart +++ /dev/null @@ -1,25 +0,0 @@ -library platform_maps_flutter; - -import 'dart:io'; - -import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart' as google_maps; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; - -part 'src/bitmap.dart'; -part 'src/camera.dart'; -part 'src/cap.dart'; -part 'src/circle.dart'; -part 'src/controller.dart'; -part 'src/joint_type.dart'; -part 'src/location.dart'; -part 'src/marker.dart'; -part 'src/pattern_item.dart'; -part 'src/platform_maps.dart'; -part 'src/polygon.dart'; -part 'src/polyline.dart'; -part 'src/ui.dart'; diff --git a/lib/src/bitmap.dart b/lib/src/bitmap.dart deleted file mode 100644 index dba2d36..0000000 --- a/lib/src/bitmap.dart +++ /dev/null @@ -1,61 +0,0 @@ -part of '../platform_maps_flutter.dart'; - -/// 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 { - final dynamic bitmapDescriptor; - - BitmapDescriptor._(this.bitmapDescriptor); - - /// Creates a BitmapDescriptor that refers to the default marker image. - static BitmapDescriptor? get defaultMarker { - if (Platform.isIOS) { - return BitmapDescriptor._(apple_maps.BitmapDescriptor.defaultAnnotation); - } else if (Platform.isAndroid) { - return BitmapDescriptor._(google_maps.BitmapDescriptor.defaultMarker); - } - return null; - } - - /// Creates a [BitmapDescriptor] from an asset image. - /// Asset images in flutter are stored per: https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets - /// - /// This method takes into consideration various asset resolutions and scales the images to the right resolution depending on the dpi. - /// - /// Don't forget to rebuild the map with the new Icons if it was already build. - static Future fromAssetImage( - ImageConfiguration configuration, - String assetName, { - AssetBundle? bundle, - String? package, - }) async { - dynamic bitmap; - if (Platform.isIOS) { - bitmap = await apple_maps.BitmapDescriptor.fromAssetImage( - configuration, - assetName, - bundle: bundle, - package: package, - ); - } else if (Platform.isAndroid) { - bitmap = await google_maps.BitmapDescriptor.fromAssetImage( - configuration, - assetName, - bundle: bundle, - package: package, - ); - } - return BitmapDescriptor._(bitmap); - } - - /// Creates a BitmapDescriptor using an array of bytes that must be encoded - /// as PNG. - static BitmapDescriptor fromBytes(Uint8List byteData) { - var bitmap = Platform.isAndroid - ? google_maps.BitmapDescriptor.fromBytes(byteData) - : apple_maps.BitmapDescriptor.fromBytes(byteData); - return BitmapDescriptor._(bitmap); - } -} diff --git a/lib/src/camera.dart b/lib/src/camera.dart deleted file mode 100644 index aea0754..0000000 --- a/lib/src/camera.dart +++ /dev/null @@ -1,168 +0,0 @@ -part of '../platform_maps_flutter.dart'; - -/// The position of the map "camera", the view point from which the world is -/// shown in the map view. Aggregates the camera's [target] geographical -/// location, its [zoom] level, [pitch] angle, and [heading]. -class CameraPosition { - const CameraPosition({ - required this.target, - this.bearing = 0.0, - this.tilt = 0.0, - this.zoom = 0, - }); - - /// The camera's bearing in degrees, measured clockwise from north. - /// - /// A bearing of 0.0, the default, means the camera points north. - /// A bearing of 90.0 means the camera points east. - final double bearing; - - /// The geographical location that the camera is pointing at. - final LatLng target; - - // In degrees where 0 is looking straight down. Pitch may be clamped to an appropriate value. - final double tilt; - - /// The zoom level of the camera. - /// - /// A zoom of 0.0, the default, means the screen width of the world is 256. - /// Adding 1.0 to the zoom level doubles the screen width of the map. So at - /// zoom level 3.0, the screen width of the world is 2³x256=2048. - /// - /// Larger zoom levels thus means the camera is placed closer to the surface - /// of the Earth, revealing more detail in a narrower geographical region. - /// - /// The supported zoom level range depends on the map data and device. Values - /// beyond the supported range are allowed, but on applying them to a map they - /// will be silently clamped to the supported range. - final double zoom; - - apple_maps.CameraPosition get appleMapsCameraPosition { - return apple_maps.CameraPosition( - target: target.appleLatLng, - heading: bearing, - pitch: tilt, - zoom: zoom, - ); - } - - google_maps.CameraPosition get googleMapsCameraPosition { - return google_maps.CameraPosition( - target: target.googleLatLng, - bearing: bearing, - tilt: tilt, - zoom: zoom, - ); - } - - static CameraPosition fromAppleMapCameraPosition(apple_maps.CameraPosition cameraPosition) { - return CameraPosition( - target: LatLng._fromAppleLatLng(cameraPosition.target), - bearing: cameraPosition.heading, - tilt: cameraPosition.pitch, - zoom: cameraPosition.zoom, - ); - } - - static CameraPosition fromGoogleMapCameraPosition(google_maps.CameraPosition cameraPosition) { - return CameraPosition( - target: LatLng._fromGoogleLatLng(cameraPosition.target), - bearing: cameraPosition.bearing, - tilt: cameraPosition.tilt, - zoom: cameraPosition.zoom, - ); - } -} - -class CameraUpdate { - CameraUpdate._(this._json); - - /// Returns a camera update that moves the camera to the specified position. - static newCameraPosition(CameraPosition cameraPosition) { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.newCameraPosition(cameraPosition.appleMapsCameraPosition); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.newCameraPosition(cameraPosition.googleMapsCameraPosition); - } - } - - /// Returns a camera update that moves the camera target to the specified geographical location. - static newLatLng(LatLng latLng) { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.newLatLng(latLng.appleLatLng); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.newLatLng(latLng.googleLatLng); - } - } - - /// Returns a camera update that moves the camera target to the specified geographical location and zoom level. - static newLatLngZoom(LatLng latLng, double zoom) { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.newLatLngZoom(latLng.appleLatLng, zoom); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.newLatLngZoom(latLng.googleLatLng, zoom); - } - } - - /// Returns a camera update that transforms the camera so that - /// the specified geographical bounding box is centered in the map - /// view at the greatest possible zoom level. - /// A non-zero [padding] insets the bounding box from the map view's edges. - /// The camera's new tilt and bearing will both be 0.0. - static newLatLngBounds(LatLngBounds bounds, double padding) { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.newLatLngBounds(bounds.appleLatLngBounds, padding); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.newLatLngBounds(bounds.googleLatLngBounds, padding); - } - } - - /// Returns a camera update that modifies the camera zoom level by the specified amount. - /// The optional [focus] is a screen point whose underlying geographical location - /// should be invariant, if possible, by the movement. - static zoomBy(double amount) { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.zoomBy(amount); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.zoomBy(amount); - } - } - - /// Returns a camera update that zooms the camera in, - /// bringing the camera closer to the surface of the Earth. - /// - /// Equivalent to the result of calling zoomBy(1.0). - static zoomIn() { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.zoomIn(); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.zoomIn(); - } - } - - /// Returns a camera update that zooms the camera out, - /// bringing the camera further away from the surface of the Earth. - /// - /// Equivalent to the result of calling zoomBy(-1.0). - static zoomOut() { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.zoomOut(); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.zoomOut(); - } - } - - /// Returns a camera update that sets the camera zoom level. - static zoomTo(double zoom) { - if (Platform.isIOS) { - return apple_maps.CameraUpdate.zoomTo(zoom); - } else if (Platform.isAndroid) { - return google_maps.CameraUpdate.zoomTo(zoom); - } - } - - final dynamic _json; - - /// Converts this object to something serializable in JSON. - dynamic toJson() => _json; -} diff --git a/lib/src/controller.dart b/lib/src/controller.dart deleted file mode 100644 index cb43fa6..0000000 --- a/lib/src/controller.dart +++ /dev/null @@ -1,113 +0,0 @@ -part of '../platform_maps_flutter.dart'; - -class PlatformMapController { - apple_maps.AppleMapController? appleController; - google_maps.GoogleMapController? googleController; - - PlatformMapController(dynamic controller) { - if (controller.runtimeType == google_maps.GoogleMapController) { - googleController = controller; - } else if (controller.runtimeType == apple_maps.AppleMapController) { - appleController = controller; - } - } - - /// Programmatically show the Info Window for a [Marker]. - /// - /// The `markerId` must match one of the markers on the map. - /// An invalid `markerId` triggers an "Invalid markerId" error. - /// - /// * See also: - /// * [hideMarkerInfoWindow] to hide the Info Window. - /// * [isMarkerInfoWindowShown] to check if the Info Window is showing. - Future showMarkerInfoWindow(MarkerId markerId) { - if (Platform.isAndroid) { - return googleController!.showMarkerInfoWindow(markerId.googleMapsMarkerId); - } else if (Platform.isIOS) { - return appleController!.showMarkerInfoWindow(markerId.appleMapsAnnoationId); - } - throw ('Platform not supported.'); - } - - /// Programmatically hide the Info Window for a [Marker]. - /// - /// The `markerId` must match one of the markers on the map. - /// An invalid `markerId` triggers an "Invalid markerId" error. - /// - /// * See also: - /// * [showMarkerInfoWindow] to show the Info Window. - /// * [isMarkerInfoWindowShown] to check if the Info Window is showing. - Future hideMarkerInfoWindow(MarkerId markerId) { - if (Platform.isAndroid) { - return googleController!.hideMarkerInfoWindow(markerId.googleMapsMarkerId); - } else if (Platform.isIOS) { - return appleController!.hideMarkerInfoWindow(markerId.appleMapsAnnoationId); - } - throw ('Platform not supported.'); - } - - /// Returns `true` when the [InfoWindow] is showing, `false` otherwise. - /// - /// The `markerId` must match one of the markers on the map. - /// An invalid `markerId` triggers an "Invalid markerId" error. - /// - /// * See also: - /// * [showMarkerInfoWindow] to show the Info Window. - /// * [hideMarkerInfoWindow] to hide the Info Window. - Future isMarkerInfoWindowShown(MarkerId markerId) async { - if (Platform.isAndroid) { - return googleController!.isMarkerInfoWindowShown(markerId.googleMapsMarkerId); - } else if (Platform.isIOS) { - return await appleController!.isMarkerInfoWindowShown(markerId.appleMapsAnnoationId) ?? false; - } - throw ('Platform not supported.'); - } - - /// Starts an animated change of the map camera position. - /// - /// The returned [Future] completes after the change has been started on the - /// platform side. - Future animateCamera(cameraUpdate) async { - if (Platform.isIOS) { - return appleController!.animateCamera(cameraUpdate); - } else if (Platform.isAndroid) { - return googleController!.animateCamera(cameraUpdate); - } - throw ('Platform not supported.'); - } - - /// Changes the map camera position. - /// - /// The returned [Future] completes after the change has been made on the - /// platform side. - Future moveCamera(cameraUpdate) async { - if (Platform.isIOS) { - return appleController!.moveCamera(cameraUpdate); - } else if (Platform.isAndroid) { - return googleController!.moveCamera(cameraUpdate); - } - } - - /// Return [LatLngBounds] defining the region that is visible in a map. - Future getVisibleRegion() async { - late LatLngBounds bounds; - if (Platform.isIOS) { - apple_maps.LatLngBounds appleBounds = await appleController!.getVisibleRegion(); - bounds = LatLngBounds._fromAppleLatLngBounds(appleBounds); - } else if (Platform.isAndroid) { - google_maps.LatLngBounds googleBounds = await googleController!.getVisibleRegion(); - bounds = LatLngBounds._fromGoogleLatLngBounds(googleBounds); - } - return bounds; - } - - /// Returns the image bytes of the map - Future takeSnapshot() async { - if (Platform.isIOS) { - return appleController!.takeSnapshot(); - } else if (Platform.isAndroid) { - return googleController!.takeSnapshot(); - } - throw UnsupportedError('platform_maps only supports iOS and Android'); - } -} diff --git a/lib/src/location.dart b/lib/src/location.dart deleted file mode 100644 index 2ea7d7b..0000000 --- a/lib/src/location.dart +++ /dev/null @@ -1,107 +0,0 @@ -part of '../platform_maps_flutter.dart'; - -/// A pair of latitude and longitude coordinates, stored as degrees. -class LatLng { - /// Creates a geographical location specified in degrees [latitude] and - /// [longitude]. - /// - /// The latitude is clamped to the inclusive interval from -90.0 to +90.0. - /// - /// The longitude is normalized to the half-open interval from -180.0 - /// (inclusive) to +180.0 (exclusive) - const LatLng(double latitude, double longitude) - : latitude = (latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude)), - longitude = (longitude + 180.0) % 360.0 - 180.0; - - /// The latitude in degrees between -90.0 and 90.0, both inclusive. - final double latitude; - - /// The longitude in degrees between -180.0 (inclusive) and 180.0 (exclusive). - final double longitude; - - @override - String toString() => '$runtimeType($latitude, $longitude)'; - - static LatLng _fromAppleLatLng(apple_maps.LatLng latLng) => - LatLng(latLng.latitude, latLng.longitude); - - static LatLng _fromGoogleLatLng(google_maps.LatLng latLng) => - LatLng(latLng.latitude, latLng.longitude); - - apple_maps.LatLng get appleLatLng => apple_maps.LatLng( - latitude, - longitude, - ); - - google_maps.LatLng get googleLatLng => google_maps.LatLng( - latitude, - longitude, - ); - - static List googleMapsLatLngsFromList(List latlngs) { - List googleMapsLatLngs = []; - for (var latlng in latlngs) { - googleMapsLatLngs.add(latlng.googleLatLng); - } - return googleMapsLatLngs; - } - - static List appleMapsLatLngsFromList(List latlngs) { - List appleMapsLatLngs = []; - for (var latlng in latlngs) { - appleMapsLatLngs.add(latlng.appleLatLng); - } - return appleMapsLatLngs; - } -} - -class LatLngBounds { - /// Creates geographical bounding box with the specified corners. - /// - /// The latitude of the southwest corner cannot be larger than the - /// latitude of the northeast corner. - LatLngBounds({required this.southwest, required this.northeast}) - : assert(southwest.latitude <= northeast.latitude); - - static LatLngBounds _fromAppleLatLngBounds(apple_maps.LatLngBounds bounds) => LatLngBounds( - southwest: LatLng._fromAppleLatLng(bounds.southwest), - northeast: LatLng._fromAppleLatLng(bounds.northeast), - ); - - static LatLngBounds _fromGoogleLatLngBounds(google_maps.LatLngBounds bounds) => LatLngBounds( - southwest: LatLng._fromGoogleLatLng(bounds.southwest), - northeast: LatLng._fromGoogleLatLng(bounds.northeast), - ); - - /// The southwest corner of the rectangle. - final LatLng southwest; - - /// The northeast corner of the rectangle. - final LatLng northeast; - - apple_maps.LatLngBounds get appleLatLngBounds => apple_maps.LatLngBounds( - southwest: southwest.appleLatLng, - northeast: northeast.appleLatLng, - ); - - google_maps.LatLngBounds get googleLatLngBounds => google_maps.LatLngBounds( - southwest: southwest.googleLatLng, - northeast: northeast.googleLatLng, - ); - - bool contains(LatLng point) { - return _containsLatitude(point.latitude) && _containsLongitude(point.longitude); - } - - bool _containsLatitude(double lat) { - return (southwest.latitude <= lat) && (lat <= northeast.latitude); - } - - bool _containsLongitude(double lng) { - if (southwest.longitude <= northeast.longitude) { - return southwest.longitude <= lng && lng <= northeast.longitude; - } else { - return southwest.longitude <= lng || lng <= northeast.longitude; - } - } -} diff --git a/lib/src/marker.dart b/lib/src/marker.dart deleted file mode 100644 index 1fcda3f..0000000 --- a/lib/src/marker.dart +++ /dev/null @@ -1,266 +0,0 @@ -part of '../platform_maps_flutter.dart'; - -/// Text labels for a [Marker] info window. -class InfoWindow { - const InfoWindow({this.title, this.snippet, this.onTap, this.anchor}); - - /// Text labels specifying that no text is to be displayed. - static const InfoWindow noText = InfoWindow(); - - /// Text displayed in an info window when the user taps the marker. - /// - /// A null value means no title. - final String? title; - - /// Additional text displayed below the [title]. - /// - /// A null value means no additional text. - final String? snippet; - - /// The icon image point that will be the anchor of the info window when - /// displayed. - /// - /// The image point is specified in normalized coordinates: An anchor of - /// (0.0, 0.0) means the top left corner of the image. An anchor - /// of (1.0, 1.0) means the bottom right corner of the image. - final Offset? anchor; - - /// onTap callback for this [InfoWindow]. - final VoidCallback? onTap; - - apple_maps.InfoWindow get appleMapsInfoWindow => apple_maps.InfoWindow( - anchor: anchor ?? const Offset(0, 0), - onTap: onTap, - snippet: snippet, - title: title, - ); - - google_maps.InfoWindow get googleMapsInfoWindow => google_maps.InfoWindow( - anchor: anchor ?? const Offset(0, 0), - onTap: onTap, - snippet: snippet, - title: title, - ); - - /// Creates a new [InfoWindow] object whose values are the same as this instance, - /// unless overwritten by the specified parameters. - InfoWindow copyWith({ - String? titleParam, - String? snippetParam, - Offset? anchorParam, - VoidCallback? onTapParam, - }) { - return InfoWindow( - title: titleParam ?? title, - snippet: snippetParam ?? snippet, - anchor: anchorParam ?? anchor, - onTap: onTapParam ?? onTap, - ); - } -} - -/// Uniquely identifies a [Marker] among [PlatformMaps] markers. -/// -/// This does not have to be globally unique, only unique among the list. -@immutable -class MarkerId { - const MarkerId(this.value); - - /// value of the [MarkerId]. - final String value; - - apple_maps.AnnotationId get appleMapsAnnoationId => apple_maps.AnnotationId( - value, - ); - - google_maps.MarkerId get googleMapsMarkerId => google_maps.MarkerId( - value, - ); -} - -/// Marks a geographical location on the map. -/// -/// A marker icon is drawn oriented against the device's screen rather than -/// the map's surface; that is, it will not necessarily change orientation -/// due to map rotations, tilting, or zooming. -@immutable -class Marker { - /// Creates a set of marker configuration options. - /// - /// Default marker options. - /// - /// Specifies a marker that - /// * is fully opaque; [alpha] is 1.0 - /// * has default tap handling; [consumeTapEvents] is false - /// * is stationary; [draggable] is false - /// * has a default icon; [icon] is `BitmapDescriptor.defaultMarker` - /// * has no info window text; [infoWindowText] is `InfoWindowText.noText` - /// * is positioned at 0, 0; [position] is `LatLng(0.0, 0.0)` - /// * is visible; [visible] is true - /// * is placed at the base of the drawing order; [zIndex] is 0.0 - const Marker({ - required this.markerId, - this.alpha = 1.0, - this.anchor = const Offset(0.5, 1.0), - this.consumeTapEvents = false, - this.draggable = false, - this.icon, - this.infoWindow = InfoWindow.noText, - this.position = const LatLng(0.0, 0.0), - this.onTap, - this.visible = true, - this.onDragEnd, - }) : assert((0.0 <= alpha && alpha <= 1.0)); - - /// Uniquely identifies a [Marker]. - final MarkerId markerId; - - /// The opacity of the marker, between 0.0 and 1.0 inclusive. - /// - /// 0.0 means fully transparent, 1.0 means fully opaque. - final double alpha; - - /// The icon image point that will be placed at the [position] of the marker. - /// - /// The image point is specified in normalized coordinates: An anchor of - /// (0.0, 0.0) means the top left corner of the image. An anchor - /// of (1.0, 1.0) means the bottom right corner of the image. - final Offset anchor; - - /// True if the marker icon consumes tap events. If not, the map will perform - /// default tap handling by centering the map on the marker and displaying its - /// info window. - final bool consumeTapEvents; - - /// True if the marker is draggable by user touch events. - final bool draggable; - - /// A description of the bitmap used to draw the marker icon. - final BitmapDescriptor? icon; - - /// A Google Maps InfoWindow. - /// - /// The window is displayed when the marker is tapped. - final InfoWindow infoWindow; - - /// Geographical location of the marker. - final LatLng position; - - /// Callbacks to receive tap events for markers placed on this map. - final VoidCallback? onTap; - - /// True if the annotation is visible. - final bool visible; - - final ValueChanged? onDragEnd; - - apple_maps.Annotation get appleMapsAnnotation => apple_maps.Annotation( - annotationId: markerId.appleMapsAnnoationId, - alpha: alpha, - anchor: anchor, - draggable: draggable, - infoWindow: infoWindow.appleMapsInfoWindow, - onTap: onTap, - icon: icon?.bitmapDescriptor ?? BitmapDescriptor.defaultMarker?.bitmapDescriptor, - visible: visible, - onDragEnd: onDragEnd != null - ? (apple_maps.LatLng latLng) => _onAppleAnnotationDragEnd(latLng, onDragEnd) - : null, - position: position.appleLatLng, - ); - - google_maps.Marker get googleMapsMarker => google_maps.Marker( - markerId: markerId.googleMapsMarkerId, - alpha: alpha, - anchor: anchor, - draggable: draggable, - infoWindow: infoWindow.googleMapsInfoWindow, - onTap: onTap, - icon: icon?.bitmapDescriptor ?? BitmapDescriptor.defaultMarker?.bitmapDescriptor, - visible: visible, - onDragEnd: onDragEnd != null - ? (google_maps.LatLng latLng) => _onGoogleMarkerDragEnd(latLng, onDragEnd) - : null, - position: position.googleLatLng, - ); - - static apple_maps.Annotation appleMapsAnnotationFromMarker(Marker marker) => - apple_maps.Annotation( - annotationId: marker.markerId.appleMapsAnnoationId, - alpha: marker.alpha, - anchor: marker.anchor, - draggable: marker.draggable, - infoWindow: marker.infoWindow.appleMapsInfoWindow, - onTap: marker.onTap, - icon: marker.icon?.bitmapDescriptor ?? BitmapDescriptor.defaultMarker?.bitmapDescriptor, - visible: marker.visible, - onDragEnd: marker.onDragEnd != null - ? (apple_maps.LatLng latLng) => _onAppleAnnotationDragEnd(latLng, marker.onDragEnd) - : null, - position: marker.position.appleLatLng, - ); - - static google_maps.Marker googleMapsMarkerFromMarker(Marker marker) => google_maps.Marker( - markerId: marker.markerId.googleMapsMarkerId, - alpha: marker.alpha, - anchor: marker.anchor, - draggable: marker.draggable, - infoWindow: marker.infoWindow.googleMapsInfoWindow, - onTap: marker.onTap, - icon: marker.icon?.bitmapDescriptor ?? BitmapDescriptor.defaultMarker?.bitmapDescriptor, - visible: marker.visible, - onDragEnd: marker.onDragEnd != null - ? (google_maps.LatLng latLng) => _onGoogleMarkerDragEnd(latLng, marker.onDragEnd) - : null, - position: marker.position.googleLatLng, - ); - - static Set toAppleMapsAnnotationSet(Set markers) { - List annotations = []; - for (Marker marker in markers) { - annotations.add(appleMapsAnnotationFromMarker(marker)); - } - return Set.from(annotations); - } - - static Set toGoogleMapsMarkerSet(Set markers) { - List markers0 = []; - for (Marker marker in markers) { - markers0.add(googleMapsMarkerFromMarker(marker)); - } - return Set.from(markers0); - } - - Marker copyWith({ - double? alphaParam, - Offset? anchorParam, - bool? consumeTapEventsParam, - bool? draggableParam, - BitmapDescriptor? iconParam, - InfoWindow? infoWindowParam, - LatLng? positionParam, - bool? visibleParam, - VoidCallback? onTapParam, - }) { - return Marker( - markerId: markerId, - alpha: alphaParam ?? alpha, - anchor: anchorParam ?? anchor, - consumeTapEvents: consumeTapEventsParam ?? consumeTapEvents, - draggable: draggableParam ?? draggable, - icon: iconParam ?? icon, - infoWindow: infoWindowParam ?? infoWindow, - position: positionParam ?? position, - visible: visibleParam ?? visible, - onTap: onTapParam ?? onTap, - ); - } - - static _onGoogleMarkerDragEnd(google_maps.LatLng latLng, Function? onDragEnd) { - onDragEnd?.call(LatLng._fromGoogleLatLng(latLng)); - } - - static _onAppleAnnotationDragEnd(apple_maps.LatLng latLng, Function? onDragEnd) { - onDragEnd?.call(LatLng._fromAppleLatLng(latLng)); - } -} diff --git a/lib/src/pattern_item.dart b/lib/src/pattern_item.dart deleted file mode 100644 index 376d807..0000000 --- a/lib/src/pattern_item.dart +++ /dev/null @@ -1,66 +0,0 @@ -part of '../platform_maps_flutter.dart'; - -/// Item used in the stroke pattern for a Polyline. -@immutable -class PatternItem { - const PatternItem._(this._json); - - static const PatternItem dot = PatternItem._(['dot']); - - /// A dash used in the stroke pattern for a [Polyline]. - /// - /// [length] has to be non-negative. - static PatternItem dash(double length) { - assert(length >= 0.0); - return PatternItem._(['dash', length]); - } - - /// A gap used in the stroke pattern for a [Polyline]. - /// - /// [length] has to be non-negative. - static PatternItem gap(double length) { - assert(length >= 0.0); - return PatternItem._(['gap', length]); - } - - static google_maps.PatternItem _googleMapsPatternItem(PatternItem patternItem) { - if (patternItem._json[0] == 'dash') { - return google_maps.PatternItem.dash(patternItem._json[1]); - } else if (patternItem._json[0] == 'gap') { - return google_maps.PatternItem.gap(patternItem._json[1]); - } - return google_maps.PatternItem.dot; - } - - static apple_maps.PatternItem _appleMapsPatternItem(PatternItem patternItem) { - if (patternItem._json[0] == 'dash') { - return apple_maps.PatternItem.dash(patternItem._json[1]); - } else if (patternItem._json[0] == 'gap') { - return apple_maps.PatternItem.gap(patternItem._json[1]); - } - return apple_maps.PatternItem.dot; - } - - final dynamic _json; - - static List getGoogleMapsPatternItemList( - List patternItems) { - List googleMapsPatternItems = []; - for (var patternItem in patternItems) { - googleMapsPatternItems.add( - _googleMapsPatternItem(patternItem), - ); - } - return googleMapsPatternItems; - } - - static List getAppleMapsPatternItemList(List patternItems) { - List appleMapsPatternItems = []; - for (var patternItem in patternItems) { - appleMapsPatternItems.add( - _appleMapsPatternItem(patternItem), - ); - } - return appleMapsPatternItems; - } -} diff --git a/lib/src/ui.dart b/lib/src/ui.dart deleted file mode 100644 index 6d1419c..0000000 --- a/lib/src/ui.dart +++ /dev/null @@ -1,42 +0,0 @@ -// 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 '../platform_maps_flutter.dart'; - -/// Type of map tiles to display. -enum MapType { - /// Normal tiles (traffic and labels, subtle terrain information). - normal, - - /// Satellite imaging tiles (aerial photos) - satellite, - - /// Hybrid tiles (satellite images with some labels/overlays) - hybrid, -} - -// Used with [PlatformMapOptions] to wrap min and max zoom. This allows -// distinguishing between specifying unbounded zooming (null `minZoom` and -// `maxZoom`) from not specifying anything (null `MinMaxZoomPreference`). -class MinMaxZoomPreference { - const MinMaxZoomPreference(this.minZoom, this.maxZoom) - : assert(minZoom == null || maxZoom == null || minZoom <= maxZoom); - - static const unbounded = MinMaxZoomPreference(null, null); - - /// The preferred minimum zoom level or null, if unbounded from below. - final double? minZoom; - - /// The preferred maximum zoom level or null, if unbounded from above. - final double? maxZoom; - - apple_maps.MinMaxZoomPreference get appleMapsZoomPreference => - apple_maps.MinMaxZoomPreference(maxZoom, maxZoom); - - google_maps.MinMaxZoomPreference get googleMapsZoomPreference => - google_maps.MinMaxZoomPreference(maxZoom, maxZoom); - - /// Converts this object to something serializable in JSON. - dynamic toJson() => [minZoom, maxZoom]; -} diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 0000000..5b1e3c1 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,11 @@ +name: platform_maps_flutter + +sdkPath: .fvm/flutter_sdk + +packages: + - packages/** + +scripts: + dart_fix: > + melos exec -c 1 --fail-fast --\ + dart fix --apply \ No newline at end of file diff --git a/CHANGELOG.md b/packages/platform_maps_flutter/CHANGELOG.md similarity index 96% rename from CHANGELOG.md rename to packages/platform_maps_flutter/CHANGELOG.md index 031d4f8..f8f53b2 100644 --- a/CHANGELOG.md +++ b/packages/platform_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.0-beta + +* Migrate to a federated plugin structure + ## 1.0.2 * Updates apple_maps_flutter to apply memory leak fix diff --git a/packages/platform_maps_flutter/LICENSE b/packages/platform_maps_flutter/LICENSE new file mode 100644 index 0000000..67f5463 --- /dev/null +++ b/packages/platform_maps_flutter/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright (c) 2024, Albert Heijn Technology +Copyright (c) 2019, Luis Thein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/platform_maps_flutter/README.md b/packages/platform_maps_flutter/README.md new file mode 100644 index 0000000..63c8eae --- /dev/null +++ b/packages/platform_maps_flutter/README.md @@ -0,0 +1,94 @@ +# platform_maps_flutter + +A Flutter package that provides a native map to both Android and iOS devices. + +The plugin relies on Flutter's mechanism for embedding Android and iOS views. As that mechanism is currently in a developers preview, this plugin should also be considered a developers preview. + +This package combines the `google_maps_flutter` plugin with `apple_maps_flutter` to create a cross platform implementation of native maps for Android/iOS. + +# Screenshots + +| Android | iOS | +| :-----------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------: | +| ![Example 1](https://luisthein.de/flutter-platform-maps-images/android_screenshot1-min.png) | ![Example 2](https://luisthein.de/flutter-platform-maps-images/ios_screenshot1-min.png) | +| ![Example 1](https://luisthein.de/flutter-platform-maps-images/android_screenshot2-min.png) | ![Example 2](https://luisthein.de/flutter-platform-maps-images/ios_screenshot2-min.png) | + +# Current functionality + +- Camera movement including bearing, heading, tilt (also animated) +- Markers, including custom marker images and Info windows +- Different map types +- Map manipulation, enable/disable gestures, show current location, show compass ... + + +# iOS + +To use this plugin on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's Info.plist file, with the key `io.flutter.embedded_views_preview` and the value `YES`. You will also have to add the key `Privacy - Location When In Use Usage Description` with the value of your usage description. + +# Android + +Specify your API key in the application manifest android/app/src/main/AndroidManifest.xml: + +```xml + +``` + +## Sample Usage + +```dart +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: PlatformMap( + initialCameraPosition: CameraPosition( + target: const LatLng(47.6, 8.8796), + zoom: 16.0, + ), + markers: Set.of( + [ + Marker( + markerId: MarkerId('marker_1'), + position: LatLng(47.6, 8.8796), + consumeTapEvents: true, + infoWindow: InfoWindow( + title: 'PlatformMarker', + snippet: "Hi I'm a Platform Marker", + ), + onTap: () { + print("Marker tapped"); + }, + ), + ], + ), + myLocationEnabled: true, + myLocationButtonEnabled: true, + onTap: (location) => print('onTap: $location'), + onCameraMove: (cameraUpdate) => print('onCameraMove: $cameraUpdate'), + compassEnabled: true, + onMapCreated: (controller) { + Future.delayed(Duration(seconds: 2)).then( + (_) { + controller.animateCamera( + CameraUpdate.newCameraPosition( + const CameraPosition( + bearing: 270.0, + target: LatLng(51.5160895, -0.1294527), + tilt: 30.0, + zoom: 18, + ), + ), + ); + }, + ); + }, + ), + ); + } +} +``` + +Suggestions and PR's to make this plugin better are always welcome. Please notice that the features provided by this package depend on the apple_maps_flutter plugin, which will improve in the future. diff --git a/packages/platform_maps_flutter/analysis_options.yaml b/packages/platform_maps_flutter/analysis_options.yaml new file mode 100644 index 0000000..90992ec --- /dev/null +++ b/packages/platform_maps_flutter/analysis_options.yaml @@ -0,0 +1,8 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - always_declare_return_types + - avoid_annotating_with_dynamic + - avoid_dynamic_calls + - require_trailing_commas \ No newline at end of file diff --git a/example/.gitignore b/packages/platform_maps_flutter/example/.gitignore similarity index 100% rename from example/.gitignore rename to packages/platform_maps_flutter/example/.gitignore diff --git a/example/.metadata b/packages/platform_maps_flutter/example/.metadata similarity index 100% rename from example/.metadata rename to packages/platform_maps_flutter/example/.metadata diff --git a/example/README.md b/packages/platform_maps_flutter/example/README.md similarity index 100% rename from example/README.md rename to packages/platform_maps_flutter/example/README.md diff --git a/packages/platform_maps_flutter/example/analysis_options.yaml b/packages/platform_maps_flutter/example/analysis_options.yaml new file mode 100644 index 0000000..d6b52f7 --- /dev/null +++ b/packages/platform_maps_flutter/example/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - require_trailing_commas \ No newline at end of file diff --git a/example/android/.gitignore b/packages/platform_maps_flutter/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to packages/platform_maps_flutter/example/android/.gitignore diff --git a/example/android/.project b/packages/platform_maps_flutter/example/android/.project similarity index 100% rename from example/android/.project rename to packages/platform_maps_flutter/example/android/.project diff --git a/example/android/app/.project b/packages/platform_maps_flutter/example/android/app/.project similarity index 100% rename from example/android/app/.project rename to packages/platform_maps_flutter/example/android/app/.project diff --git a/example/android/app/build.gradle b/packages/platform_maps_flutter/example/android/app/build.gradle similarity index 100% rename from example/android/app/build.gradle rename to packages/platform_maps_flutter/example/android/app/build.gradle diff --git a/example/android/app/src/debug/AndroidManifest.xml b/packages/platform_maps_flutter/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example/android/app/src/debug/AndroidManifest.xml rename to packages/platform_maps_flutter/example/android/app/src/debug/AndroidManifest.xml diff --git a/example/android/app/src/main/AndroidManifest.xml b/packages/platform_maps_flutter/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to packages/platform_maps_flutter/example/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/kotlin/com/example/flutter_platform_maps_example/MainActivity.kt b/packages/platform_maps_flutter/example/android/app/src/main/kotlin/com/example/flutter_platform_maps_example/MainActivity.kt similarity index 100% rename from example/android/app/src/main/kotlin/com/example/flutter_platform_maps_example/MainActivity.kt rename to packages/platform_maps_flutter/example/android/app/src/main/kotlin/com/example/flutter_platform_maps_example/MainActivity.kt diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/platform_maps_flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable-v21/launch_background.xml rename to packages/platform_maps_flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/packages/platform_maps_flutter/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to packages/platform_maps_flutter/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/platform_maps_flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values-night/styles.xml b/packages/platform_maps_flutter/example/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from example/android/app/src/main/res/values-night/styles.xml rename to packages/platform_maps_flutter/example/android/app/src/main/res/values-night/styles.xml diff --git a/example/android/app/src/main/res/values/styles.xml b/packages/platform_maps_flutter/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to packages/platform_maps_flutter/example/android/app/src/main/res/values/styles.xml diff --git a/example/android/app/src/profile/AndroidManifest.xml b/packages/platform_maps_flutter/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example/android/app/src/profile/AndroidManifest.xml rename to packages/platform_maps_flutter/example/android/app/src/profile/AndroidManifest.xml diff --git a/example/android/build.gradle b/packages/platform_maps_flutter/example/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to packages/platform_maps_flutter/example/android/build.gradle diff --git a/example/android/gradle.properties b/packages/platform_maps_flutter/example/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to packages/platform_maps_flutter/example/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/platform_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/platform_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/settings.gradle b/packages/platform_maps_flutter/example/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to packages/platform_maps_flutter/example/android/settings.gradle diff --git a/example/ios/.gitignore b/packages/platform_maps_flutter/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to packages/platform_maps_flutter/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/packages/platform_maps_flutter/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to packages/platform_maps_flutter/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/example/ios/Flutter/Debug.xcconfig b/packages/platform_maps_flutter/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from example/ios/Flutter/Debug.xcconfig rename to packages/platform_maps_flutter/example/ios/Flutter/Debug.xcconfig diff --git a/example/ios/Flutter/Release.xcconfig b/packages/platform_maps_flutter/example/ios/Flutter/Release.xcconfig similarity index 100% rename from example/ios/Flutter/Release.xcconfig rename to packages/platform_maps_flutter/example/ios/Flutter/Release.xcconfig diff --git a/example/ios/Podfile b/packages/platform_maps_flutter/example/ios/Podfile similarity index 100% rename from example/ios/Podfile rename to packages/platform_maps_flutter/example/ios/Podfile diff --git a/packages/platform_maps_flutter/example/ios/Podfile.lock b/packages/platform_maps_flutter/example/ios/Podfile.lock new file mode 100644 index 0000000..22c8bbf --- /dev/null +++ b/packages/platform_maps_flutter/example/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - apple_maps_flutter (0.0.1): + - Flutter + - Flutter (1.0.0) + +DEPENDENCIES: + - apple_maps_flutter (from `.symlinks/plugins/apple_maps_flutter/ios`) + - Flutter (from `Flutter`) + +EXTERNAL SOURCES: + apple_maps_flutter: + :path: ".symlinks/plugins/apple_maps_flutter/ios" + Flutter: + :path: Flutter + +SPEC CHECKSUMS: + apple_maps_flutter: c59725efea39e13e703cde52a1d2b14866ad68a8 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + +PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 + +COCOAPODS: 1.15.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj similarity index 95% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 5967552..3c066ad 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -139,8 +139,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 882663772740F21D0502C7C7 /* [CP] Embed Pods Frameworks */, - 509C3ECF666D4B4E74707666 /* [CP] Copy Pods Resources */, + 76650B2D2D84915992E827CA /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -215,23 +214,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 509C3ECF666D4B4E74707666 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 5B104C5C09A0B66E644C71E6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -254,7 +236,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 882663772740F21D0502C7C7 /* [CP] Embed Pods Frameworks */ = { + 76650B2D2D84915992E827CA /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -379,7 +361,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPlatformMapsExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -503,7 +488,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPlatformMapsExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -522,7 +510,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPlatformMapsExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/platform_maps_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/platform_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/platform_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/platform_maps_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/platform_maps_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/platform_maps_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/platform_maps_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/platform_maps_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/platform_maps_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.swift b/packages/platform_maps_flutter/example/ios/Runner/AppDelegate.swift similarity index 100% rename from example/ios/Runner/AppDelegate.swift rename to packages/platform_maps_flutter/example/ios/Runner/AppDelegate.swift diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/platform_maps_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/platform_maps_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/platform_maps_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/packages/platform_maps_flutter/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to packages/platform_maps_flutter/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/packages/platform_maps_flutter/example/ios/Runner/Info.plist similarity index 100% rename from example/ios/Runner/Info.plist rename to packages/platform_maps_flutter/example/ios/Runner/Info.plist diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/packages/platform_maps_flutter/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from example/ios/Runner/Runner-Bridging-Header.h rename to packages/platform_maps_flutter/example/ios/Runner/Runner-Bridging-Header.h diff --git a/example/lib/main.dart b/packages/platform_maps_flutter/example/lib/main.dart similarity index 95% rename from example/lib/main.dart rename to packages/platform_maps_flutter/example/lib/main.dart index 31f9b34..1989228 100644 --- a/example/lib/main.dart +++ b/packages/platform_maps_flutter/example/lib/main.dart @@ -47,7 +47,8 @@ class _MyHomePageState extends State { }, mapType: MapType.satellite, onTap: (location) => debugPrint('onTap: $location'), - onCameraMove: (cameraUpdate) => debugPrint('onCameraMove: $cameraUpdate'), + onCameraMove: (cameraUpdate) => + debugPrint('onCameraMove: $cameraUpdate'), compassEnabled: true, onMapCreated: (controller) { Future.delayed(const Duration(seconds: 2)).then( diff --git a/example/pubspec.yaml b/packages/platform_maps_flutter/example/pubspec.yaml similarity index 100% rename from example/pubspec.yaml rename to packages/platform_maps_flutter/example/pubspec.yaml diff --git a/packages/platform_maps_flutter/lib/platform_maps_flutter.dart b/packages/platform_maps_flutter/lib/platform_maps_flutter.dart new file mode 100644 index 0000000..cfe2489 --- /dev/null +++ b/packages/platform_maps_flutter/lib/platform_maps_flutter.dart @@ -0,0 +1,21 @@ +library platform_maps_flutter; + +export 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart' + show + ArgumentCallback, + BitmapDescriptor, + CameraPosition, + CameraPositionCallback, + CameraUpdate, + Circle, + InfoWindow, + LatLng, + LatLngBounds, + MapType, + Marker, + MarkerId, + MinMaxZoomPreference, + PlatformMap, + PlatformMapController, + Polygon, + Polyline; diff --git a/packages/platform_maps_flutter/pubspec.yaml b/packages/platform_maps_flutter/pubspec.yaml new file mode 100644 index 0000000..b842dd7 --- /dev/null +++ b/packages/platform_maps_flutter/pubspec.yaml @@ -0,0 +1,31 @@ +name: platform_maps_flutter +description: A Flutter package that combines google_maps and apple_maps to provide a crossplatform native map implementation. +version: 2.0.0-beta +homepage: https://github.com/albert-heijn-technology +repository: https://github.com/albert-heijn-technology/platform_maps_flutter/tree/master/packages/platform_maps_flutter +issue_tracker: https://github.com/albert-heijn-technology/platform_maps_flutter/issues + +environment: + flutter: '>=2.11.0' + sdk: '>=2.17.0 <4.0.0' + +flutter: + plugin: + platforms: + android: + default_package: platform_maps_flutter_google_android + ios: + default_package: platform_maps_flutter_apple + +dependencies: + flutter: + sdk: flutter + + platform_maps_flutter_apple: ^1.0.0-beta + platform_maps_flutter_google_android: ^1.0.0-beta + platform_maps_flutter_platform_interface: ^1.0.0-beta + +dev_dependencies: + flutter_lints: ^3.0.1 + flutter_test: + sdk: flutter diff --git a/packages/platform_maps_flutter/test/platform_maps_test.dart b/packages/platform_maps_flutter/test/platform_maps_test.dart new file mode 100644 index 0000000..f365595 --- /dev/null +++ b/packages/platform_maps_flutter/test/platform_maps_test.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +void main() { + testWidgets('Load platform widget', (tester) async { + PlatformMapsPlatform.instance = FakePlatform(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: PlatformMap( + initialCameraPosition: const CameraPosition( + target: LatLng(4.4, 5.5), + ), + ), + ), + ), + ); + + expect(find.text('4.4,5.5'), findsOneWidget); + }); +} + +class FakePlatform extends PlatformMapsPlatform { + @override + PlatformMapsPlatformWidget createPlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams params, + ) { + return FakeWidget(params); + } +} + +class FakeWidget extends PlatformMapsPlatformWidget { + FakeWidget(super.params) : super.implementation(); + + @override + Widget build(BuildContext context) { + final target = params.initialCameraPosition.target; + return Center(child: Text('${target.latitude},${target.longitude}')); + } +} diff --git a/packages/platform_maps_flutter_apple/.gitignore b/packages/platform_maps_flutter_apple/.gitignore new file mode 100644 index 0000000..ac5aa98 --- /dev/null +++ b/packages/platform_maps_flutter_apple/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/platform_maps_flutter_apple/.metadata b/packages/platform_maps_flutter_apple/.metadata new file mode 100644 index 0000000..056185a --- /dev/null +++ b/packages/platform_maps_flutter_apple/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ef1af02aead6fe2414f3aafa5a61087b610e1332" + channel: "stable" + +project_type: package diff --git a/packages/platform_maps_flutter_apple/CHANGELOG.md b/packages/platform_maps_flutter_apple/CHANGELOG.md new file mode 100644 index 0000000..cf84db1 --- /dev/null +++ b/packages/platform_maps_flutter_apple/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0-beta + +* Initial version, based on platform_maps_flutter 1.0.2 diff --git a/packages/platform_maps_flutter_apple/LICENSE b/packages/platform_maps_flutter_apple/LICENSE new file mode 100644 index 0000000..67f5463 --- /dev/null +++ b/packages/platform_maps_flutter_apple/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright (c) 2024, Albert Heijn Technology +Copyright (c) 2019, Luis Thein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/platform_maps_flutter_apple/README.md b/packages/platform_maps_flutter_apple/README.md new file mode 100644 index 0000000..90355db --- /dev/null +++ b/packages/platform_maps_flutter_apple/README.md @@ -0,0 +1,4 @@ +# platform_maps_flutter_apple + +This is the Apple (iOS) implementation of the `platform_maps_flutter` plugin. +If you add `platform_maps_flutter` to your pubspec.yaml this will automatically be used. \ No newline at end of file diff --git a/packages/platform_maps_flutter_apple/analysis_options.yaml b/packages/platform_maps_flutter_apple/analysis_options.yaml new file mode 100644 index 0000000..bb69c09 --- /dev/null +++ b/packages/platform_maps_flutter_apple/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - require_trailing_commas diff --git a/packages/platform_maps_flutter_apple/lib/platform_maps_flutter_apple.dart b/packages/platform_maps_flutter_apple/lib/platform_maps_flutter_apple.dart new file mode 100644 index 0000000..1e1fe58 --- /dev/null +++ b/packages/platform_maps_flutter_apple/lib/platform_maps_flutter_apple.dart @@ -0,0 +1,35 @@ +library platform_maps_flutter_apple; + +import 'package:platform_maps_flutter_apple/src/apple_maps_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_apple/src/apple_maps_camera_update.dart'; +import 'package:platform_maps_flutter_apple/src/apple_maps_widget.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class PlatformMapsApple extends PlatformMapsPlatform { + static void registerWith() { + PlatformMapsPlatform.instance = PlatformMapsApple(); + } + + /// Create a new [PlatformPlatformMapsWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PlatformMap] in `platform_maps_flutter` instead. + @override + PlatformMapsPlatformWidget createPlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams params, + ) { + return AppleMapsWidget(params); + } + + /// Create a new [PlatformBitmapDescriptor]. + /// This function should only be called by the app-facing package. + @override + PlatformBitmapDescriptor createBitmapDescriptor() { + return AppleMapsPlatformBitmapDescriptor(); + } + + @override + PlatformCameraUpdate createPlatformCameraUpdate() { + return AppleMapsPlatformCameraUpdate(); + } +} diff --git a/packages/platform_maps_flutter_apple/lib/src/apple_maps_bitmap_descriptor.dart b/packages/platform_maps_flutter_apple/lib/src/apple_maps_bitmap_descriptor.dart new file mode 100644 index 0000000..dd4238b --- /dev/null +++ b/packages/platform_maps_flutter_apple/lib/src/apple_maps_bitmap_descriptor.dart @@ -0,0 +1,36 @@ +import 'dart:typed_data'; + +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps; + +class AppleMapsPlatformBitmapDescriptor extends PlatformBitmapDescriptor { + AppleMapsPlatformBitmapDescriptor() : super.implementation(); + @override + Future fromAssetImage( + ImageConfiguration configuration, + String assetName, { + AssetBundle? bundle, + String? package, + }) async { + final descriptor = await apple_maps.BitmapDescriptor.fromAssetImage( + configuration, + assetName, + bundle: bundle, + package: package, + ); + return AppleMapsBitmapDescriptor(descriptor); + } + + @override + AppleMapsBitmapDescriptor fromBytes(Uint8List byteData) { + return AppleMapsBitmapDescriptor( + apple_maps.BitmapDescriptor.fromBytes(byteData), + ); + } +} + +class AppleMapsBitmapDescriptor extends BitmapDescriptor { + AppleMapsBitmapDescriptor(this.descriptor); + apple_maps.BitmapDescriptor descriptor; +} diff --git a/packages/platform_maps_flutter_apple/lib/src/apple_maps_camera_update.dart b/packages/platform_maps_flutter_apple/lib/src/apple_maps_camera_update.dart new file mode 100644 index 0000000..f202559 --- /dev/null +++ b/packages/platform_maps_flutter_apple/lib/src/apple_maps_camera_update.dart @@ -0,0 +1,78 @@ +import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps; +import 'package:platform_maps_flutter_apple/src/mapper_extensions.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class AppleMapsPlatformCameraUpdate extends PlatformCameraUpdate { + AppleMapsPlatformCameraUpdate() : super.implementation(); + + @override + CameraUpdate newCameraPosition(CameraPosition cameraPosition) { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.newCameraPosition( + cameraPosition.appleMapsCameraPosition, + ), + ); + } + + @override + CameraUpdate newLatLng(LatLng latLng) { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.newLatLng( + latLng.appleMapsLatLng, + ), + ); + } + + @override + CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.newLatLngBounds( + bounds.appleMapsLatLngBounds, + padding, + ), + ); + } + + @override + CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.newLatLngZoom( + latLng.appleMapsLatLng, + zoom, + ), + ); + } + + @override + CameraUpdate zoomBy(double amount) { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.zoomBy(amount), + ); + } + + @override + CameraUpdate zoomIn() { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.zoomIn(), + ); + } + + @override + CameraUpdate zoomOut() { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.zoomOut(), + ); + } + + @override + CameraUpdate zoomTo(double zoom) { + return AppleMapsCameraUpdate._( + apple_maps.CameraUpdate.zoomTo(zoom), + ); + } +} + +class AppleMapsCameraUpdate extends CameraUpdate { + const AppleMapsCameraUpdate._(this.appleMapsCameraUpdate); + final apple_maps.CameraUpdate appleMapsCameraUpdate; +} diff --git a/packages/platform_maps_flutter_apple/lib/src/apple_maps_platform_controller.dart b/packages/platform_maps_flutter_apple/lib/src/apple_maps_platform_controller.dart new file mode 100644 index 0000000..0f83173 --- /dev/null +++ b/packages/platform_maps_flutter_apple/lib/src/apple_maps_platform_controller.dart @@ -0,0 +1,62 @@ +import 'dart:typed_data'; + +import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps; +import 'package:platform_maps_flutter_apple/src/apple_maps_camera_update.dart'; +import 'package:platform_maps_flutter_apple/src/mapper_extensions.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class AppleMapsPlatformController extends PlatformMapsPlatformController { + AppleMapsPlatformController(this._appleMapController); + final apple_maps.AppleMapController _appleMapController; + + @override + Future animateCamera(CameraUpdate cameraUpdate) { + if (cameraUpdate is AppleMapsCameraUpdate) { + return _appleMapController + .animateCamera(cameraUpdate.appleMapsCameraUpdate); + } + throw UnsupportedError( + 'AppleMapsPlatformController: animateCamera - cameraUpdate is not a AppleMapsCameraUpdate\n${cameraUpdate.toString()}', + ); + } + + @override + Future getVisibleRegion() async { + final visibleRegion = await _appleMapController.getVisibleRegion(); + return visibleRegion.platformLatLngBounds; + } + + @override + Future hideMarkerInfoWindow(MarkerId markerId) { + return _appleMapController + .hideMarkerInfoWindow(markerId.appleMapsAnnotationId); + } + + @override + Future isMarkerInfoWindowShown(MarkerId markerId) async { + return (await _appleMapController + .isMarkerInfoWindowShown(markerId.appleMapsAnnotationId)) ?? + false; + } + + @override + Future moveCamera(CameraUpdate cameraUpdate) { + if (cameraUpdate is AppleMapsCameraUpdate) { + return _appleMapController.moveCamera(cameraUpdate.appleMapsCameraUpdate); + } + throw UnsupportedError( + 'AppleMapsPlatformController: moveCamera - cameraUpdate is not a AppleMapsCameraUpdate\n${cameraUpdate.toString()}', + ); + } + + @override + Future showMarkerInfoWindow(MarkerId markerId) { + return _appleMapController + .showMarkerInfoWindow(markerId.appleMapsAnnotationId); + } + + @override + Future takeSnapshot() { + return _appleMapController.takeSnapshot(); + } +} diff --git a/packages/platform_maps_flutter_apple/lib/src/apple_maps_widget.dart b/packages/platform_maps_flutter_apple/lib/src/apple_maps_widget.dart new file mode 100644 index 0000000..8eaa7f8 --- /dev/null +++ b/packages/platform_maps_flutter_apple/lib/src/apple_maps_widget.dart @@ -0,0 +1,217 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_apple/src/mapper_extensions.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps; + +class AppleMapsWidget extends PlatformMapsPlatformWidget { + AppleMapsWidget(PlatformMapsPlatformWidgetCreationParams params) + : super.implementation(params); + + @override + Widget build(BuildContext context) { + return _PlatformMap(params: params); + } +} + +class _PlatformMap extends StatefulWidget { + const _PlatformMap({ + required this.params, + }); + + final PlatformMapsPlatformWidgetCreationParams params; + + /// Callback method for when the map is ready to be used. + /// + /// Used to receive a [AppleMapController] for this [AppleMap]. + MapCreatedCallback? get onMapCreated => params.onMapCreated; + + /// The initial position of the map's camera. + CameraPosition get initialCameraPosition => params.initialCameraPosition; + + /// True if the map should show a compass when rotated. + bool get compassEnabled => params.compassEnabled; + + /// Type of map tiles to be rendered. + MapType get mapType => params.mapType; + + /// Preferred bounds for the camera zoom level. + /// + /// Actual bounds depend on map data and device. + MinMaxZoomPreference get minMaxZoomPreference => params.minMaxZoomPreference; + + /// True if the map view should respond to rotate gestures. + bool get rotateGesturesEnabled => params.rotateGesturesEnabled; + + /// True if the map view should respond to scroll gestures. + bool get scrollGesturesEnabled => params.scrollGesturesEnabled; + + /// True if the map view should show zoom controls. This includes two buttons + /// to zoom in and zoom out. The default value is to show zoom controls. + /// + /// This is only supported on Android. And this field is silently ignored on iOS. + bool get zoomControlsEnabled => params.zoomControlsEnabled; + + /// True if the map view should respond to zoom gestures. + bool get zoomGesturesEnabled => params.zoomGesturesEnabled; + + /// True if the map view should respond to tilt gestures. + bool get tiltGesturesEnabled => params.tiltGesturesEnabled; + + /// Padding to be set on map. + EdgeInsets get padding => params.padding; + + /// Markers to be placed on the map. + Set get markers => params.markers; + + /// Polygons to be placed on the map. + Set get polygons => params.polygons; + + /// Polylines to be placed on the map. + Set get polylines => params.polylines; + + /// Circles to be placed on the map. + Set get circles => params.circles; + + /// Called when the camera starts moving. + /// + /// This can be initiated by the following: + /// 1. Non-gesture animation initiated in response to user actions. + /// For example: zoom buttons, my location button, or marker clicks. + /// 2. Programmatically initiated animation. + /// 3. Camera motion initiated in response to user gestures on the map. + /// For example: pan, tilt, pinch to zoom, or rotate. + VoidCallback? get onCameraMoveStarted => params.onCameraMoveStarted; + + /// Called repeatedly as the camera continues to move after an + /// onCameraMoveStarted call. + /// + /// This may be called as often as once every frame and should + /// not perform expensive operations. + CameraPositionCallback? get onCameraMove => params.onCameraMove; + + /// Called when camera movement has ended, there are no pending + /// animations and the user has stopped interacting with the map. + VoidCallback? get onCameraIdle => params.onCameraIdle; + + /// Called every time a [PlatformMap] is tapped. + ArgumentCallback? get onTap => params.onTap; + + /// Called every time a [PlatformMap] is long pressed. + ArgumentCallback? get onLongPress => params.onLongPress; + + /// True if a "My Location" layer should be shown on the map. + /// + /// This layer includes a location indicator at the current device location, + /// as well as a My Location button. + /// * The indicator is a small blue dot if the device is stationary, or a + /// chevron if the device is moving. + /// * The My Location button animates to focus on the user's current location + /// if the user's location is currently known. + /// + /// Enabling this feature requires adding location permissions to both native + /// platforms of your app. + /// * On Android add either + /// `` + /// or `` + /// to your `AndroidManifest.xml` file. `ACCESS_COARSE_LOCATION` returns a + /// location with an accuracy approximately equivalent to a city block, while + /// `ACCESS_FINE_LOCATION` returns as precise a location as possible, although + /// it consumes more battery power. You will also need to request these + /// permissions during run-time. If they are not granted, the My Location + /// feature will fail silently. + /// * On iOS add a `NSLocationWhenInUseUsageDescription` key to your + /// `Info.plist` file. This will automatically prompt the user for permissions + /// when the map tries to turn on the My Location layer. + bool get myLocationEnabled => params.myLocationEnabled; + + /// Enables or disables the my-location button. + /// + /// The my-location button causes the camera to move such that the user's + /// location is in the center of the map. If the button is enabled, it is + /// only shown when the my-location layer is enabled. + /// + /// By default, the my-location button is enabled (and hence shown when the + /// my-location layer is enabled). + /// + /// See also: + /// * [myLocationEnabled] parameter. + bool get myLocationButtonEnabled => params.myLocationButtonEnabled; + + /// Enables or disables the traffic layer of the map + bool get trafficEnabled => params.trafficEnabled; + + /// Which gestures should be consumed by the map. + /// + /// It is possible for other gesture recognizers to be competing with the map on pointer + /// events, e.g if the map is inside a [ListView] the [ListView] will want to handle + /// vertical drags. The map will claim gestures that are recognized by any of the + /// recognizers on this list. + /// + /// When this set is empty, the map will only handle pointer events for gestures that + /// were not claimed by any other gesture recognizer. + Set> get gestureRecognizers => + params.gestureRecognizers; + @override + _PlatformMapState createState() => _PlatformMapState(); +} + +class _PlatformMapState extends State<_PlatformMap> { + @override + Widget build(BuildContext context) { + return apple_maps.AppleMap( + initialCameraPosition: + widget.initialCameraPosition.appleMapsCameraPosition, + compassEnabled: widget.compassEnabled, + mapType: _getMapType(), + padding: widget.padding, + annotations: widget.markers.appleMapsAnnotationSet, + polylines: widget.polylines.appleMapsPolylineSet, + polygons: widget.polygons.appleMapsPolygonSet, + circles: widget.circles.appleMapsCircleSet, + gestureRecognizers: widget.gestureRecognizers, + onCameraIdle: widget.onCameraIdle, + myLocationButtonEnabled: widget.myLocationButtonEnabled, + myLocationEnabled: widget.myLocationEnabled, + onCameraMoveStarted: widget.onCameraMoveStarted, + pitchGesturesEnabled: widget.tiltGesturesEnabled, + rotateGesturesEnabled: widget.rotateGesturesEnabled, + zoomGesturesEnabled: widget.zoomGesturesEnabled, + scrollGesturesEnabled: widget.scrollGesturesEnabled, + onMapCreated: _onMapCreated, + onCameraMove: _onCameraMove, + onTap: _onTap, + onLongPress: _onLongPress, + trafficEnabled: widget.trafficEnabled, + minMaxZoomPreference: widget.minMaxZoomPreference.appleMapsZoomPreference, + ); + } + + void _onMapCreated(apple_maps.AppleMapController controller) { + widget.onMapCreated?.call(controller.platformMapController); + } + + void _onCameraMove(apple_maps.CameraPosition cameraPosition) { + widget.onCameraMove?.call(cameraPosition.platformCameraPosition); + } + + void _onTap(apple_maps.LatLng position) { + widget.onTap?.call(position.platformLatLng); + } + + void _onLongPress(apple_maps.LatLng position) { + widget.onLongPress?.call(position.platformLatLng); + } + + apple_maps.MapType _getMapType() { + switch (widget.mapType) { + case MapType.normal: + return apple_maps.MapType.standard; + case MapType.satellite: + return apple_maps.MapType.satellite; + case MapType.hybrid: + return apple_maps.MapType.hybrid; + } + } +} diff --git a/packages/platform_maps_flutter_apple/lib/src/mapper_extensions.dart b/packages/platform_maps_flutter_apple/lib/src/mapper_extensions.dart new file mode 100644 index 0000000..b928864 --- /dev/null +++ b/packages/platform_maps_flutter_apple/lib/src/mapper_extensions.dart @@ -0,0 +1,231 @@ +import 'dart:ui'; + +import 'package:apple_maps_flutter/apple_maps_flutter.dart' as apple_maps; +import 'package:platform_maps_flutter_apple/src/apple_maps_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_apple/src/apple_maps_platform_controller.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +extension AppleMapsLatLngMapper on apple_maps.LatLng { + LatLng get platformLatLng { + return LatLng(latitude, longitude); + } +} + +extension LatLngMapper on LatLng { + apple_maps.LatLng get appleMapsLatLng { + return apple_maps.LatLng(latitude, longitude); + } +} + +extension on List { + List get appleLatLngList { + return map((latLng) => latLng.appleMapsLatLng).toList(); + } +} + +extension LatLngBoundsMapper on LatLngBounds { + apple_maps.LatLngBounds get appleMapsLatLngBounds { + return apple_maps.LatLngBounds( + southwest: southwest.appleMapsLatLng, + northeast: northeast.appleMapsLatLng, + ); + } +} + +extension AppleMapsLatLngBoundsMapper on apple_maps.LatLngBounds { + LatLngBounds get platformLatLngBounds { + return LatLngBounds( + southwest: southwest.platformLatLng, + northeast: northeast.platformLatLng, + ); + } +} + +extension MarkersMapper on Set { + Set get appleMapsAnnotationSet { + return map((marker) => marker.appleMapsAnnotation).toSet(); + } +} + +extension on Marker { + apple_maps.Annotation get appleMapsAnnotation => apple_maps.Annotation( + annotationId: markerId.appleMapsAnnotationId, + alpha: alpha, + anchor: anchor, + draggable: draggable, + infoWindow: infoWindow.appleMapsInfoWindow, + onTap: onTap, + icon: (icon as AppleMapsBitmapDescriptor?)?.descriptor ?? + apple_maps.BitmapDescriptor.defaultAnnotation, + visible: visible, + onDragEnd: onDragEnd != null + ? (apple_maps.LatLng latLng) => _onDragEnd(latLng, onDragEnd) + : null, + position: position.appleMapsLatLng, + ); + + static _onDragEnd(apple_maps.LatLng latLng, Function? onDragEnd) { + onDragEnd?.call(latLng.platformLatLng); + } +} + +extension MarkerIdMapper on MarkerId { + apple_maps.AnnotationId get appleMapsAnnotationId { + return apple_maps.AnnotationId(value); + } +} + +extension on InfoWindow { + apple_maps.InfoWindow get appleMapsInfoWindow => apple_maps.InfoWindow( + anchor: anchor ?? const Offset(0, 0), + onTap: onTap, + snippet: snippet, + title: title, + ); +} + +extension CameraMapper on CameraPosition { + apple_maps.CameraPosition get appleMapsCameraPosition { + return apple_maps.CameraPosition( + target: target.appleMapsLatLng, + heading: bearing, + pitch: tilt, + zoom: zoom, + ); + } +} + +extension AppleMapsCameraMapper on apple_maps.CameraPosition { + CameraPosition get platformCameraPosition { + return CameraPosition( + target: target.platformLatLng, + bearing: heading, + tilt: pitch, + zoom: zoom, + ); + } +} + +extension PolylinesMapper on Set { + Set get appleMapsPolylineSet { + return map((polyline) => polyline.appleMapsPolyline).toSet(); + } +} + +extension on Polyline { + apple_maps.Polyline get appleMapsPolyline { + return apple_maps.Polyline( + polylineId: polylineId.appleMapsPolylineId, + color: color, + consumeTapEvents: consumeTapEvents, + polylineCap: polylineCap.applePolylineCap, + jointType: jointType.appleMapsJointType, + onTap: onTap, + patterns: patterns.appleMapsPatternItemList, + points: points.appleLatLngList, + visible: visible, + width: width, + ); + } +} + +extension on PolylineId { + apple_maps.PolylineId get appleMapsPolylineId { + return apple_maps.PolylineId(value); + } +} + +extension on Cap { + static const Map appleMapsCaps = { + Cap.buttCap: apple_maps.Cap.buttCap, + Cap.roundCap: apple_maps.Cap.roundCap, + Cap.squareCap: apple_maps.Cap.squareCap, + }; + + apple_maps.Cap get applePolylineCap { + return appleMapsCaps[this]!; + } +} + +extension on JointType { + static const List appleMapsJointTypes = [ + apple_maps.JointType.mitered, + apple_maps.JointType.bevel, + apple_maps.JointType.round, + ]; + + apple_maps.JointType get appleMapsJointType { + return appleMapsJointTypes[value]; + } +} + +extension on List { + List get appleMapsPatternItemList { + return map((patternItem) => patternItem.appleMapsPatternItem).toList(); + } +} + +extension on PatternItem { + apple_maps.PatternItem get appleMapsPatternItem { + return switch (this) { + DotPatternItem() => apple_maps.PatternItem.dot, + DashPatternItem dash => apple_maps.PatternItem.dash(dash.length), + GapPatternItem gap => apple_maps.PatternItem.gap(gap.length), + }; + } +} + +extension PolygonMapper on Set { + Set get appleMapsPolygonSet { + return map((polygon) => polygon.appleMapsPolygon).toSet(); + } +} + +extension on Polygon { + apple_maps.Polygon get appleMapsPolygon { + return apple_maps.Polygon( + polygonId: apple_maps.PolygonId(polygonId.value), + consumeTapEvents: consumeTapEvents, + fillColor: fillColor, + onTap: onTap, + points: points.appleLatLngList, + strokeColor: strokeColor, + strokeWidth: strokeWidth, + visible: visible, + ); + } +} + +extension CircleMapper on Set { + Set get appleMapsCircleSet { + return map((circle) => circle.appleMapsCircle).toSet(); + } +} + +extension on Circle { + apple_maps.Circle get appleMapsCircle { + return apple_maps.Circle( + circleId: apple_maps.CircleId(circleId.value), + consumeTapEvents: consumeTapEvents, + fillColor: fillColor, + onTap: onTap, + center: center.appleMapsLatLng, + radius: radius, + strokeColor: strokeColor, + strokeWidth: strokeWidth, + visible: visible, + ); + } +} + +extension ZoomMapper on MinMaxZoomPreference { + apple_maps.MinMaxZoomPreference get appleMapsZoomPreference { + return apple_maps.MinMaxZoomPreference(minZoom, maxZoom); + } +} + +extension AppleMapsControllerMappers on apple_maps.AppleMapController { + PlatformMapController get platformMapController { + return PlatformMapController(AppleMapsPlatformController(this)); + } +} diff --git a/packages/platform_maps_flutter_apple/pubspec.yaml b/packages/platform_maps_flutter_apple/pubspec.yaml new file mode 100644 index 0000000..346da04 --- /dev/null +++ b/packages/platform_maps_flutter_apple/pubspec.yaml @@ -0,0 +1,30 @@ +name: platform_maps_flutter_apple +description: Apple Maps implementation for the platform_maps_flutter plugin. +version: 1.0.0-beta +homepage: https://github.com/albert-heijn-technology +repository: https://github.com/albert-heijn-technology/platform_maps_flutter/tree/master/packages/platform_maps_flutter_apple +issue_tracker: https://github.com/albert-heijn-technology/platform_maps_flutter/issues + +environment: + sdk: '>=3.2.4 <4.0.0' + flutter: ">=1.17.0" + +flutter: + plugin: + implements: platform_maps_flutter + platforms: + ios: + dartPluginClass: PlatformMapsApple + fileName: platform_maps_flutter_apple.dart + +dependencies: + flutter: + sdk: flutter + apple_maps_flutter: ^1.3.0 + platform_maps_flutter_platform_interface: ^1.0.0-beta + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + diff --git a/packages/platform_maps_flutter_apple/test/platform_maps_flutter_apple_test.dart b/packages/platform_maps_flutter_apple/test/platform_maps_flutter_apple_test.dart new file mode 100644 index 0000000..f62fe88 --- /dev/null +++ b/packages/platform_maps_flutter_apple/test/platform_maps_flutter_apple_test.dart @@ -0,0 +1,42 @@ +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:platform_maps_flutter_apple/platform_maps_flutter_apple.dart'; +import 'package:platform_maps_flutter_apple/src/apple_maps_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_apple/src/apple_maps_camera_update.dart'; +import 'package:platform_maps_flutter_apple/src/apple_maps_widget.dart'; + +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +void main() { + test('Verify widget generation', () { + PlatformMapsApple.registerWith(); + + final widget = PlatformMapsPlatformWidget( + const PlatformMapsPlatformWidgetCreationParams( + initialCameraPosition: CameraPosition(target: LatLng(0, 0)), + ), + ); + expect(widget, isA()); + }); + + test('Verify bitmap descriptor generation', () { + PlatformMapsApple.registerWith(); + + final platformBitmapDescriptor = PlatformBitmapDescriptor(); + expect(platformBitmapDescriptor, isA()); + + final bitmapDescriptor = platformBitmapDescriptor.fromBytes(Uint8List(0)); + expect(bitmapDescriptor, isA()); + }); + + test('Verify camera update generation', () { + PlatformMapsApple.registerWith(); + + final platformCameraUpdate = PlatformCameraUpdate(); + expect(platformCameraUpdate, isA()); + + final cameraUpdate = platformCameraUpdate.zoomIn(); + expect(cameraUpdate, isA()); + }); +} diff --git a/packages/platform_maps_flutter_google_android/.gitignore b/packages/platform_maps_flutter_google_android/.gitignore new file mode 100644 index 0000000..ac5aa98 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/platform_maps_flutter_google_android/.metadata b/packages/platform_maps_flutter_google_android/.metadata new file mode 100644 index 0000000..056185a --- /dev/null +++ b/packages/platform_maps_flutter_google_android/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ef1af02aead6fe2414f3aafa5a61087b610e1332" + channel: "stable" + +project_type: package diff --git a/packages/platform_maps_flutter_google_android/CHANGELOG.md b/packages/platform_maps_flutter_google_android/CHANGELOG.md new file mode 100644 index 0000000..cf84db1 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0-beta + +* Initial version, based on platform_maps_flutter 1.0.2 diff --git a/packages/platform_maps_flutter_google_android/LICENSE b/packages/platform_maps_flutter_google_android/LICENSE new file mode 100644 index 0000000..67f5463 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright (c) 2024, Albert Heijn Technology +Copyright (c) 2019, Luis Thein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/platform_maps_flutter_google_android/README.md b/packages/platform_maps_flutter_google_android/README.md new file mode 100644 index 0000000..efef462 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/README.md @@ -0,0 +1,6 @@ +# platform_maps_flutter_google_android + +This is the Google Maps for Android implementation of the `platform_maps_flutter` plugin. +If you add `platform_maps_flutter` to your pubspec.yaml this will automatically be used. + +It's Android only to prevent iOS dependencies for Google Maps being added to your project. \ No newline at end of file diff --git a/packages/platform_maps_flutter_google_android/analysis_options.yaml b/packages/platform_maps_flutter_google_android/analysis_options.yaml new file mode 100644 index 0000000..bb69c09 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - require_trailing_commas diff --git a/packages/platform_maps_flutter_google_android/lib/platform_maps_flutter_google_android.dart b/packages/platform_maps_flutter_google_android/lib/platform_maps_flutter_google_android.dart new file mode 100644 index 0000000..95571e1 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/platform_maps_flutter_google_android.dart @@ -0,0 +1,35 @@ +library platform_maps_flutter_google_android; + +import 'package:platform_maps_flutter_google_android/src/google_maps_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_google_android/src/google_maps_camera_update.dart'; +import 'package:platform_maps_flutter_google_android/src/google_maps_widget.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class PlatformMapsGoogleAndroid extends PlatformMapsPlatform { + static void registerWith() { + PlatformMapsPlatform.instance = PlatformMapsGoogleAndroid(); + } + + /// Create a new [PlatformPlatformMapsWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PlatformMap] in `platform_maps_flutter` instead. + @override + PlatformMapsPlatformWidget createPlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams params, + ) { + return GoogleMapsWidget(params); + } + + /// Create a new [PlatformBitmapDescriptor]. + /// This function should only be called by the app-facing package. + @override + GoogleMapsPlatformBitmapDescriptor createBitmapDescriptor() { + return GoogleMapsPlatformBitmapDescriptor(); + } + + @override + PlatformCameraUpdate createPlatformCameraUpdate() { + return GoogleMapsPlatformCameraUpdate(); + } +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_map_original.dart b/packages/platform_maps_flutter_google_android/lib/src/google_map_original.dart new file mode 100644 index 0000000..dcc549c --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_map_original.dart @@ -0,0 +1,6 @@ +/// These files are literal copies of [google_maps_flutter](https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter/lib/src) +/// We cannot depend on google_maps_flutter directly as it will include google_maps_ios and its native artifacts. +/// +/// Can be updated if this issue is resolved: https://github.com/flutter/flutter/issues/81650 +/// Related discussion: https://github.com/flutter/flutter/issues/148210 +export 'google_map_original/google_map.dart'; diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_map_original/controller.dart b/packages/platform_maps_flutter_google_android/lib/src/google_map_original/controller.dart new file mode 100644 index 0000000..89f0cf0 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_map_original/controller.dart @@ -0,0 +1,288 @@ +// 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: library_private_types_in_public_api + +part of 'google_map.dart'; + +/// Controller for a single GoogleMap instance running on the host platform. +class GoogleMapController { + GoogleMapController._( + this._googleMapState, { + required this.mapId, + }) { + _connectStreams(mapId); + } + + /// The mapId for this controller + final int mapId; + + /// Initialize control of a [GoogleMap] with [id]. + /// + /// Mainly for internal use when instantiating a [GoogleMapController] passed + /// in [GoogleMap.onMapCreated] callback. + static Future init( + int id, + CameraPosition initialCameraPosition, + _GoogleMapState googleMapState, + ) async { + await GoogleMapsFlutterPlatform.instance.init(id); + return GoogleMapController._( + googleMapState, + mapId: id, + ); + } + + final _GoogleMapState _googleMapState; + + void _connectStreams(int mapId) { + if (_googleMapState.widget.onCameraMoveStarted != null) { + GoogleMapsFlutterPlatform.instance + .onCameraMoveStarted(mapId: mapId) + .listen((_) => _googleMapState.widget.onCameraMoveStarted!()); + } + if (_googleMapState.widget.onCameraMove != null) { + GoogleMapsFlutterPlatform.instance.onCameraMove(mapId: mapId).listen( + (CameraMoveEvent e) => + _googleMapState.widget.onCameraMove!(e.value), + ); + } + if (_googleMapState.widget.onCameraIdle != null) { + GoogleMapsFlutterPlatform.instance + .onCameraIdle(mapId: mapId) + .listen((_) => _googleMapState.widget.onCameraIdle!()); + } + GoogleMapsFlutterPlatform.instance + .onMarkerTap(mapId: mapId) + .listen((MarkerTapEvent e) => _googleMapState.onMarkerTap(e.value)); + GoogleMapsFlutterPlatform.instance.onMarkerDragStart(mapId: mapId).listen( + (MarkerDragStartEvent e) => + _googleMapState.onMarkerDragStart(e.value, e.position), + ); + GoogleMapsFlutterPlatform.instance.onMarkerDrag(mapId: mapId).listen( + (MarkerDragEvent e) => + _googleMapState.onMarkerDrag(e.value, e.position), + ); + GoogleMapsFlutterPlatform.instance.onMarkerDragEnd(mapId: mapId).listen( + (MarkerDragEndEvent e) => + _googleMapState.onMarkerDragEnd(e.value, e.position), + ); + GoogleMapsFlutterPlatform.instance.onInfoWindowTap(mapId: mapId).listen( + (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value), + ); + GoogleMapsFlutterPlatform.instance + .onPolylineTap(mapId: mapId) + .listen((PolylineTapEvent e) => _googleMapState.onPolylineTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onPolygonTap(mapId: mapId) + .listen((PolygonTapEvent e) => _googleMapState.onPolygonTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onCircleTap(mapId: mapId) + .listen((CircleTapEvent e) => _googleMapState.onCircleTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onTap(mapId: mapId) + .listen((MapTapEvent e) => _googleMapState.onTap(e.position)); + GoogleMapsFlutterPlatform.instance.onLongPress(mapId: mapId).listen( + (MapLongPressEvent e) => _googleMapState.onLongPress(e.position), + ); + } + + /// Updates configuration options of the map user interface. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future _updateMapConfiguration(MapConfiguration update) { + return GoogleMapsFlutterPlatform.instance + .updateMapConfiguration(update, mapId: mapId); + } + + /// Updates marker configuration. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future _updateMarkers(MarkerUpdates markerUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateMarkers(markerUpdates, mapId: mapId); + } + + /// Updates polygon configuration. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future _updatePolygons(PolygonUpdates polygonUpdates) { + return GoogleMapsFlutterPlatform.instance + .updatePolygons(polygonUpdates, mapId: mapId); + } + + /// Updates polyline configuration. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future _updatePolylines(PolylineUpdates polylineUpdates) { + return GoogleMapsFlutterPlatform.instance + .updatePolylines(polylineUpdates, mapId: mapId); + } + + /// Updates circle configuration. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future _updateCircles(CircleUpdates circleUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateCircles(circleUpdates, mapId: mapId); + } + + /// Updates tile overlays configuration. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future _updateTileOverlays(Set newTileOverlays) { + return GoogleMapsFlutterPlatform.instance + .updateTileOverlays(newTileOverlays: newTileOverlays, mapId: mapId); + } + + /// Clears the tile cache so that all tiles will be requested again from the + /// [TileProvider]. + /// + /// The current tiles from this tile overlay will also be + /// cleared from the map after calling this method. The API maintains a small + /// in-memory cache of tiles. If you want to cache tiles for longer, you + /// should implement an on-disk cache. + Future clearTileCache(TileOverlayId tileOverlayId) async { + return GoogleMapsFlutterPlatform.instance + .clearTileCache(tileOverlayId, mapId: mapId); + } + + /// Starts an animated change of the map camera position. + /// + /// The returned [Future] completes after the change has been started on the + /// platform side. + Future animateCamera(CameraUpdate cameraUpdate) { + return GoogleMapsFlutterPlatform.instance + .animateCamera(cameraUpdate, mapId: mapId); + } + + /// Changes the map camera position. + /// + /// The returned [Future] completes after the change has been made on the + /// platform side. + Future moveCamera(CameraUpdate cameraUpdate) { + return GoogleMapsFlutterPlatform.instance + .moveCamera(cameraUpdate, mapId: mapId); + } + + /// Sets the styling of the base map. + /// + /// Set to `null` to clear any previous custom styling. + /// + /// If problems were detected with the [mapStyle], including un-parsable + /// styling JSON, unrecognized feature type, unrecognized element type, or + /// invalid styler keys: [MapStyleException] is thrown and the current + /// style is left unchanged. + /// + /// The style string can be generated using [map style tool](https://mapstyle.withgoogle.com/). + /// Also, refer [iOS](https://developers.google.com/maps/documentation/ios-sdk/style-reference) + /// and [Android](https://developers.google.com/maps/documentation/android-sdk/style-reference) + /// style reference for more information regarding the supported styles. + @Deprecated('Use GoogleMap.style instead.') + Future setMapStyle(String? mapStyle) { + return GoogleMapsFlutterPlatform.instance + .setMapStyle(mapStyle, mapId: mapId); + } + + /// Returns the last style error, if any. + Future getStyleError() { + return GoogleMapsFlutterPlatform.instance.getStyleError(mapId: mapId); + } + + /// Return [LatLngBounds] defining the region that is visible in a map. + Future getVisibleRegion() { + return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId); + } + + /// Return [ScreenCoordinate] of the [LatLng] in the current map view. + /// + /// A projection is used to translate between on screen location and geographic coordinates. + /// Screen location is in screen pixels (not display pixels) with respect to the top left corner + /// of the map, not necessarily of the whole screen. + Future getScreenCoordinate(LatLng latLng) { + return GoogleMapsFlutterPlatform.instance + .getScreenCoordinate(latLng, mapId: mapId); + } + + /// Returns [LatLng] corresponding to the [ScreenCoordinate] in the current map view. + /// + /// Returned [LatLng] corresponds to a screen location. The screen location is specified in screen + /// pixels (not display pixels) relative to the top left of the map, not top left of the whole screen. + Future getLatLng(ScreenCoordinate screenCoordinate) { + return GoogleMapsFlutterPlatform.instance + .getLatLng(screenCoordinate, mapId: mapId); + } + + /// Programmatically show the Info Window for a [Marker]. + /// + /// The `markerId` must match one of the markers on the map. + /// An invalid `markerId` triggers an "Invalid markerId" error. + /// + /// * See also: + /// * [hideMarkerInfoWindow] to hide the Info Window. + /// * [isMarkerInfoWindowShown] to check if the Info Window is showing. + Future showMarkerInfoWindow(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .showMarkerInfoWindow(markerId, mapId: mapId); + } + + /// Programmatically hide the Info Window for a [Marker]. + /// + /// The `markerId` must match one of the markers on the map. + /// An invalid `markerId` triggers an "Invalid markerId" error. + /// + /// * See also: + /// * [showMarkerInfoWindow] to show the Info Window. + /// * [isMarkerInfoWindowShown] to check if the Info Window is showing. + Future hideMarkerInfoWindow(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .hideMarkerInfoWindow(markerId, mapId: mapId); + } + + /// Returns `true` when the [InfoWindow] is showing, `false` otherwise. + /// + /// The `markerId` must match one of the markers on the map. + /// An invalid `markerId` triggers an "Invalid markerId" error. + /// + /// * See also: + /// * [showMarkerInfoWindow] to show the Info Window. + /// * [hideMarkerInfoWindow] to hide the Info Window. + Future isMarkerInfoWindowShown(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .isMarkerInfoWindowShown(markerId, mapId: mapId); + } + + /// Returns the current zoom level of the map + Future getZoomLevel() { + return GoogleMapsFlutterPlatform.instance.getZoomLevel(mapId: mapId); + } + + /// Returns the image bytes of the map + Future takeSnapshot() { + return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId); + } + + /// Disposes of the platform resources + void dispose() { + GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId); + } +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_map_original/google_map.dart b/packages/platform_maps_flutter_google_android/lib/src/google_map_original/google_map.dart new file mode 100644 index 0000000..85bd733 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_map_original/google_map.dart @@ -0,0 +1,553 @@ +// 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. + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +part 'controller.dart'; + +/// Callback method for when the map is ready to be used. +/// +/// Pass to [GoogleMap.onMapCreated] to receive a [GoogleMapController] when the +/// map is created. +typedef MapCreatedCallback = void Function(GoogleMapController controller); + +// This counter is used to provide a stable "constant" initialization id +// to the buildView function, so the web implementation can use it as a +// cache key. This needs to be provided from the outside, because web +// views seem to re-render much more often that mobile platform views. +int _nextMapCreationId = 0; + +/// Error thrown when an unknown map object ID is provided to a method. +class UnknownMapObjectIdError extends Error { + /// Creates an assertion error with the provided [message]. + UnknownMapObjectIdError(this.objectType, this.objectId, [this.context]); + + /// The name of the map object whose ID is unknown. + final String objectType; + + /// The unknown maps object ID. + final MapsObjectId objectId; + + /// The context where the error occurred. + final String? context; + + @override + String toString() { + if (context != null) { + return 'Unknown $objectType ID "${objectId.value}" in $context'; + } + return 'Unknown $objectType ID "${objectId.value}"'; + } +} + +/// A widget which displays a map with data obtained from the Google Maps service. +class GoogleMap extends StatefulWidget { + /// Creates a widget displaying data from Google Maps services. + /// + /// [AssertionError] will be thrown if [initialCameraPosition] is null; + const GoogleMap({ + super.key, + required this.initialCameraPosition, + this.style, + this.onMapCreated, + this.gestureRecognizers = const >{}, + this.webGestureHandling, + this.compassEnabled = true, + this.mapToolbarEnabled = true, + this.cameraTargetBounds = CameraTargetBounds.unbounded, + this.mapType = MapType.normal, + this.minMaxZoomPreference = MinMaxZoomPreference.unbounded, + this.rotateGesturesEnabled = true, + this.scrollGesturesEnabled = true, + this.zoomControlsEnabled = true, + this.zoomGesturesEnabled = true, + this.liteModeEnabled = false, + this.tiltGesturesEnabled = true, + this.fortyFiveDegreeImageryEnabled = false, + this.myLocationEnabled = false, + this.myLocationButtonEnabled = true, + this.layoutDirection, + + /// If no padding is specified default padding will be 0. + this.padding = EdgeInsets.zero, + this.indoorViewEnabled = false, + this.trafficEnabled = false, + this.buildingsEnabled = true, + this.markers = const {}, + this.polygons = const {}, + this.polylines = const {}, + this.circles = const {}, + this.onCameraMoveStarted, + this.tileOverlays = const {}, + this.onCameraMove, + this.onCameraIdle, + this.onTap, + this.onLongPress, + this.cloudMapId, + }); + + /// Callback method for when the map is ready to be used. + /// + /// Used to receive a [GoogleMapController] for this [GoogleMap]. + final MapCreatedCallback? onMapCreated; + + /// The initial position of the map's camera. + final CameraPosition initialCameraPosition; + + /// The style for the map. + /// + /// Set to null to clear any previous custom styling. + /// + /// If problems were detected with the [mapStyle], including un-parsable + /// styling JSON, unrecognized feature type, unrecognized element type, or + /// invalid styler keys, the style is left unchanged, and the error can be + /// retrieved with [GoogleMapController.getStyleError]. + /// + /// The style string can be generated using the + /// [map style tool](https://mapstyle.withgoogle.com/). + final String? style; + + /// True if the map should show a compass when rotated. + final bool compassEnabled; + + /// True if the map should show a toolbar when you interact with the map. Android only. + final bool mapToolbarEnabled; + + /// Geographical bounding box for the camera target. + final CameraTargetBounds cameraTargetBounds; + + /// Type of map tiles to be rendered. + final MapType mapType; + + /// The layout direction to use for the embedded view. + /// + /// If this is null, the ambient [Directionality] is used instead. If there is + /// no ambient [Directionality], [TextDirection.ltr] is used. + final TextDirection? layoutDirection; + + /// Preferred bounds for the camera zoom level. + /// + /// Actual bounds depend on map data and device. + final MinMaxZoomPreference minMaxZoomPreference; + + /// True if the map view should respond to rotate gestures. + final bool rotateGesturesEnabled; + + /// True if the map view should respond to scroll gestures. + final bool scrollGesturesEnabled; + + /// True if the map view should show zoom controls. This includes two buttons + /// to zoom in and zoom out. The default value is to show zoom controls. + /// + /// This is only supported on Android. And this field is silently ignored on iOS. + final bool zoomControlsEnabled; + + /// True if the map view should respond to zoom gestures. + final bool zoomGesturesEnabled; + + /// True if the map view should be in lite mode. Android only. + /// + /// See https://developers.google.com/maps/documentation/android-sdk/lite#overview_of_lite_mode for more details. + final bool liteModeEnabled; + + /// True if the map view should respond to tilt gestures. + final bool tiltGesturesEnabled; + + /// True if 45 degree imagery should be enabled. Web only. + final bool fortyFiveDegreeImageryEnabled; + + /// Padding to be set on map. See https://developers.google.com/maps/documentation/android-sdk/map#map_padding for more details. + final EdgeInsets padding; + + /// Markers to be placed on the map. + final Set markers; + + /// Polygons to be placed on the map. + final Set polygons; + + /// Polylines to be placed on the map. + final Set polylines; + + /// Circles to be placed on the map. + final Set circles; + + /// Tile overlays to be placed on the map. + final Set tileOverlays; + + /// Called when the camera starts moving. + /// + /// This can be initiated by the following: + /// 1. Non-gesture animation initiated in response to user actions. + /// For example: zoom buttons, my location button, or marker clicks. + /// 2. Programmatically initiated animation. + /// 3. Camera motion initiated in response to user gestures on the map. + /// For example: pan, tilt, pinch to zoom, or rotate. + final VoidCallback? onCameraMoveStarted; + + /// Called repeatedly as the camera continues to move after an + /// onCameraMoveStarted call. + /// + /// This may be called as often as once every frame and should + /// not perform expensive operations. + final CameraPositionCallback? onCameraMove; + + /// Called when camera movement has ended, there are no pending + /// animations and the user has stopped interacting with the map. + final VoidCallback? onCameraIdle; + + /// Called every time a [GoogleMap] is tapped. + final ArgumentCallback? onTap; + + /// Called every time a [GoogleMap] is long pressed. + final ArgumentCallback? onLongPress; + + /// True if a "My Location" layer should be shown on the map. + /// + /// This layer includes a location indicator at the current device location, + /// as well as a My Location button. + /// * The indicator is a small blue dot if the device is stationary, or a + /// chevron if the device is moving. + /// * The My Location button animates to focus on the user's current location + /// if the user's location is currently known. + /// + /// Enabling this feature requires adding location permissions to both native + /// platforms of your app. + /// * On Android add either + /// `` + /// or `` + /// to your `AndroidManifest.xml` file. `ACCESS_COARSE_LOCATION` returns a + /// location with an accuracy approximately equivalent to a city block, while + /// `ACCESS_FINE_LOCATION` returns as precise a location as possible, although + /// it consumes more battery power. You will also need to request these + /// permissions during run-time. If they are not granted, the My Location + /// feature will fail silently. + /// * On iOS add a `NSLocationWhenInUseUsageDescription` key to your + /// `Info.plist` file. This will automatically prompt the user for permissions + /// when the map tries to turn on the My Location layer. + final bool myLocationEnabled; + + /// Enables or disables the my-location button. + /// + /// The my-location button causes the camera to move such that the user's + /// location is in the center of the map. If the button is enabled, it is + /// only shown when the my-location layer is enabled. + /// + /// By default, the my-location button is enabled (and hence shown when the + /// my-location layer is enabled). + /// + /// See also: + /// * [myLocationEnabled] parameter. + final bool myLocationButtonEnabled; + + /// Enables or disables the indoor view from the map + final bool indoorViewEnabled; + + /// Enables or disables the traffic layer of the map + final bool trafficEnabled; + + /// Enables or disables showing 3D buildings where available + final bool buildingsEnabled; + + /// Which gestures should be consumed by the map. + /// + /// It is possible for other gesture recognizers to be competing with the map on pointer + /// events, e.g if the map is inside a [ListView] the [ListView] will want to handle + /// vertical drags. The map will claim gestures that are recognized by any of the + /// recognizers on this list. + /// + /// When this set is empty, the map will only handle pointer events for gestures that + /// were not claimed by any other gesture recognizer. + final Set> gestureRecognizers; + + /// This setting controls how the API handles gestures on the map. Web only. + /// + /// See [WebGestureHandling] for more details. + final WebGestureHandling? webGestureHandling; + + /// Identifier that's associated with a specific cloud-based map style. + /// + /// See https://developers.google.com/maps/documentation/get-map-id + /// for more details. + final String? cloudMapId; + + /// Creates a [State] for this [GoogleMap]. + @override + State createState() => _GoogleMapState(); +} + +class _GoogleMapState extends State { + final int _mapId = _nextMapCreationId++; + + final Completer _controller = + Completer(); + + Map _markers = {}; + Map _polygons = {}; + Map _polylines = {}; + Map _circles = {}; + late MapConfiguration _mapConfiguration; + + @override + Widget build(BuildContext context) { + return GoogleMapsFlutterPlatform.instance.buildViewWithConfiguration( + _mapId, + onPlatformViewCreated, + widgetConfiguration: MapWidgetConfiguration( + textDirection: widget.layoutDirection ?? + Directionality.maybeOf(context) ?? + TextDirection.ltr, + initialCameraPosition: widget.initialCameraPosition, + gestureRecognizers: widget.gestureRecognizers, + ), + mapObjects: MapObjects( + markers: widget.markers, + polygons: widget.polygons, + polylines: widget.polylines, + circles: widget.circles, + ), + mapConfiguration: _mapConfiguration, + ); + } + + @override + void initState() { + super.initState(); + _mapConfiguration = _configurationFromMapWidget(widget); + _markers = keyByMarkerId(widget.markers); + _polygons = keyByPolygonId(widget.polygons); + _polylines = keyByPolylineId(widget.polylines); + _circles = keyByCircleId(widget.circles); + } + + @override + void dispose() { + _disposeController(); + super.dispose(); + } + + Future _disposeController() async { + final GoogleMapController controller = await _controller.future; + controller.dispose(); + } + + @override + void didUpdateWidget(GoogleMap oldWidget) { + super.didUpdateWidget(oldWidget); + _updateOptions(); + _updateMarkers(); + _updatePolygons(); + _updatePolylines(); + _updateCircles(); + _updateTileOverlays(); + } + + Future _updateOptions() async { + final MapConfiguration newConfig = _configurationFromMapWidget(widget); + final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration); + if (updates.isEmpty) { + return; + } + final GoogleMapController controller = await _controller.future; + unawaited(controller._updateMapConfiguration(updates)); + _mapConfiguration = newConfig; + } + + Future _updateMarkers() async { + final GoogleMapController controller = await _controller.future; + unawaited( + controller._updateMarkers( + MarkerUpdates.from(_markers.values.toSet(), widget.markers), + ), + ); + _markers = keyByMarkerId(widget.markers); + } + + Future _updatePolygons() async { + final GoogleMapController controller = await _controller.future; + unawaited( + controller._updatePolygons( + PolygonUpdates.from(_polygons.values.toSet(), widget.polygons), + ), + ); + _polygons = keyByPolygonId(widget.polygons); + } + + Future _updatePolylines() async { + final GoogleMapController controller = await _controller.future; + unawaited( + controller._updatePolylines( + PolylineUpdates.from(_polylines.values.toSet(), widget.polylines), + ), + ); + _polylines = keyByPolylineId(widget.polylines); + } + + Future _updateCircles() async { + final GoogleMapController controller = await _controller.future; + unawaited( + controller._updateCircles( + CircleUpdates.from(_circles.values.toSet(), widget.circles), + ), + ); + _circles = keyByCircleId(widget.circles); + } + + Future _updateTileOverlays() async { + final GoogleMapController controller = await _controller.future; + unawaited(controller._updateTileOverlays(widget.tileOverlays)); + } + + Future onPlatformViewCreated(int id) async { + final GoogleMapController controller = await GoogleMapController.init( + id, + widget.initialCameraPosition, + this, + ); + _controller.complete(controller); + unawaited(_updateTileOverlays()); + final MapCreatedCallback? onMapCreated = widget.onMapCreated; + if (onMapCreated != null) { + onMapCreated(controller); + } + } + + void onMarkerTap(MarkerId markerId) { + final Marker? marker = _markers[markerId]; + if (marker == null) { + throw UnknownMapObjectIdError('marker', markerId, 'onTap'); + } + final VoidCallback? onTap = marker.onTap; + if (onTap != null) { + onTap(); + } + } + + void onMarkerDragStart(MarkerId markerId, LatLng position) { + final Marker? marker = _markers[markerId]; + if (marker == null) { + throw UnknownMapObjectIdError('marker', markerId, 'onDragStart'); + } + final ValueChanged? onDragStart = marker.onDragStart; + if (onDragStart != null) { + onDragStart(position); + } + } + + void onMarkerDrag(MarkerId markerId, LatLng position) { + final Marker? marker = _markers[markerId]; + if (marker == null) { + throw UnknownMapObjectIdError('marker', markerId, 'onDrag'); + } + final ValueChanged? onDrag = marker.onDrag; + if (onDrag != null) { + onDrag(position); + } + } + + void onMarkerDragEnd(MarkerId markerId, LatLng position) { + final Marker? marker = _markers[markerId]; + if (marker == null) { + throw UnknownMapObjectIdError('marker', markerId, 'onDragEnd'); + } + final ValueChanged? onDragEnd = marker.onDragEnd; + if (onDragEnd != null) { + onDragEnd(position); + } + } + + void onPolygonTap(PolygonId polygonId) { + final Polygon? polygon = _polygons[polygonId]; + if (polygon == null) { + throw UnknownMapObjectIdError('polygon', polygonId, 'onTap'); + } + final VoidCallback? onTap = polygon.onTap; + if (onTap != null) { + onTap(); + } + } + + void onPolylineTap(PolylineId polylineId) { + final Polyline? polyline = _polylines[polylineId]; + if (polyline == null) { + throw UnknownMapObjectIdError('polyline', polylineId, 'onTap'); + } + final VoidCallback? onTap = polyline.onTap; + if (onTap != null) { + onTap(); + } + } + + void onCircleTap(CircleId circleId) { + final Circle? circle = _circles[circleId]; + if (circle == null) { + throw UnknownMapObjectIdError('marker', circleId, 'onTap'); + } + final VoidCallback? onTap = circle.onTap; + if (onTap != null) { + onTap(); + } + } + + void onInfoWindowTap(MarkerId markerId) { + final Marker? marker = _markers[markerId]; + if (marker == null) { + throw UnknownMapObjectIdError('marker', markerId, 'InfoWindow onTap'); + } + final VoidCallback? onTap = marker.infoWindow.onTap; + if (onTap != null) { + onTap(); + } + } + + void onTap(LatLng position) { + final ArgumentCallback? onTap = widget.onTap; + if (onTap != null) { + onTap(position); + } + } + + void onLongPress(LatLng position) { + final ArgumentCallback? onLongPress = widget.onLongPress; + if (onLongPress != null) { + onLongPress(position); + } + } +} + +/// Builds a [MapConfiguration] from the given [map]. +MapConfiguration _configurationFromMapWidget(GoogleMap map) { + assert(!map.liteModeEnabled || Platform.isAndroid); + return MapConfiguration( + webGestureHandling: map.webGestureHandling, + compassEnabled: map.compassEnabled, + mapToolbarEnabled: map.mapToolbarEnabled, + cameraTargetBounds: map.cameraTargetBounds, + mapType: map.mapType, + minMaxZoomPreference: map.minMaxZoomPreference, + rotateGesturesEnabled: map.rotateGesturesEnabled, + scrollGesturesEnabled: map.scrollGesturesEnabled, + tiltGesturesEnabled: map.tiltGesturesEnabled, + fortyFiveDegreeImageryEnabled: map.fortyFiveDegreeImageryEnabled, + trackCameraPosition: map.onCameraMove != null, + zoomControlsEnabled: map.zoomControlsEnabled, + zoomGesturesEnabled: map.zoomGesturesEnabled, + liteModeEnabled: map.liteModeEnabled, + myLocationEnabled: map.myLocationEnabled, + myLocationButtonEnabled: map.myLocationButtonEnabled, + padding: map.padding, + indoorViewEnabled: map.indoorViewEnabled, + trafficEnabled: map.trafficEnabled, + buildingsEnabled: map.buildingsEnabled, + cloudMapId: map.cloudMapId, + // A null style in the widget means no style, which is expressed as '' in + // the configuration to distinguish from no change (null). + style: map.style ?? '', + ); +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_maps_bitmap_descriptor.dart b/packages/platform_maps_flutter_google_android/lib/src/google_maps_bitmap_descriptor.dart new file mode 100644 index 0000000..202c171 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_maps_bitmap_descriptor.dart @@ -0,0 +1,37 @@ +import 'dart:typed_data'; + +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' + as google_maps; + +class GoogleMapsPlatformBitmapDescriptor extends PlatformBitmapDescriptor { + GoogleMapsPlatformBitmapDescriptor() : super.implementation(); + @override + Future fromAssetImage( + ImageConfiguration configuration, + String assetName, { + AssetBundle? bundle, + String? package, + }) async { + final descriptor = await google_maps.BitmapDescriptor.fromAssetImage( + configuration, + assetName, + bundle: bundle, + package: package, + ); + return GoogleMapsBitmapDescriptor(descriptor); + } + + @override + GoogleMapsBitmapDescriptor fromBytes(Uint8List byteData) { + return GoogleMapsBitmapDescriptor( + google_maps.BitmapDescriptor.fromBytes(byteData), + ); + } +} + +class GoogleMapsBitmapDescriptor extends BitmapDescriptor { + GoogleMapsBitmapDescriptor(this.descriptor); + google_maps.BitmapDescriptor descriptor; +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_maps_camera_update.dart b/packages/platform_maps_flutter_google_android/lib/src/google_maps_camera_update.dart new file mode 100644 index 0000000..3cbad86 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_maps_camera_update.dart @@ -0,0 +1,79 @@ +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' + as google_maps; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:platform_maps_flutter_google_android/src/mapper_extensions.dart'; + +class GoogleMapsPlatformCameraUpdate extends PlatformCameraUpdate { + GoogleMapsPlatformCameraUpdate() : super.implementation(); + + @override + CameraUpdate newCameraPosition(CameraPosition cameraPosition) { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.newCameraPosition( + cameraPosition.googleMapsCameraPosition, + ), + ); + } + + @override + CameraUpdate newLatLng(LatLng latLng) { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.newLatLng( + latLng.googleMapsLatLng, + ), + ); + } + + @override + CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.newLatLngBounds( + bounds.googleMapsLatLngBounds, + padding, + ), + ); + } + + @override + CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.newLatLngZoom( + latLng.googleMapsLatLng, + zoom, + ), + ); + } + + @override + CameraUpdate zoomBy(double amount) { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.zoomBy(amount), + ); + } + + @override + CameraUpdate zoomIn() { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.zoomIn(), + ); + } + + @override + CameraUpdate zoomOut() { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.zoomOut(), + ); + } + + @override + CameraUpdate zoomTo(double zoom) { + return GoogleMapsCameraUpdate._( + google_maps.CameraUpdate.zoomTo(zoom), + ); + } +} + +class GoogleMapsCameraUpdate extends CameraUpdate { + const GoogleMapsCameraUpdate._(this.googleMapsCameraUpdate); + final google_maps.CameraUpdate googleMapsCameraUpdate; +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_maps_platform_controller.dart b/packages/platform_maps_flutter_google_android/lib/src/google_maps_platform_controller.dart new file mode 100644 index 0000000..9daa3d2 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_maps_platform_controller.dart @@ -0,0 +1,63 @@ +import 'dart:typed_data'; + +import 'package:platform_maps_flutter_google_android/src/google_map_original.dart' + as google_map_original; +import 'package:platform_maps_flutter_google_android/src/google_maps_camera_update.dart'; +import 'package:platform_maps_flutter_google_android/src/mapper_extensions.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class GoogleMapsPlatformController extends PlatformMapsPlatformController { + GoogleMapsPlatformController(this._googleMapController); + final google_map_original.GoogleMapController _googleMapController; + + @override + Future animateCamera(CameraUpdate cameraUpdate) { + if (cameraUpdate is GoogleMapsCameraUpdate) { + return _googleMapController + .animateCamera(cameraUpdate.googleMapsCameraUpdate); + } + throw UnsupportedError( + 'GoogleMapsPlatformController: animateCamera - cameraUpdate is not a GoogleMapsCameraUpdate\n${cameraUpdate.toString()}', + ); + } + + @override + Future getVisibleRegion() async { + final visibleRegion = await _googleMapController.getVisibleRegion(); + return visibleRegion.platformLatLngBounds; + } + + @override + Future hideMarkerInfoWindow(MarkerId markerId) { + return _googleMapController + .hideMarkerInfoWindow(markerId.googleMapsMarkerId); + } + + @override + Future isMarkerInfoWindowShown(MarkerId markerId) { + return _googleMapController + .isMarkerInfoWindowShown(markerId.googleMapsMarkerId); + } + + @override + Future moveCamera(CameraUpdate cameraUpdate) { + if (cameraUpdate is GoogleMapsCameraUpdate) { + return _googleMapController + .moveCamera(cameraUpdate.googleMapsCameraUpdate); + } + throw UnsupportedError( + 'GoogleMapsPlatformController: moveCamera - cameraUpdate is not a GoogleMapsCameraUpdate\n${cameraUpdate.toString()}', + ); + } + + @override + Future showMarkerInfoWindow(MarkerId markerId) { + return _googleMapController + .showMarkerInfoWindow(markerId.googleMapsMarkerId); + } + + @override + Future takeSnapshot() { + return _googleMapController.takeSnapshot(); + } +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/google_maps_widget.dart b/packages/platform_maps_flutter_google_android/lib/src/google_maps_widget.dart new file mode 100644 index 0000000..6e6aed2 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/google_maps_widget.dart @@ -0,0 +1,222 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_google_android/src/mapper_extensions.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' + as google_maps; +import 'package:platform_maps_flutter_google_android/src/google_map_original.dart' + as google_map_original; + +class GoogleMapsWidget extends PlatformMapsPlatformWidget { + GoogleMapsWidget(PlatformMapsPlatformWidgetCreationParams params) + : super.implementation(params); + + @override + Widget build(BuildContext context) { + return _PlatformMap(params: params); + } +} + +class _PlatformMap extends StatefulWidget { + const _PlatformMap({ + required this.params, + }); + + final PlatformMapsPlatformWidgetCreationParams params; + + /// Callback method for when the map is ready to be used. + /// + /// Used to receive a [GoogleMapController] for this [GoogleMap]. + MapCreatedCallback? get onMapCreated => params.onMapCreated; + + /// The initial position of the map's camera. + CameraPosition get initialCameraPosition => params.initialCameraPosition; + + /// True if the map should show a compass when rotated. + bool get compassEnabled => params.compassEnabled; + + /// Type of map tiles to be rendered. + MapType get mapType => params.mapType; + + /// Preferred bounds for the camera zoom level. + /// + /// Actual bounds depend on map data and device. + MinMaxZoomPreference get minMaxZoomPreference => params.minMaxZoomPreference; + + /// True if the map view should respond to rotate gestures. + bool get rotateGesturesEnabled => params.rotateGesturesEnabled; + + /// True if the map view should respond to scroll gestures. + bool get scrollGesturesEnabled => params.scrollGesturesEnabled; + + /// True if the map view should show zoom controls. This includes two buttons + /// to zoom in and zoom out. The default value is to show zoom controls. + /// + /// This is only supported on Android. And this field is silently ignored on iOS. + bool get zoomControlsEnabled => params.zoomControlsEnabled; + + /// True if the map view should respond to zoom gestures. + bool get zoomGesturesEnabled => params.zoomGesturesEnabled; + + /// True if the map view should respond to tilt gestures. + bool get tiltGesturesEnabled => params.tiltGesturesEnabled; + + /// Padding to be set on map. See https://developers.google.com/maps/documentation/android-sdk/map#map_padding for more details. + EdgeInsets get padding => params.padding; + + /// Markers to be placed on the map. + Set get markers => params.markers; + + /// Polygons to be placed on the map. + Set get polygons => params.polygons; + + /// Polylines to be placed on the map. + Set get polylines => params.polylines; + + /// Circles to be placed on the map. + Set get circles => params.circles; + + /// Called when the camera starts moving. + /// + /// This can be initiated by the following: + /// 1. Non-gesture animation initiated in response to user actions. + /// For example: zoom buttons, my location button, or marker clicks. + /// 2. Programmatically initiated animation. + /// 3. Camera motion initiated in response to user gestures on the map. + /// For example: pan, tilt, pinch to zoom, or rotate. + VoidCallback? get onCameraMoveStarted => params.onCameraMoveStarted; + + /// Called repeatedly as the camera continues to move after an + /// onCameraMoveStarted call. + /// + /// This may be called as often as once every frame and should + /// not perform expensive operations. + CameraPositionCallback? get onCameraMove => params.onCameraMove; + + /// Called when camera movement has ended, there are no pending + /// animations and the user has stopped interacting with the map. + VoidCallback? get onCameraIdle => params.onCameraIdle; + + /// Called every time a [GoogleMap] is tapped. + ArgumentCallback? get onTap => params.onTap; + + /// Called every time a [GoogleMap] is long pressed. + ArgumentCallback? get onLongPress => params.onLongPress; + + /// True if a "My Location" layer should be shown on the map. + /// + /// This layer includes a location indicator at the current device location, + /// as well as a My Location button. + /// * The indicator is a small blue dot if the device is stationary, or a + /// chevron if the device is moving. + /// * The My Location button animates to focus on the user's current location + /// if the user's location is currently known. + /// + /// Enabling this feature requires adding location permissions to both native + /// platforms of your app. + /// * On Android add either + /// `` + /// or `` + /// to your `AndroidManifest.xml` file. `ACCESS_COARSE_LOCATION` returns a + /// location with an accuracy approximately equivalent to a city block, while + /// `ACCESS_FINE_LOCATION` returns as precise a location as possible, although + /// it consumes more battery power. You will also need to request these + /// permissions during run-time. If they are not granted, the My Location + /// feature will fail silently. + /// * On iOS add a `NSLocationWhenInUseUsageDescription` key to your + /// `Info.plist` file. This will automatically prompt the user for permissions + /// when the map tries to turn on the My Location layer. + bool get myLocationEnabled => params.myLocationEnabled; + + /// Enables or disables the my-location button. + /// + /// The my-location button causes the camera to move such that the user's + /// location is in the center of the map. If the button is enabled, it is + /// only shown when the my-location layer is enabled. + /// + /// By default, the my-location button is enabled (and hence shown when the + /// my-location layer is enabled). + /// + /// See also: + /// * [myLocationEnabled] parameter. + bool get myLocationButtonEnabled => params.myLocationButtonEnabled; + + /// Enables or disables the traffic layer of the map + bool get trafficEnabled => params.trafficEnabled; + + /// Which gestures should be consumed by the map. + /// + /// It is possible for other gesture recognizers to be competing with the map on pointer + /// events, e.g if the map is inside a [ListView] the [ListView] will want to handle + /// vertical drags. The map will claim gestures that are recognized by any of the + /// recognizers on this list. + /// + /// When this set is empty, the map will only handle pointer events for gestures that + /// were not claimed by any other gesture recognizer. + Set> get gestureRecognizers => + params.gestureRecognizers; + @override + _PlatformMapState createState() => _PlatformMapState(); +} + +class _PlatformMapState extends State<_PlatformMap> { + @override + Widget build(BuildContext context) { + return google_map_original.GoogleMap( + initialCameraPosition: + widget.initialCameraPosition.googleMapsCameraPosition, + compassEnabled: widget.compassEnabled, + mapType: _getGoogleMapType(), + padding: widget.padding, + markers: widget.markers.googleMapsMarkerSet, + polylines: widget.polylines.googleMapsPolylineSet, + polygons: widget.polygons.googleMapsPolygonSet, + circles: widget.circles.googleMapsCircleSet, + gestureRecognizers: widget.gestureRecognizers, + onCameraIdle: widget.onCameraIdle, + myLocationButtonEnabled: widget.myLocationButtonEnabled, + myLocationEnabled: widget.myLocationEnabled, + onCameraMoveStarted: widget.onCameraMoveStarted, + tiltGesturesEnabled: widget.tiltGesturesEnabled, + rotateGesturesEnabled: widget.rotateGesturesEnabled, + zoomControlsEnabled: widget.zoomControlsEnabled, + zoomGesturesEnabled: widget.zoomGesturesEnabled, + scrollGesturesEnabled: widget.scrollGesturesEnabled, + onMapCreated: _onMapCreated, + onCameraMove: _onCameraMove, + onTap: _onTap, + onLongPress: _onLongPress, + trafficEnabled: widget.trafficEnabled, + minMaxZoomPreference: + widget.minMaxZoomPreference.googleMapsZoomPreference, + ); + } + + void _onMapCreated(google_map_original.GoogleMapController controller) { + widget.onMapCreated?.call(controller.platformMapController); + } + + void _onCameraMove(google_maps.CameraPosition cameraPosition) { + widget.onCameraMove?.call(cameraPosition.platformCameraPosition); + } + + void _onTap(google_maps.LatLng position) { + widget.onTap?.call(position.platformLatLng); + } + + void _onLongPress(google_maps.LatLng position) { + widget.onLongPress?.call(position.platformLatLng); + } + + google_maps.MapType _getGoogleMapType() { + switch (widget.mapType) { + case MapType.normal: + return google_maps.MapType.normal; + case MapType.satellite: + return google_maps.MapType.satellite; + case MapType.hybrid: + return google_maps.MapType.hybrid; + } + } +} diff --git a/packages/platform_maps_flutter_google_android/lib/src/mapper_extensions.dart b/packages/platform_maps_flutter_google_android/lib/src/mapper_extensions.dart new file mode 100644 index 0000000..3cf91cd --- /dev/null +++ b/packages/platform_maps_flutter_google_android/lib/src/mapper_extensions.dart @@ -0,0 +1,243 @@ +import 'dart:ui'; + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' + as google_maps; +import 'package:platform_maps_flutter_google_android/src/google_maps_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_google_android/src/google_maps_platform_controller.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +import 'package:platform_maps_flutter_google_android/src/google_map_original.dart' + as google_map_original; + +extension GoogleMapsLatLngMapper on google_maps.LatLng { + LatLng get platformLatLng { + return LatLng(latitude, longitude); + } +} + +extension LatLngMapper on LatLng { + google_maps.LatLng get googleMapsLatLng { + return google_maps.LatLng(latitude, longitude); + } +} + +extension on List { + List get googleLatLngList { + return map((latLng) => latLng.googleMapsLatLng).toList(); + } +} + +extension LatLngBoundsMapper on LatLngBounds { + google_maps.LatLngBounds get googleMapsLatLngBounds { + return google_maps.LatLngBounds( + southwest: southwest.googleMapsLatLng, + northeast: northeast.googleMapsLatLng, + ); + } +} + +extension GoogleMapsLatLngBoundsMapper on google_maps.LatLngBounds { + LatLngBounds get platformLatLngBounds { + return LatLngBounds( + southwest: southwest.platformLatLng, + northeast: northeast.platformLatLng, + ); + } +} + +extension MarkersMapper on Set { + Set get googleMapsMarkerSet { + return map((marker) => marker.googleMapsMarker).toSet(); + } +} + +extension on Marker { + google_maps.Marker get googleMapsMarker { + return google_maps.Marker( + markerId: markerId.googleMapsMarkerId, + alpha: alpha, + anchor: anchor, + draggable: draggable, + infoWindow: infoWindow.googleMapsInfoWindow, + onTap: onTap, + icon: (icon as GoogleMapsBitmapDescriptor?)?.descriptor ?? + google_maps.BitmapDescriptor.defaultMarker, + visible: visible, + onDragEnd: onDragEnd != null + ? (google_maps.LatLng latLng) => + _onGoogleMarkerDragEnd(latLng, onDragEnd) + : null, + position: position.googleMapsLatLng, + ); + } + + static _onGoogleMarkerDragEnd( + google_maps.LatLng latLng, + Function? onDragEnd, + ) { + onDragEnd?.call(latLng.platformLatLng); + } +} + +extension MarkerIdMapper on MarkerId { + google_maps.MarkerId get googleMapsMarkerId { + return google_maps.MarkerId(value); + } +} + +extension on InfoWindow { + google_maps.InfoWindow get googleMapsInfoWindow => google_maps.InfoWindow( + anchor: anchor ?? const Offset(0, 0), + onTap: onTap, + snippet: snippet, + title: title, + ); +} + +extension CameraMapper on CameraPosition { + google_maps.CameraPosition get googleMapsCameraPosition { + return google_maps.CameraPosition( + target: target.googleMapsLatLng, + bearing: bearing, + tilt: tilt, + zoom: zoom, + ); + } +} + +extension GoogleMapsCameraMapper on google_maps.CameraPosition { + CameraPosition get platformCameraPosition { + return CameraPosition( + target: target.platformLatLng, + bearing: bearing, + tilt: tilt, + zoom: zoom, + ); + } +} + +extension PolylinesMapper on Set { + Set get googleMapsPolylineSet { + return map((polyline) => polyline.googleMapsPolyline).toSet(); + } +} + +extension on Polyline { + google_maps.Polyline get googleMapsPolyline { + return google_maps.Polyline( + polylineId: polylineId.googleMapsPolylineId, + color: color, + consumeTapEvents: consumeTapEvents, + startCap: polylineCap.googlePolylineCap, + endCap: polylineCap.googlePolylineCap, + jointType: jointType.googleMapsJointType, + onTap: onTap, + patterns: patterns.googleMapsPatternItemList, + points: points.googleLatLngList, + visible: visible, + width: width, + ); + } +} + +extension on PolylineId { + google_maps.PolylineId get googleMapsPolylineId { + return google_maps.PolylineId(value); + } +} + +extension on Cap { + static const Map googleMapsCaps = { + Cap.buttCap: google_maps.Cap.buttCap, + Cap.roundCap: google_maps.Cap.roundCap, + Cap.squareCap: google_maps.Cap.squareCap, + }; + + google_maps.Cap get googlePolylineCap { + return googleMapsCaps[this]!; + } +} + +extension on JointType { + static const List googleMapsJointTypes = [ + google_maps.JointType.mitered, + google_maps.JointType.bevel, + google_maps.JointType.round, + ]; + + google_maps.JointType get googleMapsJointType { + return googleMapsJointTypes[value]; + } +} + +extension on List { + List get googleMapsPatternItemList { + return map((patternItem) => patternItem.googleMapsPatternItem).toList(); + } +} + +extension on PatternItem { + google_maps.PatternItem get googleMapsPatternItem { + return switch (this) { + DotPatternItem() => google_maps.PatternItem.dot, + DashPatternItem dash => google_maps.PatternItem.dash(dash.length), + GapPatternItem gap => google_maps.PatternItem.gap(gap.length), + }; + } +} + +extension PolygonMapper on Set { + Set get googleMapsPolygonSet { + return map((polygon) => polygon.googleMapsPolygon).toSet(); + } +} + +extension on Polygon { + google_maps.Polygon get googleMapsPolygon { + return google_maps.Polygon( + polygonId: google_maps.PolygonId(polygonId.value), + consumeTapEvents: consumeTapEvents, + fillColor: fillColor, + onTap: onTap, + points: points.googleLatLngList, + strokeColor: strokeColor, + strokeWidth: strokeWidth, + visible: visible, + ); + } +} + +extension CircleMapper on Set { + Set get googleMapsCircleSet { + return map((circle) => circle.googleMapsCircle).toSet(); + } +} + +extension on Circle { + google_maps.Circle get googleMapsCircle { + return google_maps.Circle( + circleId: google_maps.CircleId(circleId.value), + consumeTapEvents: consumeTapEvents, + fillColor: fillColor, + onTap: onTap, + center: center.googleMapsLatLng, + radius: radius, + strokeColor: strokeColor, + strokeWidth: strokeWidth, + visible: visible, + ); + } +} + +extension ZoomMapper on MinMaxZoomPreference { + google_maps.MinMaxZoomPreference get googleMapsZoomPreference { + return google_maps.MinMaxZoomPreference(minZoom, maxZoom); + } +} + +extension GoogleMapsControllerMappers + on google_map_original.GoogleMapController { + PlatformMapController get platformMapController { + return PlatformMapController(GoogleMapsPlatformController(this)); + } +} diff --git a/packages/platform_maps_flutter_google_android/pubspec.yaml b/packages/platform_maps_flutter_google_android/pubspec.yaml new file mode 100644 index 0000000..1f1c4e1 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/pubspec.yaml @@ -0,0 +1,31 @@ +name: platform_maps_flutter_google_android +description: Android Google Maps implementation for the platform_maps_flutter plugin. +version: 1.0.0-beta +homepage: https://github.com/albert-heijn-technology +repository: https://github.com/albert-heijn-technology/platform_maps_flutter/tree/master/packages/platform_maps_flutter_google_android +issue_tracker: https://github.com/albert-heijn-technology/platform_maps_flutter/issues + +environment: + sdk: '>=3.2.4 <4.0.0' + flutter: ">=1.17.0" + +flutter: + plugin: + implements: platform_maps_flutter + platforms: + android: + dartPluginClass: PlatformMapsGoogleAndroid + fileName: platform_maps_flutter_google.dart + +dependencies: + flutter: + sdk: flutter + google_maps_flutter_android: ^2.7.0 + google_maps_flutter_platform_interface: ^2.6.0 + platform_maps_flutter_platform_interface: ^1.0.0-beta + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + diff --git a/packages/platform_maps_flutter_google_android/test/platform_maps_flutter_google_android_test.dart b/packages/platform_maps_flutter_google_android/test/platform_maps_flutter_google_android_test.dart new file mode 100644 index 0000000..bcf2454 --- /dev/null +++ b/packages/platform_maps_flutter_google_android/test/platform_maps_flutter_google_android_test.dart @@ -0,0 +1,43 @@ +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:platform_maps_flutter_google_android/platform_maps_flutter_google_android.dart'; +import 'package:platform_maps_flutter_google_android/src/google_maps_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_google_android/src/google_maps_camera_update.dart'; +import 'package:platform_maps_flutter_google_android/src/google_maps_widget.dart'; + +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +void main() { + test('Verify widget generation', () { + PlatformMapsGoogleAndroid.registerWith(); + + final widget = PlatformMapsPlatformWidget( + const PlatformMapsPlatformWidgetCreationParams( + initialCameraPosition: CameraPosition(target: LatLng(0, 0)), + ), + ); + expect(widget, isA()); + }); + + test('Verify bitmap descriptor generation', () { + PlatformMapsGoogleAndroid.registerWith(); + + final platformBitmapDescriptor = PlatformBitmapDescriptor(); + expect(platformBitmapDescriptor, isA()); + + final bitmapDescriptor = + platformBitmapDescriptor.fromBytes(Uint8List.fromList([1, 2])); + expect(bitmapDescriptor, isA()); + }); + + test('Verify camera update generation', () { + PlatformMapsGoogleAndroid.registerWith(); + + final platformCameraUpdate = PlatformCameraUpdate(); + expect(platformCameraUpdate, isA()); + + final cameraUpdate = platformCameraUpdate.zoomIn(); + expect(cameraUpdate, isA()); + }); +} diff --git a/packages/platform_maps_flutter_platform_interface/.gitignore b/packages/platform_maps_flutter_platform_interface/.gitignore new file mode 100644 index 0000000..3cceda5 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/packages/platform_maps_flutter_platform_interface/CHANGELOG.md b/packages/platform_maps_flutter_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..cf84db1 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0-beta + +* Initial version, based on platform_maps_flutter 1.0.2 diff --git a/packages/platform_maps_flutter_platform_interface/LICENSE b/packages/platform_maps_flutter_platform_interface/LICENSE new file mode 100644 index 0000000..67f5463 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright (c) 2024, Albert Heijn Technology +Copyright (c) 2019, Luis Thein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/platform_maps_flutter_platform_interface/README.md b/packages/platform_maps_flutter_platform_interface/README.md new file mode 100644 index 0000000..7d5ca38 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/README.md @@ -0,0 +1,23 @@ +# platform_maps_flutter_platform_interface + +A common platform interface for the `platform_maps_flutter` plugin. + +This interface allows platform-specific implementations of the `platform_maps_flutter` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `platform_maps_flutter`, extend +`PlatformMapsPlatform` with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`PlatformMapsPlatform` by calling +`PlatformMapsPlatform.instance = MyCustomPlatformMapsPlatform()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. diff --git a/packages/platform_maps_flutter_platform_interface/analysis_options.yaml b/packages/platform_maps_flutter_platform_interface/analysis_options.yaml new file mode 100644 index 0000000..830c17c --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/analysis_options.yaml @@ -0,0 +1,8 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - always_declare_return_types + - avoid_annotating_with_dynamic + - avoid_dynamic_calls + - require_trailing_commas diff --git a/packages/platform_maps_flutter_platform_interface/lib/platform_maps_flutter_platform_interface.dart b/packages/platform_maps_flutter_platform_interface/lib/platform_maps_flutter_platform_interface.dart new file mode 100644 index 0000000..134b9d4 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/platform_maps_flutter_platform_interface.dart @@ -0,0 +1,7 @@ +export 'src/platform_maps_platform_widget.dart'; +export 'src/platform_maps_platform_controller.dart'; +export 'src/platform_maps_platform.dart'; +export 'src/types.dart'; +export 'src/platform_bitmap_descriptor.dart'; +export 'src/platform_camera_update.dart'; +export 'src/platform_map.dart'; diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/platform_bitmap_descriptor.dart b/packages/platform_maps_flutter_platform_interface/lib/src/platform_bitmap_descriptor.dart new file mode 100644 index 0000000..6f3c61e --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/platform_bitmap_descriptor.dart @@ -0,0 +1,41 @@ +import 'dart:typed_data'; + +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +abstract class PlatformBitmapDescriptor extends PlatformInterface { + /// Creates a new [PlatformBitmapDescriptor] + factory PlatformBitmapDescriptor() { + assert( + PlatformMapsPlatform.instance != null, + 'A platform implementation for `platform_maps_flutter` has not been set. Please ' + 'ensure that an implementation of `PlatformMapsPlatform` has been set to ' + '`PlatformMapsPlatform.instance` before use. For unit testing, ' + '`PlatformMapsPlatform.instance` can be set with your own test implementation.', + ); + final PlatformBitmapDescriptor bitmapDescriptorDelegate = + PlatformMapsPlatform.instance!.createBitmapDescriptor(); + PlatformInterface.verify(bitmapDescriptorDelegate, _token); + return bitmapDescriptorDelegate; + } + + /// Used by the platform implementation to create a new + /// [PlatformBitmapDescriptor]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformBitmapDescriptor.implementation() : super(token: _token); + + static final Object _token = Object(); + + Future fromAssetImage( + ImageConfiguration configuration, + String assetName, { + AssetBundle? bundle, + String? package, + }); + + BitmapDescriptor fromBytes(Uint8List byteData); +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/platform_camera_update.dart b/packages/platform_maps_flutter_platform_interface/lib/src/platform_camera_update.dart new file mode 100644 index 0000000..fc00ffd --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/platform_camera_update.dart @@ -0,0 +1,34 @@ +import 'package:flutter/foundation.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +abstract class PlatformCameraUpdate extends PlatformInterface { + /// Creates a new [PlatformCameraUpdate] + factory PlatformCameraUpdate() { + assert( + PlatformMapsPlatform.instance != null, + 'A platform implementation for `platform_maps_flutter` has not been set. Please ' + 'ensure that an implementation of `PlatformMapsPlatform` has been set to ' + '`PlatformMapsPlatform.instance` before use. For unit testing, ' + '`PlatformMapsPlatform.instance` can be set with your own test implementation.', + ); + final PlatformCameraUpdate cameraUpdateDelegate = + PlatformMapsPlatform.instance!.createPlatformCameraUpdate(); + PlatformInterface.verify(cameraUpdateDelegate, _token); + return cameraUpdateDelegate; + } + + @protected + PlatformCameraUpdate.implementation() : super(token: _token); + + static final Object _token = Object(); + + CameraUpdate newCameraPosition(CameraPosition cameraPosition); + CameraUpdate newLatLng(LatLng latLng); + CameraUpdate newLatLngZoom(LatLng latLng, double zoom); + CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding); + CameraUpdate zoomBy(double amount); + CameraUpdate zoomIn(); + CameraUpdate zoomOut(); + CameraUpdate zoomTo(double zoom); +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/platform_map.dart b/packages/platform_maps_flutter_platform_interface/lib/src/platform_map.dart new file mode 100644 index 0000000..ba2aa54 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/platform_map.dart @@ -0,0 +1,198 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class PlatformMap extends StatelessWidget { + PlatformMap({ + super.key, + required this.initialCameraPosition, + this.onMapCreated, + this.gestureRecognizers = const >{}, + this.compassEnabled = true, + this.mapType = MapType.normal, + this.minMaxZoomPreference = MinMaxZoomPreference.unbounded, + this.rotateGesturesEnabled = true, + this.scrollGesturesEnabled = true, + this.zoomControlsEnabled = true, + this.zoomGesturesEnabled = true, + this.tiltGesturesEnabled = true, + this.myLocationEnabled = false, + this.myLocationButtonEnabled = false, + this.padding = const EdgeInsets.all(0), + this.trafficEnabled = false, + this.markers = const {}, + this.polygons = const {}, + this.polylines = const {}, + this.circles = const {}, + this.onCameraMoveStarted, + this.onCameraMove, + this.onCameraIdle, + this.onTap, + this.onLongPress, + }) : _platform = PlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams( + compassEnabled: compassEnabled, + gestureRecognizers: gestureRecognizers, + initialCameraPosition: initialCameraPosition, + mapType: mapType, + minMaxZoomPreference: minMaxZoomPreference, + myLocationButtonEnabled: myLocationButtonEnabled, + myLocationEnabled: myLocationEnabled, + onCameraIdle: onCameraIdle, + onCameraMove: onCameraMove, + onCameraMoveStarted: onCameraMoveStarted, + onLongPress: onLongPress, + onTap: onTap, + padding: padding, + polylines: polylines, + polygons: polygons, + circles: circles, + markers: markers, + rotateGesturesEnabled: rotateGesturesEnabled, + scrollGesturesEnabled: scrollGesturesEnabled, + tiltGesturesEnabled: tiltGesturesEnabled, + trafficEnabled: trafficEnabled, + zoomControlsEnabled: zoomControlsEnabled, + zoomGesturesEnabled: zoomGesturesEnabled, + ), + ); + + /// Callback method for when the map is ready to be used. + /// + /// Used to receive a [GoogleMapController] for this [GoogleMap]. + final MapCreatedCallback? onMapCreated; + + /// The initial position of the map's camera. + final CameraPosition initialCameraPosition; + + /// True if the map should show a compass when rotated. + final bool compassEnabled; + + /// Type of map tiles to be rendered. + final MapType mapType; + + /// Preferred bounds for the camera zoom level. + /// + /// Actual bounds depend on map data and device. + final MinMaxZoomPreference minMaxZoomPreference; + + /// True if the map view should respond to rotate gestures. + final bool rotateGesturesEnabled; + + /// True if the map view should respond to scroll gestures. + final bool scrollGesturesEnabled; + + /// True if the map view should show zoom controls. This includes two buttons + /// to zoom in and zoom out. The default value is to show zoom controls. + /// + /// This is only supported on Android. And this field is silently ignored on iOS. + final bool zoomControlsEnabled; + + /// True if the map view should respond to zoom gestures. + final bool zoomGesturesEnabled; + + /// True if the map view should respond to tilt gestures. + final bool tiltGesturesEnabled; + + /// Padding to be set on map. See https://developers.google.com/maps/documentation/android-sdk/map#map_padding for more details. + final EdgeInsets padding; + + /// Markers to be placed on the map. + final Set markers; + + /// Polygons to be placed on the map. + final Set polygons; + + /// Polylines to be placed on the map. + final Set polylines; + + /// Circles to be placed on the map. + final Set circles; + + /// Called when the camera starts moving. + /// + /// This can be initiated by the following: + /// 1. Non-gesture animation initiated in response to user actions. + /// For example: zoom buttons, my location button, or marker clicks. + /// 2. Programmatically initiated animation. + /// 3. Camera motion initiated in response to user gestures on the map. + /// For example: pan, tilt, pinch to zoom, or rotate. + final VoidCallback? onCameraMoveStarted; + + /// Called repeatedly as the camera continues to move after an + /// onCameraMoveStarted call. + /// + /// This may be called as often as once every frame and should + /// not perform expensive operations. + final CameraPositionCallback? onCameraMove; + + /// Called when camera movement has ended, there are no pending + /// animations and the user has stopped interacting with the map. + final VoidCallback? onCameraIdle; + + /// Called every time a [GoogleMap] is tapped. + final ArgumentCallback? onTap; + + /// Called every time a [GoogleMap] is long pressed. + final ArgumentCallback? onLongPress; + + /// True if a "My Location" layer should be shown on the map. + /// + /// This layer includes a location indicator at the current device location, + /// as well as a My Location button. + /// * The indicator is a small blue dot if the device is stationary, or a + /// chevron if the device is moving. + /// * The My Location button animates to focus on the user's current location + /// if the user's location is currently known. + /// + /// Enabling this feature requires adding location permissions to both native + /// platforms of your app. + /// * On Android add either + /// `` + /// or `` + /// to your `AndroidManifest.xml` file. `ACCESS_COARSE_LOCATION` returns a + /// location with an accuracy approximately equivalent to a city block, while + /// `ACCESS_FINE_LOCATION` returns as precise a location as possible, although + /// it consumes more battery power. You will also need to request these + /// permissions during run-time. If they are not granted, the My Location + /// feature will fail silently. + /// * On iOS add a `NSLocationWhenInUseUsageDescription` key to your + /// `Info.plist` file. This will automatically prompt the user for permissions + /// when the map tries to turn on the My Location layer. + final bool myLocationEnabled; + + /// Enables or disables the my-location button. + /// + /// The my-location button causes the camera to move such that the user's + /// location is in the center of the map. If the button is enabled, it is + /// only shown when the my-location layer is enabled. + /// + /// By default, the my-location button is enabled (and hence shown when the + /// my-location layer is enabled). + /// + /// See also: + /// * [myLocationEnabled] parameter. + final bool myLocationButtonEnabled; + + /// Enables or disables the traffic layer of the map + final bool trafficEnabled; + + /// Which gestures should be consumed by the map. + /// + /// It is possible for other gesture recognizers to be competing with the map on pointer + /// events, e.g if the map is inside a [ListView] the [ListView] will want to handle + /// vertical drags. The map will claim gestures that are recognized by any of the + /// recognizers on this list. + /// + /// When this set is empty, the map will only handle pointer events for gestures that + /// were not claimed by any other gesture recognizer. + final Set> gestureRecognizers; + + final PlatformMapsPlatformWidget _platform; + + @override + Widget build(BuildContext context) { + return _platform.build(context); + } +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform.dart b/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform.dart new file mode 100644 index 0000000..70e15c8 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform.dart @@ -0,0 +1,57 @@ +import 'package:platform_maps_flutter_platform_interface/src/platform_bitmap_descriptor.dart'; +import 'package:platform_maps_flutter_platform_interface/src/platform_camera_update.dart'; +import 'package:platform_maps_flutter_platform_interface/src/platform_maps_platform_widget.dart'; +import 'package:platform_maps_flutter_platform_interface/src/types.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +/// Interface for a platform implementation of a PlatformMaps. +abstract class PlatformMapsPlatform extends PlatformInterface { + /// Creates a new [PlatformMapsPlatform]. + PlatformMapsPlatform() : super(token: _token); + + static final Object _token = Object(); + + static PlatformMapsPlatform? _instance; + + /// The instance of [PlatformMapsPlatform] to use. + static PlatformMapsPlatform? get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [PlatformMapsPlatform] when they register themselves. + static set instance(PlatformMapsPlatform? instance) { + if (instance == null) { + throw AssertionError( + 'Platform interfaces can only be set to a non-null instance', + ); + } + + PlatformInterface.verify(instance, _token); + _instance = instance; + } + + /// Create a new [PlatformPlatformMapsWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [PlatformMap] in `platform_maps_flutter` instead. + PlatformMapsPlatformWidget createPlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformMapsPlatformWidget is not implemented on the current platform.', + ); + } + + /// Create a new [PlatformBitmapDescriptor]. + /// This function should only be called by the app-facing package. + PlatformBitmapDescriptor createBitmapDescriptor() { + throw UnimplementedError( + 'createBitmapDescriptor is not implemented on the current platform.', + ); + } + + PlatformCameraUpdate createPlatformCameraUpdate() { + throw UnimplementedError( + 'createCameraUpdate is not implemented on the current platform.', + ); + } +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform_controller.dart b/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform_controller.dart new file mode 100644 index 0000000..0cc91d7 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform_controller.dart @@ -0,0 +1,12 @@ +import 'package:flutter/foundation.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +abstract class PlatformMapsPlatformController { + Future showMarkerInfoWindow(MarkerId markerId); + Future hideMarkerInfoWindow(MarkerId markerId); + Future isMarkerInfoWindowShown(MarkerId markerId); + Future animateCamera(CameraUpdate cameraUpdate); + Future moveCamera(CameraUpdate cameraUpdate); + Future getVisibleRegion(); + Future takeSnapshot(); +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform_widget.dart b/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform_widget.dart new file mode 100644 index 0000000..f6260dd --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/platform_maps_platform_widget.dart @@ -0,0 +1,41 @@ +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_platform_interface/src/platform_maps_platform.dart'; +import 'package:platform_maps_flutter_platform_interface/src/types.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +abstract class PlatformMapsPlatformWidget extends PlatformInterface { + /// Creates a new [PlatformMapsPlatformWidget] + factory PlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams params, + ) { + assert( + PlatformMapsPlatform.instance != null, + 'A platform implementation for `platform_maps_flutter` has not been set. Please ' + 'ensure that an implementation of `PlatformMapsPlatform` has been set to ' + '`PlatformMapsPlatform.instance` before use. For unit testing, ' + '`PlatformMapsPlatform.instance` can be set with your own test implementation.', + ); + final PlatformMapsPlatformWidget platformWidgetDelegate = + PlatformMapsPlatform.instance!.createPlatformMapsPlatformWidget(params); + PlatformInterface.verify(platformWidgetDelegate, _token); + return platformWidgetDelegate; + } + + /// Used by the platform implementation to create a new + /// [PlatformMapsPlatformWidget]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformMapsPlatformWidget.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformMapsPlatformWidget]. + final PlatformMapsPlatformWidgetCreationParams params; + + /// Builds a new WebView. + /// + /// Returns a Widget tree that embeds the created web view. + Widget build(BuildContext context); +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types.dart new file mode 100644 index 0000000..66cd200 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types.dart @@ -0,0 +1,17 @@ +export 'types/bitmap.dart'; +export 'types/callbacks.dart'; +export 'types/camera_position.dart'; +export 'types/camera_update.dart'; +export 'types/cap.dart'; +export 'types/circle.dart'; +export 'types/controller.dart'; +export 'types/info_window.dart'; +export 'types/joint_type.dart'; +export 'types/lat_lng.dart'; +export 'types/map_type.dart'; +export 'types/marker.dart'; +export 'types/min_max_zoom_preference.dart'; +export 'types/platform_maps_platform_widget_creation_params.dart'; +export 'types/pattern_item.dart'; +export 'types/polygon.dart'; +export 'types/polyline.dart'; diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/bitmap.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/bitmap.dart new file mode 100644 index 0000000..c712c23 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/bitmap.dart @@ -0,0 +1,36 @@ +import 'dart:typed_data'; + +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_platform_interface/src/platform_bitmap_descriptor.dart'; + +/// 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. + +abstract class BitmapDescriptor { + /// Creates a [BitmapDescriptor] from an asset image. + /// Asset images in flutter are stored per: https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets + /// + /// This method takes into consideration various asset resolutions and scales the images to the right resolution depending on the dpi. + /// + /// Don't forget to rebuild the map with the new Icons if it was already build. + static Future fromAssetImage( + ImageConfiguration configuration, + String assetName, { + AssetBundle? bundle, + String? package, + }) async { + return PlatformBitmapDescriptor().fromAssetImage( + configuration, + assetName, + bundle: bundle, + package: package, + ); + } + + /// Creates a BitmapDescriptor using an array of bytes that must be encoded + /// as PNG. + static BitmapDescriptor fromBytes(Uint8List byteData) { + return PlatformBitmapDescriptor().fromBytes(byteData); + } +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/callbacks.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/callbacks.dart new file mode 100644 index 0000000..4c4bb51 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/callbacks.dart @@ -0,0 +1,2 @@ +/// Callback function taking a single argument. +typedef ArgumentCallback = void Function(T argument); diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/camera_position.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/camera_position.dart new file mode 100644 index 0000000..bfbb1f7 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/camera_position.dart @@ -0,0 +1,39 @@ +import 'package:platform_maps_flutter_platform_interface/src/types.dart'; + +/// The position of the map "camera", the view point from which the world is +/// shown in the map view. Aggregates the camera's [target] geographical +/// location, its [zoom] level, [pitch] angle, and [heading]. +class CameraPosition { + const CameraPosition({ + required this.target, + this.bearing = 0.0, + this.tilt = 0.0, + this.zoom = 0, + }); + + /// The camera's bearing in degrees, measured clockwise from north. + /// + /// A bearing of 0.0, the default, means the camera points north. + /// A bearing of 90.0 means the camera points east. + final double bearing; + + /// The geographical location that the camera is pointing at. + final LatLng target; + + // In degrees where 0 is looking straight down. Pitch may be clamped to an appropriate value. + final double tilt; + + /// The zoom level of the camera. + /// + /// A zoom of 0.0, the default, means the screen width of the world is 256. + /// Adding 1.0 to the zoom level doubles the screen width of the map. So at + /// zoom level 3.0, the screen width of the world is 2³x256=2048. + /// + /// Larger zoom levels thus means the camera is placed closer to the surface + /// of the Earth, revealing more detail in a narrower geographical region. + /// + /// The supported zoom level range depends on the map data and device. Values + /// beyond the supported range are allowed, but on applying them to a map they + /// will be silently clamped to the supported range. + final double zoom; +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/camera_update.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/camera_update.dart new file mode 100644 index 0000000..2f0e0a0 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/camera_update.dart @@ -0,0 +1,47 @@ +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +abstract class CameraUpdate { + const CameraUpdate(); + + /// Returns a camera update that moves the camera to the specified position. + static CameraUpdate newCameraPosition(CameraPosition cameraPosition) => + PlatformCameraUpdate().newCameraPosition(cameraPosition); + + /// Returns a camera update that moves the camera target to the specified geographical location. + static CameraUpdate newLatLng(LatLng latLng) => + PlatformCameraUpdate().newLatLng(latLng); + + /// Returns a camera update that moves the camera target to the specified geographical location and zoom level. + static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) => + PlatformCameraUpdate().newLatLngZoom(latLng, zoom); + + /// Returns a camera update that transforms the camera so that + /// the specified geographical bounding box is centered in the map + /// view at the greatest possible zoom level. + /// A non-zero [padding] insets the bounding box from the map view's edges. + /// The camera's new tilt and bearing will both be 0.0. + static CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) => + PlatformCameraUpdate().newLatLngBounds(bounds, padding); + + /// Returns a camera update that modifies the camera zoom level by the specified amount. + /// The optional [focus] is a screen point whose underlying geographical location + /// should be invariant, if possible, by the movement. + static CameraUpdate zoomBy(double amount) => + PlatformCameraUpdate().zoomBy(amount); + + /// Returns a camera update that zooms the camera in, + /// bringing the camera closer to the surface of the Earth. + /// + /// Equivalent to the result of calling zoomBy(1.0). + static CameraUpdate zoomIn() => PlatformCameraUpdate().zoomIn(); + + /// Returns a camera update that zooms the camera out, + /// bringing the camera further away from the surface of the Earth. + /// + /// Equivalent to the result of calling zoomBy(-1.0). + static CameraUpdate zoomOut() => PlatformCameraUpdate().zoomOut(); + + /// Returns a camera update that sets the camera zoom level. + static CameraUpdate zoomTo(double zoom) => + PlatformCameraUpdate().zoomTo(zoom); +} diff --git a/lib/src/cap.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/cap.dart similarity index 50% rename from lib/src/cap.dart rename to packages/platform_maps_flutter_platform_interface/lib/src/types/cap.dart index 5d7385f..86e0922 100644 --- a/lib/src/cap.dart +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/cap.dart @@ -1,5 +1,4 @@ // Copyright 2019 The Chromium Authors. All rights reserved. -part of '../platform_maps_flutter.dart'; enum Cap { /// Cap that is squared off exactly at the start or end vertex of a [Polyline] with solid stroke pattern, @@ -16,27 +15,3 @@ enum Cap { /// or end vertex of a [Polyline] with solid stroke pattern. squareCap, } - -/// Cap that can be applied at the start or end vertex of a [Polyline]. -@immutable -class _Cap { - static const Map googleMapsCaps = { - Cap.buttCap: google_maps.Cap.buttCap, - Cap.roundCap: google_maps.Cap.roundCap, - Cap.squareCap: google_maps.Cap.squareCap, - }; - - static const Map appleMapsCaps = { - Cap.buttCap: apple_maps.Cap.buttCap, - Cap.roundCap: apple_maps.Cap.roundCap, - Cap.squareCap: apple_maps.Cap.squareCap, - }; - - static google_maps.Cap googlePolylineCap(Cap cap) { - return googleMapsCaps[cap]!; - } - - static apple_maps.Cap applePolylineCap(Cap cap) { - return appleMapsCaps[cap]!; - } -} diff --git a/lib/src/circle.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/circle.dart similarity index 74% rename from lib/src/circle.dart rename to packages/platform_maps_flutter_platform_interface/lib/src/types/circle.dart index 25ddb31..9a8a2f7 100644 --- a/lib/src/circle.dart +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/circle.dart @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of '../platform_maps_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:platform_maps_flutter_platform_interface/src/types.dart'; /// Uniquely identifies a [Circle] among [PlatformMap] circles. /// @@ -125,46 +126,6 @@ class Circle { onTap == typedOther.onTap; } - static Set toGoogleMapsCircleSet(Set circles) { - List circles0 = []; - for (Circle circle in circles) { - circles0.add(circle.googleMapsCircle); - } - return Set.from(circles0); - } - - static Set toAppleMapsCircleSet(Set circles) { - List circles0 = []; - for (Circle circle in circles) { - circles0.add(circle.appleMapsCircle); - } - return Set.from(circles0); - } - - google_maps.Circle get googleMapsCircle => google_maps.Circle( - circleId: google_maps.CircleId(circleId.value), - consumeTapEvents: consumeTapEvents, - fillColor: fillColor, - onTap: onTap, - center: center.googleLatLng, - radius: radius, - strokeColor: strokeColor, - strokeWidth: strokeWidth, - visible: visible, - ); - - apple_maps.Circle get appleMapsCircle => apple_maps.Circle( - circleId: apple_maps.CircleId(circleId.value), - consumeTapEvents: consumeTapEvents, - fillColor: fillColor, - onTap: onTap, - center: center.appleLatLng, - radius: radius, - strokeColor: strokeColor, - strokeWidth: strokeWidth, - visible: visible, - ); - @override int get hashCode => circleId.hashCode; } diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/controller.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/controller.dart new file mode 100644 index 0000000..67e0f2f --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/controller.dart @@ -0,0 +1,63 @@ +import 'dart:typed_data'; + +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +class PlatformMapController { + final PlatformMapsPlatformController platformController; + + PlatformMapController(this.platformController); + + /// Programmatically show the Info Window for a [Marker]. + /// + /// The `markerId` must match one of the markers on the map. + /// An invalid `markerId` triggers an "Invalid markerId" error. + /// + /// * See also: + /// * [hideMarkerInfoWindow] to hide the Info Window. + /// * [isMarkerInfoWindowShown] to check if the Info Window is showing. + Future showMarkerInfoWindow(MarkerId markerId) => + platformController.showMarkerInfoWindow(markerId); + + /// Programmatically hide the Info Window for a [Marker]. + /// + /// The `markerId` must match one of the markers on the map. + /// An invalid `markerId` triggers an "Invalid markerId" error. + /// + /// * See also: + /// * [showMarkerInfoWindow] to show the Info Window. + /// * [isMarkerInfoWindowShown] to check if the Info Window is showing. + Future hideMarkerInfoWindow(MarkerId markerId) => + platformController.hideMarkerInfoWindow(markerId); + + /// Returns `true` when the [InfoWindow] is showing, `false` otherwise. + /// + /// The `markerId` must match one of the markers on the map. + /// An invalid `markerId` triggers an "Invalid markerId" error. + /// + /// * See also: + /// * [showMarkerInfoWindow] to show the Info Window. + /// * [hideMarkerInfoWindow] to hide the Info Window. + Future isMarkerInfoWindowShown(MarkerId markerId) => + platformController.isMarkerInfoWindowShown(markerId); + + /// Starts an animated change of the map camera position. + /// + /// The returned [Future] completes after the change has been started on the + /// platform side. + Future animateCamera(cameraUpdate) => + platformController.animateCamera(cameraUpdate); + + /// Changes the map camera position. + /// + /// The returned [Future] completes after the change has been made on the + /// platform side. + Future moveCamera(cameraUpdate) => + platformController.moveCamera(cameraUpdate); + + /// Return [LatLngBounds] defining the region that is visible in a map. + Future getVisibleRegion() => + platformController.getVisibleRegion(); + + /// Returns the image bytes of the map + Future takeSnapshot() => platformController.takeSnapshot(); +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/info_window.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/info_window.dart new file mode 100644 index 0000000..b64a647 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/info_window.dart @@ -0,0 +1,46 @@ +import 'dart:ui'; + +/// Text labels for a [Marker] info window. +class InfoWindow { + const InfoWindow({this.title, this.snippet, this.onTap, this.anchor}); + + /// Text labels specifying that no text is to be displayed. + static const InfoWindow noText = InfoWindow(); + + /// Text displayed in an info window when the user taps the marker. + /// + /// A null value means no title. + final String? title; + + /// Additional text displayed below the [title]. + /// + /// A null value means no additional text. + final String? snippet; + + /// The icon image point that will be the anchor of the info window when + /// displayed. + /// + /// The image point is specified in normalized coordinates: An anchor of + /// (0.0, 0.0) means the top left corner of the image. An anchor + /// of (1.0, 1.0) means the bottom right corner of the image. + final Offset? anchor; + + /// onTap callback for this [InfoWindow]. + final VoidCallback? onTap; + + /// Creates a new [InfoWindow] object whose values are the same as this instance, + /// unless overwritten by the specified parameters. + InfoWindow copyWith({ + String? titleParam, + String? snippetParam, + Offset? anchorParam, + VoidCallback? onTapParam, + }) { + return InfoWindow( + title: titleParam ?? title, + snippet: snippetParam ?? snippet, + anchor: anchorParam ?? anchor, + onTap: onTapParam ?? onTap, + ); + } +} diff --git a/lib/src/joint_type.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/joint_type.dart similarity index 56% rename from lib/src/joint_type.dart rename to packages/platform_maps_flutter_platform_interface/lib/src/types/joint_type.dart index a307dc6..7618935 100644 --- a/lib/src/joint_type.dart +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/joint_type.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of '../platform_maps_flutter.dart'; +import 'package:flutter/widgets.dart'; /// Joint types for [Polyline]. @immutable @@ -12,18 +12,6 @@ class JointType { /// The value representing the [JointType] on the sdk. final int value; - static const List googleMapsJointTypes = [ - google_maps.JointType.mitered, - google_maps.JointType.bevel, - google_maps.JointType.round, - ]; - - static const List appleMapsJointTypes = [ - apple_maps.JointType.mitered, - apple_maps.JointType.bevel, - apple_maps.JointType.round, - ]; - /// Mitered joint, with fixed pointed extrusion equal to half the stroke width on the outside of the joint. /// /// Constant Value: 0 @@ -38,12 +26,4 @@ class JointType { /// /// Constant Value: 2 static const JointType round = JointType._(2); - - static google_maps.JointType getGoogleMapsJointType(JointType jointType) { - return googleMapsJointTypes[jointType.value]; - } - - static apple_maps.JointType getAppleMapsJointType(JointType jointType) { - return appleMapsJointTypes[jointType.value]; - } } diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/lat_lng.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/lat_lng.dart new file mode 100644 index 0000000..549c084 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/lat_lng.dart @@ -0,0 +1,55 @@ +/// A pair of latitude and longitude coordinates, stored as degrees. +class LatLng { + /// Creates a geographical location specified in degrees [latitude] and + /// [longitude]. + /// + /// The latitude is clamped to the inclusive interval from -90.0 to +90.0. + /// + /// The longitude is normalized to the half-open interval from -180.0 + /// (inclusive) to +180.0 (exclusive) + const LatLng(double latitude, double longitude) + : latitude = + (latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude)), + longitude = (longitude + 180.0) % 360.0 - 180.0; + + /// The latitude in degrees between -90.0 and 90.0, both inclusive. + final double latitude; + + /// The longitude in degrees between -180.0 (inclusive) and 180.0 (exclusive). + final double longitude; + + @override + String toString() => '$runtimeType($latitude, $longitude)'; +} + +class LatLngBounds { + /// Creates geographical bounding box with the specified corners. + /// + /// The latitude of the southwest corner cannot be larger than the + /// latitude of the northeast corner. + LatLngBounds({required this.southwest, required this.northeast}) + : assert(southwest.latitude <= northeast.latitude); + + /// The southwest corner of the rectangle. + final LatLng southwest; + + /// The northeast corner of the rectangle. + final LatLng northeast; + + bool contains(LatLng point) { + return _containsLatitude(point.latitude) && + _containsLongitude(point.longitude); + } + + bool _containsLatitude(double lat) { + return (southwest.latitude <= lat) && (lat <= northeast.latitude); + } + + bool _containsLongitude(double lng) { + if (southwest.longitude <= northeast.longitude) { + return southwest.longitude <= lng && lng <= northeast.longitude; + } else { + return southwest.longitude <= lng || lng <= northeast.longitude; + } + } +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/map_type.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/map_type.dart new file mode 100644 index 0000000..72a3b8d --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/map_type.dart @@ -0,0 +1,11 @@ +/// Type of map tiles to display. +enum MapType { + /// Normal tiles (traffic and labels, subtle terrain information). + normal, + + /// Satellite imaging tiles (aerial photos) + satellite, + + /// Hybrid tiles (satellite images with some labels/overlays) + hybrid, +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/marker.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/marker.dart new file mode 100644 index 0000000..803bdc7 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/marker.dart @@ -0,0 +1,115 @@ +import 'package:flutter/widgets.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; + +/// Uniquely identifies a [Marker] among [PlatformMaps] markers. +/// +/// This does not have to be globally unique, only unique among the list. +@immutable +class MarkerId { + const MarkerId(this.value); + + /// value of the [MarkerId]. + final String value; +} + +/// Marks a geographical location on the map. +/// +/// A marker icon is drawn oriented against the device's screen rather than +/// the map's surface; that is, it will not necessarily change orientation +/// due to map rotations, tilting, or zooming. +@immutable +class Marker { + /// Creates a set of marker configuration options. + /// + /// Default marker options. + /// + /// Specifies a marker that + /// * is fully opaque; [alpha] is 1.0 + /// * has default tap handling; [consumeTapEvents] is false + /// * is stationary; [draggable] is false + /// * has a default icon; [icon] is `BitmapDescriptor.defaultMarker` + /// * has no info window text; [infoWindowText] is `InfoWindowText.noText` + /// * is positioned at 0, 0; [position] is `LatLng(0.0, 0.0)` + /// * is visible; [visible] is true + /// * is placed at the base of the drawing order; [zIndex] is 0.0 + const Marker({ + required this.markerId, + this.alpha = 1.0, + this.anchor = const Offset(0.5, 1.0), + this.consumeTapEvents = false, + this.draggable = false, + this.icon, + this.infoWindow = InfoWindow.noText, + this.position = const LatLng(0.0, 0.0), + this.onTap, + this.visible = true, + this.onDragEnd, + }) : assert((0.0 <= alpha && alpha <= 1.0)); + + /// Uniquely identifies a [Marker]. + final MarkerId markerId; + + /// The opacity of the marker, between 0.0 and 1.0 inclusive. + /// + /// 0.0 means fully transparent, 1.0 means fully opaque. + final double alpha; + + /// The icon image point that will be placed at the [position] of the marker. + /// + /// The image point is specified in normalized coordinates: An anchor of + /// (0.0, 0.0) means the top left corner of the image. An anchor + /// of (1.0, 1.0) means the bottom right corner of the image. + final Offset anchor; + + /// True if the marker icon consumes tap events. If not, the map will perform + /// default tap handling by centering the map on the marker and displaying its + /// info window. + final bool consumeTapEvents; + + /// True if the marker is draggable by user touch events. + final bool draggable; + + /// A description of the bitmap used to draw the marker icon. + final BitmapDescriptor? icon; + + /// A Google Maps InfoWindow. + /// + /// The window is displayed when the marker is tapped. + final InfoWindow infoWindow; + + /// Geographical location of the marker. + final LatLng position; + + /// Callbacks to receive tap events for markers placed on this map. + final VoidCallback? onTap; + + /// True if the annotation is visible. + final bool visible; + + final ValueChanged? onDragEnd; + + Marker copyWith({ + double? alphaParam, + Offset? anchorParam, + bool? consumeTapEventsParam, + bool? draggableParam, + BitmapDescriptor? iconParam, + InfoWindow? infoWindowParam, + LatLng? positionParam, + bool? visibleParam, + VoidCallback? onTapParam, + }) { + return Marker( + markerId: markerId, + alpha: alphaParam ?? alpha, + anchor: anchorParam ?? anchor, + consumeTapEvents: consumeTapEventsParam ?? consumeTapEvents, + draggable: draggableParam ?? draggable, + icon: iconParam ?? icon, + infoWindow: infoWindowParam ?? infoWindow, + position: positionParam ?? position, + visible: visibleParam ?? visible, + onTap: onTapParam ?? onTap, + ); + } +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/min_max_zoom_preference.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/min_max_zoom_preference.dart new file mode 100644 index 0000000..3733e69 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/min_max_zoom_preference.dart @@ -0,0 +1,18 @@ +/// Used with [PlatformMapOptions] to wrap min and max zoom. This allows +/// distinguishing between specifying unbounded zooming (null `minZoom` and +/// `maxZoom`) from not specifying anything (null `MinMaxZoomPreference`). +class MinMaxZoomPreference { + const MinMaxZoomPreference(this.minZoom, this.maxZoom) + : assert(minZoom == null || maxZoom == null || minZoom <= maxZoom); + + static const unbounded = MinMaxZoomPreference(null, null); + + /// The preferred minimum zoom level or null, if unbounded from below. + final double? minZoom; + + /// The preferred maximum zoom level or null, if unbounded from above. + final double? maxZoom; + + /// Converts this object to something serializable in JSON. + dynamic toJson() => [minZoom, maxZoom]; +} diff --git a/packages/platform_maps_flutter_platform_interface/lib/src/types/pattern_item.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/pattern_item.dart new file mode 100644 index 0000000..45f022c --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/pattern_item.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +/// Item used in the stroke pattern for a Polyline. +@immutable +sealed class PatternItem { + const PatternItem._(); + + static const PatternItem dot = DotPatternItem(); + + /// A dash used in the stroke pattern for a [Polyline]. + /// + /// [length] has to be non-negative. + static PatternItem dash(double length) { + assert(length >= 0.0); + return DashPatternItem(length); + } + + /// A gap used in the stroke pattern for a [Polyline]. + /// + /// [length] has to be non-negative. + static PatternItem gap(double length) { + assert(length >= 0.0); + return GapPatternItem(length); + } +} + +class DotPatternItem extends PatternItem { + const DotPatternItem() : super._(); +} + +class DashPatternItem extends PatternItem { + final double length; + const DashPatternItem(this.length) : super._(); +} + +class GapPatternItem extends PatternItem { + final double length; + const GapPatternItem(this.length) : super._(); +} diff --git a/lib/src/platform_maps.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/platform_maps_platform_widget_creation_params.dart similarity index 56% rename from lib/src/platform_maps.dart rename to packages/platform_maps_flutter_platform_interface/lib/src/types/platform_maps_platform_widget_creation_params.dart index 3177939..2d466fe 100644 --- a/lib/src/platform_maps.dart +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/platform_maps_platform_widget_creation_params.dart @@ -1,11 +1,15 @@ -part of '../platform_maps_flutter.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/widgets.dart'; + +import 'package:platform_maps_flutter_platform_interface/src/types.dart'; typedef MapCreatedCallback = void Function(PlatformMapController controller); typedef CameraPositionCallback = void Function(CameraPosition position); -class PlatformMap extends StatefulWidget { - const PlatformMap({ +class PlatformMapsPlatformWidgetCreationParams { + const PlatformMapsPlatformWidgetCreationParams({ Key? key, required this.initialCameraPosition, this.onMapCreated, @@ -31,7 +35,7 @@ class PlatformMap extends StatefulWidget { this.onCameraIdle, this.onTap, this.onLongPress, - }) : super(key: key); + }); /// Callback method for when the map is ready to be used. /// @@ -163,126 +167,4 @@ class PlatformMap extends StatefulWidget { /// When this set is empty, the map will only handle pointer events for gestures that /// were not claimed by any other gesture recognizer. final Set> gestureRecognizers; - @override - State createState() => _PlatformMapState(); -} - -class _PlatformMapState extends State { - @override - Widget build(BuildContext context) { - if (Platform.isAndroid) { - return google_maps.GoogleMap( - initialCameraPosition: widget.initialCameraPosition.googleMapsCameraPosition, - compassEnabled: widget.compassEnabled, - mapType: _getGoogleMapType(), - padding: widget.padding, - markers: Marker.toGoogleMapsMarkerSet(widget.markers), - polylines: Polyline.toGoogleMapsPolylines(widget.polylines), - polygons: Polygon.toGoogleMapsPolygonSet(widget.polygons), - circles: Circle.toGoogleMapsCircleSet(widget.circles), - gestureRecognizers: widget.gestureRecognizers, - onCameraIdle: widget.onCameraIdle, - myLocationButtonEnabled: widget.myLocationButtonEnabled, - myLocationEnabled: widget.myLocationEnabled, - onCameraMoveStarted: widget.onCameraMoveStarted, - tiltGesturesEnabled: widget.tiltGesturesEnabled, - rotateGesturesEnabled: widget.rotateGesturesEnabled, - zoomControlsEnabled: widget.zoomControlsEnabled, - zoomGesturesEnabled: widget.zoomGesturesEnabled, - scrollGesturesEnabled: widget.scrollGesturesEnabled, - onMapCreated: _onMapCreated, - onCameraMove: _onCameraMove, - onTap: _onTap, - onLongPress: _onLongPress, - trafficEnabled: widget.trafficEnabled, - minMaxZoomPreference: widget.minMaxZoomPreference.googleMapsZoomPreference, - ); - } else if (Platform.isIOS) { - return apple_maps.AppleMap( - initialCameraPosition: widget.initialCameraPosition.appleMapsCameraPosition, - compassEnabled: widget.compassEnabled, - mapType: _getAppleMapType(), - padding: widget.padding, - annotations: Marker.toAppleMapsAnnotationSet(widget.markers), - polylines: Polyline.toAppleMapsPolylines(widget.polylines), - polygons: Polygon.toAppleMapsPolygonSet(widget.polygons), - circles: Circle.toAppleMapsCircleSet(widget.circles), - gestureRecognizers: widget.gestureRecognizers, - onCameraIdle: widget.onCameraIdle, - myLocationButtonEnabled: widget.myLocationButtonEnabled, - myLocationEnabled: widget.myLocationEnabled, - onCameraMoveStarted: widget.onCameraMoveStarted, - pitchGesturesEnabled: widget.tiltGesturesEnabled, - rotateGesturesEnabled: widget.rotateGesturesEnabled, - zoomGesturesEnabled: widget.zoomGesturesEnabled, - scrollGesturesEnabled: widget.scrollGesturesEnabled, - onMapCreated: _onMapCreated, - onCameraMove: _onCameraMove, - onTap: _onTap, - onLongPress: _onLongPress, - trafficEnabled: widget.trafficEnabled, - minMaxZoomPreference: widget.minMaxZoomPreference.appleMapsZoomPreference, - ); - } else { - return const Text("Platform not yet implemented"); - } - } - - void _onMapCreated(dynamic controller) { - widget.onMapCreated?.call(PlatformMapController(controller)); - } - - void _onCameraMove(dynamic cameraPosition) { - if (Platform.isIOS) { - widget.onCameraMove?.call( - CameraPosition.fromAppleMapCameraPosition( - cameraPosition as apple_maps.CameraPosition, - ), - ); - } else if (Platform.isAndroid) { - widget.onCameraMove?.call( - CameraPosition.fromGoogleMapCameraPosition( - cameraPosition as google_maps.CameraPosition, - ), - ); - } - } - - void _onTap(dynamic position) { - if (Platform.isIOS) { - widget.onTap?.call(LatLng._fromAppleLatLng(position as apple_maps.LatLng)); - } else if (Platform.isAndroid) { - widget.onTap?.call(LatLng._fromGoogleLatLng(position as google_maps.LatLng)); - } - } - - void _onLongPress(dynamic position) { - if (Platform.isIOS) { - widget.onLongPress?.call(LatLng._fromAppleLatLng(position as apple_maps.LatLng)); - } else if (Platform.isAndroid) { - widget.onLongPress?.call(LatLng._fromGoogleLatLng(position as google_maps.LatLng)); - } - } - - apple_maps.MapType _getAppleMapType() { - if (widget.mapType == MapType.normal) { - return apple_maps.MapType.standard; - } else if (widget.mapType == MapType.satellite) { - return apple_maps.MapType.satellite; - } else if (widget.mapType == MapType.hybrid) { - return apple_maps.MapType.hybrid; - } - return apple_maps.MapType.standard; - } - - google_maps.MapType _getGoogleMapType() { - if (widget.mapType == MapType.normal) { - return google_maps.MapType.normal; - } else if (widget.mapType == MapType.satellite) { - return google_maps.MapType.satellite; - } else if (widget.mapType == MapType.hybrid) { - return google_maps.MapType.hybrid; - } - return google_maps.MapType.normal; - } } diff --git a/lib/src/polygon.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/polygon.dart similarity index 74% rename from lib/src/polygon.dart rename to packages/platform_maps_flutter_platform_interface/lib/src/types/polygon.dart index 9cb24b1..e11f10b 100644 --- a/lib/src/polygon.dart +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/polygon.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of '../platform_maps_flutter.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:platform_maps_flutter_platform_interface/src/types.dart'; /// Uniquely identifies a [Polygon] among [PlatformMap] polygons. /// @@ -124,42 +126,4 @@ class Polygon { @override int get hashCode => polygonId.hashCode; - - static Set toGoogleMapsPolygonSet(Set polygons) { - List polygons0 = []; - for (Polygon polygon in polygons) { - polygons0.add(polygon.googleMapsPolygon); - } - return Set.from(polygons0); - } - - static Set toAppleMapsPolygonSet(Set polygons) { - List polygons0 = []; - for (Polygon polygon in polygons) { - polygons0.add(polygon.appleMapsPolygon); - } - return Set.from(polygons0); - } - - google_maps.Polygon get googleMapsPolygon => google_maps.Polygon( - polygonId: google_maps.PolygonId(polygonId.value), - consumeTapEvents: consumeTapEvents, - fillColor: fillColor, - onTap: onTap, - points: LatLng.googleMapsLatLngsFromList(points), - strokeColor: strokeColor, - strokeWidth: strokeWidth, - visible: visible, - ); - - apple_maps.Polygon get appleMapsPolygon => apple_maps.Polygon( - polygonId: apple_maps.PolygonId(polygonId.value), - consumeTapEvents: consumeTapEvents, - fillColor: fillColor, - onTap: onTap, - points: LatLng.appleMapsLatLngsFromList(points), - strokeColor: strokeColor, - strokeWidth: strokeWidth, - visible: visible, - ); } diff --git a/lib/src/polyline.dart b/packages/platform_maps_flutter_platform_interface/lib/src/types/polyline.dart similarity index 68% rename from lib/src/polyline.dart rename to packages/platform_maps_flutter_platform_interface/lib/src/types/polyline.dart index aacc7aa..b708c8e 100644 --- a/lib/src/polyline.dart +++ b/packages/platform_maps_flutter_platform_interface/lib/src/types/polyline.dart @@ -1,4 +1,5 @@ -part of '../platform_maps_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; /// Uniquely identifies a [Polyline] among [AppleMap] polylines. /// @@ -9,18 +10,6 @@ class PolylineId { /// value of the [PolylineId]. final String value; - - google_maps.PolylineId googleMapsPolylineId() { - return google_maps.PolylineId( - value, - ); - } - - apple_maps.PolylineId appleMapsPolylineId() { - return apple_maps.PolylineId( - value, - ); - } } /// Draws a line through geographical locations on the map. @@ -106,49 +95,6 @@ class Polyline { /// Callbacks to receive tap events for polyline placed on this map. final VoidCallback? onTap; - static Set toGoogleMapsPolylines(Set polylines) { - Set googleMapsPolylines = {}; - for (var polyline in polylines) { - googleMapsPolylines.add( - google_maps.Polyline( - polylineId: polyline.polylineId.googleMapsPolylineId(), - color: polyline.color, - consumeTapEvents: polyline.consumeTapEvents, - endCap: _Cap.googlePolylineCap(polyline.polylineCap), - jointType: JointType.getGoogleMapsJointType(polyline.jointType), - onTap: polyline.onTap, - patterns: PatternItem.getGoogleMapsPatternItemList(polyline.patterns), - points: LatLng.googleMapsLatLngsFromList(polyline.points), - startCap: _Cap.googlePolylineCap(polyline.polylineCap), - visible: polyline.visible, - width: polyline.width, - ), - ); - } - return googleMapsPolylines; - } - - static Set toAppleMapsPolylines(Set polylines) { - Set appleMapsPolylines = {}; - for (var polyline in polylines) { - appleMapsPolylines.add( - apple_maps.Polyline( - polylineId: polyline.polylineId.appleMapsPolylineId(), - color: polyline.color, - consumeTapEvents: polyline.consumeTapEvents, - polylineCap: _Cap.applePolylineCap(polyline.polylineCap), - jointType: JointType.getAppleMapsJointType(polyline.jointType), - onTap: polyline.onTap, - patterns: PatternItem.getAppleMapsPatternItemList(polyline.patterns), - points: LatLng.appleMapsLatLngsFromList(polyline.points), - visible: polyline.visible, - width: polyline.width, - ), - ); - } - return appleMapsPolylines; - } - Polyline copyWith({ Color? colorParam, bool? consumeTapEventsParam, diff --git a/packages/platform_maps_flutter_platform_interface/pubspec.yaml b/packages/platform_maps_flutter_platform_interface/pubspec.yaml new file mode 100644 index 0000000..1b25b98 --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/pubspec.yaml @@ -0,0 +1,21 @@ +name: platform_maps_flutter_platform_interface +description: platform interface for platform_maps_flutter package +version: 1.0.0-beta +homepage: https://github.com/albert-heijn-technology +repository: https://github.com/albert-heijn-technology/platform_maps_flutter/tree/master/packages/platform_maps_flutter_platform_interface +issue_tracker: https://github.com/albert-heijn-technology/platform_maps_flutter/issues +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes + +environment: + sdk: ^3.2.4 + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.1.8 + +dev_dependencies: + flutter_lints: ^3.0.1 + flutter_test: + sdk: flutter diff --git a/packages/platform_maps_flutter_platform_interface/test/platform_maps_flutter_platform_interface_test.dart b/packages/platform_maps_flutter_platform_interface/test/platform_maps_flutter_platform_interface_test.dart new file mode 100644 index 0000000..4e3695a --- /dev/null +++ b/packages/platform_maps_flutter_platform_interface/test/platform_maps_flutter_platform_interface_test.dart @@ -0,0 +1,130 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:platform_maps_flutter_platform_interface/platform_maps_flutter_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Verify widget generation', () { + PlatformMapsPlatform.instance = FakePlatform(); + + final widget = PlatformMapsPlatformWidget( + const PlatformMapsPlatformWidgetCreationParams( + initialCameraPosition: CameraPosition(target: LatLng(0, 0)), + ), + ); + expect(widget, isA()); + }); + + test('Verify bitmap descriptor generation', () { + PlatformMapsPlatform.instance = FakePlatform(); + + final platformBitmapDescriptor = PlatformBitmapDescriptor(); + expect(platformBitmapDescriptor, isA()); + + final bitmapDescriptor = + platformBitmapDescriptor.fromBytes(Uint8List.fromList([1, 2])); + expect(bitmapDescriptor, isA()); + }); + + test('Verify camera update generation', () { + PlatformMapsPlatform.instance = FakePlatform(); + + final platformCameraUpdate = PlatformCameraUpdate(); + expect(platformCameraUpdate, isA()); + + final cameraUpdate = platformCameraUpdate.zoomIn(); + expect(cameraUpdate, isA()); + }); +} + +class FakePlatform extends PlatformMapsPlatform { + @override + PlatformMapsPlatformWidget createPlatformMapsPlatformWidget( + PlatformMapsPlatformWidgetCreationParams params, + ) { + return FakeWidget(params); + } + + @override + PlatformBitmapDescriptor createBitmapDescriptor() => + FakePlatformBitmapDescriptor(); + + @override + PlatformCameraUpdate createPlatformCameraUpdate() => + FakePlatformCameraUpdate(); +} + +class FakeWidget extends PlatformMapsPlatformWidget { + FakeWidget(super.params) : super.implementation(); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} + +class FakePlatformBitmapDescriptor extends PlatformBitmapDescriptor { + FakePlatformBitmapDescriptor() : super.implementation(); + + @override + Future fromAssetImage( + ImageConfiguration configuration, + String assetName, { + AssetBundle? bundle, + String? package, + }) { + return Future.value(FakeBitmapDescriptor()); + } + + @override + BitmapDescriptor fromBytes(Uint8List byteData) { + return FakeBitmapDescriptor(); + } +} + +class FakeBitmapDescriptor extends BitmapDescriptor {} + +class FakePlatformCameraUpdate extends PlatformCameraUpdate { + FakePlatformCameraUpdate() : super.implementation(); + + @override + CameraUpdate zoomIn() => FakeCameraUpdate(); + + @override + CameraUpdate newCameraPosition(CameraPosition cameraPosition) { + throw UnimplementedError(); + } + + @override + CameraUpdate newLatLng(LatLng latLng) { + throw UnimplementedError(); + } + + @override + CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) { + throw UnimplementedError(); + } + + @override + CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { + throw UnimplementedError(); + } + + @override + CameraUpdate zoomBy(double amount) { + throw UnimplementedError(); + } + + @override + CameraUpdate zoomOut() { + throw UnimplementedError(); + } + + @override + CameraUpdate zoomTo(double zoom) { + throw UnimplementedError(); + } +} + +class FakeCameraUpdate extends CameraUpdate {} diff --git a/pubspec.yaml b/pubspec.yaml index 3477e44..836f49e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,22 +1,7 @@ name: platform_maps_flutter -description: A Flutter package that combines google_maps and apple_maps to provide a crossplatform native map implementation. -version: 1.0.2 -homepage: https://github.com/LuisThein -repository: https://github.com/LuisThein/platform_maps_flutter -issue_tracker: https://github.com/LuisThein/platform_maps_flutter/issues environment: - sdk: '>=2.17.0 <4.0.0' - -dependencies: - flutter: - sdk: flutter - - google_maps_flutter: ^2.0.1 - google_maps_flutter_platform_interface: ^2.0.3 - apple_maps_flutter: ^1.0.1 + sdk: '>=3.0.0 <4.0.0' dev_dependencies: - flutter_lints: ^3.0.1 - flutter_test: - sdk: flutter + melos: ^6.0.0 \ No newline at end of file diff --git a/test/platform_maps_test.dart b/test/platform_maps_test.dart deleted file mode 100644 index 0da434d..0000000 --- a/test/platform_maps_test.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -void main() { - test('adds one to input values', () {}); -}