├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── dart.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android └── .project ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── .project │ ├── app │ │ ├── .project │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── de │ │ │ │ │ └── luisthein │ │ │ │ │ └── apple_maps_flutter_example │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── settings_aar.gradle ├── assets │ ├── 2.0x │ │ └── red_square.png │ ├── 3.0x │ │ └── red_square.png │ ├── creator.png │ ├── red_square.png │ └── test_marker.png ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ ├── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ └── outline_near_me.imageset │ │ │ ├── Contents.json │ │ │ ├── outline_near_me_black_18dp-1.png │ │ │ └── outline_near_me_black_18dp.png │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── animate_camera.dart │ ├── annotation_icons.dart │ ├── main.dart │ ├── map_click.dart │ ├── map_coordinates.dart │ ├── map_ui.dart │ ├── map_update.dart │ ├── move_camera.dart │ ├── padding.dart │ ├── page.dart │ ├── place_annotation.dart │ ├── place_circle.dart │ ├── place_polygon.dart │ ├── place_polyline.dart │ ├── scrolling_map.dart │ └── snapshot.dart ├── pubspec.yaml └── test_driver │ ├── apple_map_inspector.dart │ ├── apple_maps.dart │ ├── apple_maps_test.dart │ └── test_widgets.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── Annotations │ │ ├── AnnotationController.swift │ │ ├── AnnotationDelegate.swift │ │ ├── AnnotationIcon.swift │ │ ├── FlutterAnnotation.swift │ │ └── FlutterAnnotationView.swift │ ├── AppleMapsFlutterPlugin.h │ ├── AppleMapsFlutterPlugin.m │ ├── MapView │ │ ├── AppleMapController.swift │ │ ├── AppleMapsViewFactory.swift │ │ ├── FlutterMapView.swift │ │ └── MapViewExtension.swift │ ├── Overlays │ │ ├── FlutterOverlay.swift │ │ ├── Polygons │ │ │ ├── FlutterPolygon.swift │ │ │ ├── PolygonController.swift │ │ │ └── PolygonDelegate.swift │ │ ├── Polylines │ │ │ ├── FlutterPolyline.swift │ │ │ ├── PolylineController.swift │ │ │ └── PolylineDelegate.swift │ │ └── circles │ │ │ ├── CircleController.swift │ │ │ ├── CircleDelegate.swift │ │ │ └── FlutterCircle.swift │ ├── SwiftAppleMapsFlutterPlugin.swift │ ├── Utils │ │ ├── JsonConversion.swift │ │ ├── TouchHandler.swift │ │ └── Utils.swift │ └── models │ │ └── SnapshotOptions.swift └── apple_maps_flutter.podspec ├── lib ├── apple_maps_flutter.dart └── src │ ├── annotation.dart │ ├── annotation_updates.dart │ ├── apple_map.dart │ ├── bitmap.dart │ ├── callbacks.dart │ ├── camera.dart │ ├── cap.dart │ ├── circle.dart │ ├── circle_updates.dart │ ├── controller.dart │ ├── joint_type.dart │ ├── location.dart │ ├── pattern_item.dart │ ├── polygon.dart │ ├── polygon_updates.dart │ ├── polyline.dart │ ├── polyline_updates.dart │ ├── snapshot_options.dart │ └── ui.dart ├── pubspec.yaml └── test ├── annotation_updates_test.dart ├── apple_map_test.dart ├── circle_updates_test.dart ├── fake_maps_controllers.dart ├── polygon_update_test.dart └── polyline_updates_test.dart /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: LuisThein 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. iPhone6] 28 | - Version [e.g. 22] 29 | 30 | **flutter doctor** 31 | Please add the `flutter doctor` output. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: LuisThein 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* 2 | 3 | *List which issues are fixed by this PR.* 4 | 5 | ## Pre-launch Checklist 6 | 7 | - [ ] I updated pubspec.yaml with an appropriate new version according to the [pub versioning philosophy]. 8 | - [ ] I updated CHANGELOG.md to add a description of the change. 9 | - [ ] I updated/added relevant documentation (doc comments with `///`). 10 | - [ ] I added new tests to check the change I am making if a test is possible. 11 | - [ ] All existing and new tests are passing. -------------------------------------------------------------------------------- /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: Dart 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | runs-on: macos-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-java@v1 20 | with: 21 | java-version: '12.x' 22 | - name: Install Flutter 23 | uses: subosito/flutter-action@v1 24 | with: 25 | flutter-version: '3.27.3' 26 | - name: Install dependencies 27 | run: flutter pub get 28 | - name: Run dart analyze 29 | run: dart analyze 30 | - name: run tests 31 | run: flutter test --coverage 32 | - name: upload coverage report 33 | uses: codecov/codecov-action@v2 34 | with: 35 | token: ${{secrets.CODECOV}} 36 | file: ./coverage/lcov.info 37 | - name: build example project 38 | working-directory: ./example 39 | run: flutter build ios --release --no-codesign 40 | - name: publish --dry-run 41 | run: flutter pub publish --dry-run 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | pubspec.lock 33 | coverage/ 34 | 35 | # Android related 36 | **/android/**/gradle-wrapper.jar 37 | **/android/.gradle 38 | **/android/captures/ 39 | **/android/gradlew 40 | **/android/gradlew.bat 41 | **/android/local.properties 42 | **/android/**/GeneratedPluginRegistrant.java 43 | 44 | # iOS/XCode related 45 | **/ios/**/*.mode1v3 46 | **/ios/**/*.mode2v3 47 | **/ios/**/*.moved-aside 48 | **/ios/**/*.pbxuser 49 | **/ios/**/*.perspectivev3 50 | **/ios/**/*sync/ 51 | **/ios/**/.sconsign.dblite 52 | **/ios/**/.tags* 53 | **/ios/**/.vagrant/ 54 | **/ios/**/DerivedData/ 55 | **/ios/**/Icon? 56 | **/ios/**/Pods/ 57 | **/ios/**/.symlinks/ 58 | **/ios/**/profile 59 | **/ios/**/xcuserdata 60 | **/ios/.generated/ 61 | **/ios/Flutter/App.framework 62 | **/ios/Flutter/Flutter.framework 63 | **/ios/Flutter/Flutter.podspec 64 | **/ios/Flutter/Generated.xcconfig 65 | **/ios/Flutter/app.flx 66 | **/ios/Flutter/app.zip 67 | **/ios/Flutter/flutter_assets/ 68 | **/ios/Flutter/flutter_export_environment.sh 69 | **/ios/ServiceDefinitions.json 70 | **/ios/Runner/GeneratedPluginRegistrant.* 71 | 72 | # Exceptions to above rules. 73 | !**/ios/**/default.mode1v3 74 | !**/ios/**/default.mode2v3 75 | !**/ios/**/default.pbxuser 76 | !**/ios/**/default.perspectivev3 77 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 20e59316b8b8474554b38493b8ca888794b0234a 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.4.0 4 | 5 | * Flutter 3.27.1 compatibility, replace `ui.hash*` with `Object.hash* 6 | 7 | ## 1.3.0 8 | 9 | * Animate marker position changes instead of removing and re-adding 10 | * Fix Fatal error: Attempted to read an unowned reference but the object was already deallocated 11 | * Fixed an issue where onCameraMove was not invoked by double-tapping 12 | * Added insetsLayoutMarginsFromSafeArea 13 | 14 | ## 1.2.0 15 | 16 | * Added a `markerAnnotationWithHue()` and `pinAnnotationWithHue()` method to allow custom marker/pin colors 17 | 18 | ## 1.1.0 19 | 20 | * Added Annotation zIndex 21 | * Added posibility to take snapshots of the map 22 | 23 | ## 1.0.3 24 | 25 | * Fixes an issue where mapController.moveCamera would animate the camera transition 26 | * To animate a camera movement, mapController.animateCamera should be used instead 27 | 28 | ## 1.0.2 29 | 30 | * Removed Android folder to fix build failures 31 | 32 | ## 1.0.1 33 | 34 | * Fixes memory leak 35 | * Adds ability to take snapshots of the map 36 | ## 1.0.0 37 | 38 | Thanks to @jonbhanson 39 | * Adds null safety. 40 | * Refreshes the example app. 41 | * Updates .gitignore and removes files that should not be tracked. 42 | 43 | ## 0.1.4 44 | 45 | * Animate to bounds was added. (Thanks to @nghiashiyi) 46 | * Fixed an issue where the user location was only displayed in `authorizationInUse` status. (Thanks to @zgosalvez) 47 | 48 | * minor fixes 49 | 50 | ## 0.1.3 51 | 52 | * Thanks to @maxiundtesa the getter for the current zoomLevel was added 53 | * iOS build failure for Flutter modules was fixed 54 | 55 | ## 0.1.2+5 56 | 57 | * Fixed build failure 58 | * Added anchor param to Annotation 59 | * Added missing comparison of Overlay coordinates, which caused 60 | Circles, Annotations, Polylines and Ploygons to not update correctly 61 | on coordinate changes. 62 | 63 | ## 0.1.2+4 64 | 65 | * Added configurable Anchor for infoWindows 66 | 67 | ## 0.1.2+3 68 | 69 | * Fixed the offset of custom markers 70 | 71 | ## 0.1.2+2 72 | 73 | * Fixed the onTap event for Annotation Callouts 74 | 75 | ## 0.1.2+1 76 | 77 | * Added custom annotation icons from byte data 78 | * Fixed scaling of icons from assets => see: https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets 79 | 80 | ## 0.1.2 81 | 82 | * Annotation rework: 83 | * onTap for InfoWindow added 84 | * Multiline InfoWindow subtitle support 85 | * Overall Annotation handling refactored 86 | * Correct UserTracking Button added 87 | 88 | ## 0.1.1+2 89 | 90 | * Fixed map freezing when setState is being called 91 | 92 | ## 0.1.1+1 93 | 94 | * Fixed Polygon and Circle Tap events. 95 | 96 | ## 0.1.1 97 | 98 | * Added markerAnnotation as selectable annotation type. 99 | 100 | ## 0.1.0 101 | 102 | * Added ability to place circles on the map. 103 | 104 | ## 0.0.7 105 | 106 | * Added ability to place polygons on the map. 107 | 108 | ## 0.0.6+4 109 | 110 | * Fixed build issues 111 | 112 | ## 0.0.6+3 113 | 114 | * Fixes issue #6, location permission is only requested if it's actually used. 115 | 116 | ## 0.0.6+2 117 | 118 | * Converted iOS code to swift 5. 119 | 120 | ## 0.0.6+1 121 | 122 | * Changed annotation initialisation, fixes custom annotation icons not showing up on the map. 123 | 124 | ## 0.0.6 125 | 126 | * Added ability to add padding to the map 127 | 128 | ## 0.0.5 129 | 130 | * Added ability to place polylines. 131 | 132 | ## 0.0.4 133 | 134 | * Fixed error when updating Annotations on map. 135 | 136 | ## 0.0.3 137 | 138 | * Added getter for visible map region. 139 | 140 | ## 0.0.2 141 | 142 | * Added zoomBy functionality. 143 | * Added setter for min and max zoom levels. 144 | 145 | ## 0.0.1 146 | 147 | * Initial release. 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, Luis Thein 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apple_maps_flutter 2 | 3 | [![codecov](https://codecov.io/gh/LuisThein/apple_maps_flutter/branch/master/graph/badge.svg)](https://codecov.io/gh/LuisThein/apple_maps_flutter) 4 | 5 | A Flutter plugin that provides an Apple Maps widget. 6 | 7 | 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. 8 | 9 | This plugin was based on the `google_maps_flutter` plugin. Instead of reinventing the wheel it also uses the Flutter implementation of the `google_maps_flutter` plugin. This was also done to simplify the process of combining the `google_maps_flutter` plugin with `apple_maps_flutter` to create a cross platform implementation for Android/iOS called `flutter_platform_maps`. 10 | 11 | # Screenshots 12 | 13 | | Example 1 | Example 2 | 14 | | :-------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------: | 15 | | ![Example 1](https://luisthein.de/apple-maps-plugin-images/example_img01-min.png) | ![Example 2](https://luisthein.de/apple-maps-plugin-images/example_img02-min.png) | 16 | 17 | # iOS 18 | 19 | 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. 20 | 21 | # Android 22 | 23 | There is no Android implementation, but there is a package combining apple_maps_flutter and the google_maps_flutter plugin to have the typical map implementations for Android/iOS called platform_maps_flutter. 24 | 25 | ## Sample Usage 26 | 27 | ```dart 28 | class AppleMapsExample extends StatelessWidget { 29 | AppleMapController mapController; 30 | 31 | void _onMapCreated(AppleMapController controller) { 32 | mapController = controller; 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Column( 38 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 39 | crossAxisAlignment: CrossAxisAlignment.stretch, 40 | children: [ 41 | Expanded( 42 | child: Container( 43 | child: AppleMap( 44 | onMapCreated: _onMapCreated, 45 | initialCameraPosition: const CameraPosition( 46 | target: LatLng(0.0, 0.0), 47 | ), 48 | ), 49 | ), 50 | ), 51 | Row( 52 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 53 | children: [ 54 | Column( 55 | children: [ 56 | FlatButton( 57 | onPressed: () { 58 | mapController.moveCamera( 59 | CameraUpdate.newCameraPosition( 60 | const CameraPosition( 61 | heading: 270.0, 62 | target: LatLng(51.5160895, -0.1294527), 63 | pitch: 30.0, 64 | zoom: 17, 65 | ), 66 | ), 67 | ); 68 | }, 69 | child: const Text('newCameraPosition'), 70 | ), 71 | FlatButton( 72 | onPressed: () { 73 | mapController.moveCamera( 74 | CameraUpdate.newLatLngZoom( 75 | const LatLng(37.4231613, -122.087159), 76 | 11.0, 77 | ), 78 | ); 79 | }, 80 | child: const Text('newLatLngZoom'), 81 | ), 82 | ], 83 | ), 84 | Column( 85 | children: [ 86 | FlatButton( 87 | onPressed: () { 88 | mapController.moveCamera( 89 | CameraUpdate.zoomIn(), 90 | ); 91 | }, 92 | child: const Text('zoomIn'), 93 | ), 94 | FlatButton( 95 | onPressed: () { 96 | mapController.moveCamera( 97 | CameraUpdate.zoomOut(), 98 | ); 99 | }, 100 | child: const Text('zoomOut'), 101 | ), 102 | FlatButton( 103 | onPressed: () { 104 | mapController.moveCamera( 105 | CameraUpdate.zoomTo(16.0), 106 | ); 107 | }, 108 | child: const Text('zoomTo'), 109 | ), 110 | ], 111 | ), 112 | ], 113 | ) 114 | ], 115 | ); 116 | } 117 | } 118 | ``` 119 | 120 | Suggestions and PR's to make this plugin better are always welcome. 121 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | apple_maps_flutter 4 | Project apple_maps_flutter created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 0 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c5a4b4029c0798f37c4a39b479d7cb75daa7b05c 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example usage: 2 | 3 | ```dart 4 | class AppleMapsExample extends StatelessWidget { 5 | AppleMapController mapController; 6 | 7 | void _onMapCreated(AppleMapController controller) { 8 | mapController = controller; 9 | } 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 15 | crossAxisAlignment: CrossAxisAlignment.stretch, 16 | children: [ 17 | Expanded( 18 | child: Container( 19 | child: AppleMap( 20 | onMapCreated: _onMapCreated, 21 | initialCameraPosition: const CameraPosition( 22 | target: LatLng(0.0, 0.0), 23 | ), 24 | ), 25 | ), 26 | ), 27 | Row( 28 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 29 | children: [ 30 | Column( 31 | children: [ 32 | FlatButton( 33 | onPressed: () { 34 | mapController.moveCamera( 35 | CameraUpdate.newCameraPosition( 36 | const CameraPosition( 37 | heading: 270.0, 38 | target: LatLng(51.5160895, -0.1294527), 39 | pitch: 30.0, 40 | zoom: 17, 41 | ), 42 | ), 43 | ); 44 | }, 45 | child: const Text('newCameraPosition'), 46 | ), 47 | FlatButton( 48 | onPressed: () { 49 | mapController.moveCamera( 50 | CameraUpdate.newLatLngZoom( 51 | const LatLng(37.4231613, -122.087159), 52 | 11.0, 53 | ), 54 | ); 55 | }, 56 | child: const Text('newLatLngZoom'), 57 | ), 58 | ], 59 | ), 60 | Column( 61 | children: [ 62 | FlatButton( 63 | onPressed: () { 64 | mapController.moveCamera( 65 | CameraUpdate.zoomIn(), 66 | ); 67 | }, 68 | child: const Text('zoomIn'), 69 | ), 70 | FlatButton( 71 | onPressed: () { 72 | mapController.moveCamera( 73 | CameraUpdate.zoomOut(), 74 | ); 75 | }, 76 | child: const Text('zoomOut'), 77 | ), 78 | FlatButton( 79 | onPressed: () { 80 | mapController.moveCamera( 81 | CameraUpdate.zoomTo(16.0), 82 | ); 83 | }, 84 | child: const Text('zoomTo'), 85 | ), 86 | ], 87 | ), 88 | ], 89 | ) 90 | ], 91 | ); 92 | } 93 | } 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1601924530843 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1601924530855 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 31 29 | 30 | defaultConfig { 31 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 32 | applicationId "de.luisthein.apple_maps_flutter_example" 33 | minSdkVersion 16 34 | targetSdkVersion 31 35 | versionCode flutterVersionCode.toInteger() 36 | versionName flutterVersionName 37 | } 38 | 39 | buildTypes { 40 | release { 41 | // TODO: Add your own signing config for the release build. 42 | // Signing with the debug keys for now, so `flutter run --release` works. 43 | signingConfig signingConfigs.debug 44 | } 45 | } 46 | } 47 | 48 | flutter { 49 | source '../..' 50 | } 51 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/de/luisthein/apple_maps_flutter_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package de.luisthein.apple_maps_flutter_example; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.1.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /example/assets/2.0x/red_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/assets/2.0x/red_square.png -------------------------------------------------------------------------------- /example/assets/3.0x/red_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/assets/3.0x/red_square.png -------------------------------------------------------------------------------- /example/assets/creator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/assets/creator.png -------------------------------------------------------------------------------- /example/assets/red_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/assets/red_square.png -------------------------------------------------------------------------------- /example/assets/test_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/assets/test_marker.png -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - apple_maps_flutter (0.0.1): 3 | - Flutter 4 | - Flutter (1.0.0) 5 | 6 | DEPENDENCIES: 7 | - apple_maps_flutter (from `.symlinks/plugins/apple_maps_flutter/ios`) 8 | - Flutter (from `Flutter`) 9 | 10 | EXTERNAL SOURCES: 11 | apple_maps_flutter: 12 | :path: ".symlinks/plugins/apple_maps_flutter/ios" 13 | Flutter: 14 | :path: Flutter 15 | 16 | SPEC CHECKSUMS: 17 | apple_maps_flutter: c59725efea39e13e703cde52a1d2b14866ad68a8 18 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 19 | 20 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 21 | 22 | COCOAPODS: 1.11.3 23 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/outline_near_me.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "outline_near_me_black_18dp.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "outline_near_me_black_18dp-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/outline_near_me.imageset/outline_near_me_black_18dp-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/outline_near_me.imageset/outline_near_me_black_18dp-1.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/outline_near_me.imageset/outline_near_me_black_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/example/ios/Runner/Assets.xcassets/outline_near_me.imageset/outline_near_me_black_18dp.png -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | apple_maps_flutter_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | NSLocationWhenInUseUsageDescription 26 | This App needs permission to access your location in order to show it on the map. 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/animate_camera.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'page.dart'; 9 | 10 | class AnimateCameraPage extends ExamplePage { 11 | AnimateCameraPage() 12 | : super(const Icon(Icons.map), 'Camera control, animated'); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return const AnimateCamera(); 17 | } 18 | } 19 | 20 | class AnimateCamera extends StatefulWidget { 21 | const AnimateCamera(); 22 | @override 23 | State createState() => AnimateCameraState(); 24 | } 25 | 26 | class AnimateCameraState extends State { 27 | late AppleMapController mapController; 28 | 29 | void _onMapCreated(AppleMapController controller) { 30 | mapController = controller; 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return SafeArea( 36 | child: Column( 37 | children: [ 38 | Expanded( 39 | child: AppleMap( 40 | onMapCreated: _onMapCreated, 41 | initialCameraPosition: const CameraPosition( 42 | target: LatLng(0.0, 0.0), 43 | ), 44 | ), 45 | ), 46 | Padding( 47 | padding: const EdgeInsets.all(8.0), 48 | child: Wrap( 49 | alignment: WrapAlignment.spaceEvenly, 50 | children: [ 51 | TextButton( 52 | onPressed: () { 53 | mapController.animateCamera( 54 | CameraUpdate.newCameraPosition( 55 | const CameraPosition( 56 | heading: 270.0, 57 | target: LatLng(51.5160895, -0.1294527), 58 | pitch: 30.0, 59 | zoom: 17, 60 | ), 61 | ), 62 | ); 63 | }, 64 | child: const Text('newCameraPosition'), 65 | ), 66 | TextButton( 67 | onPressed: () { 68 | mapController.animateCamera( 69 | CameraUpdate.newLatLng( 70 | const LatLng(56.1725505, 10.1850512), 71 | ), 72 | ); 73 | }, 74 | child: const Text('newLatLng'), 75 | ), 76 | TextButton( 77 | onPressed: () { 78 | mapController.animateCamera( 79 | CameraUpdate.newLatLngZoom( 80 | const LatLng(37.4231613, -122.087159), 81 | 11.0, 82 | ), 83 | ); 84 | }, 85 | child: const Text('newLatLngZoom'), 86 | ), 87 | TextButton( 88 | onPressed: () { 89 | mapController.animateCamera( 90 | CameraUpdate.newLatLngBounds( 91 | LatLngBounds( 92 | southwest: LatLng(37.749146, -122.71829295684518), 93 | northeast: LatLng(37.94134713198227, -122.498971)), 94 | 11.0, 95 | ), 96 | ); 97 | }, 98 | child: const Text('newLatLngBounds'), 99 | ), 100 | TextButton( 101 | onPressed: () { 102 | mapController.animateCamera( 103 | CameraUpdate.zoomBy( 104 | -0.5, 105 | const Offset(30.0, 20.0), 106 | ), 107 | ); 108 | }, 109 | child: const Text('zoomBy with focus'), 110 | ), 111 | TextButton( 112 | onPressed: () { 113 | mapController.animateCamera( 114 | CameraUpdate.zoomBy(-0.5), 115 | ); 116 | }, 117 | child: const Text('zoomBy'), 118 | ), 119 | TextButton( 120 | onPressed: () { 121 | mapController.animateCamera( 122 | CameraUpdate.zoomIn(), 123 | ); 124 | }, 125 | child: const Text('zoomIn'), 126 | ), 127 | TextButton( 128 | onPressed: () { 129 | mapController.animateCamera( 130 | CameraUpdate.zoomOut(), 131 | ); 132 | }, 133 | child: const Text('zoomOut'), 134 | ), 135 | TextButton( 136 | onPressed: () { 137 | mapController.animateCamera( 138 | CameraUpdate.zoomTo(16.0), 139 | ); 140 | }, 141 | child: const Text('zoomTo'), 142 | ), 143 | ], 144 | ), 145 | ), 146 | ], 147 | ), 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /example/lib/annotation_icons.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'page.dart'; 9 | 10 | class AnnotationIconsPage extends ExamplePage { 11 | AnnotationIconsPage() : super(const Icon(Icons.image), 'Annotation icons'); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return const AnnotationIconsBody(); 16 | } 17 | } 18 | 19 | class AnnotationIconsBody extends StatefulWidget { 20 | const AnnotationIconsBody(); 21 | 22 | @override 23 | State createState() => AnnotationIconsBodyState(); 24 | } 25 | 26 | const LatLng _kMapCenter = LatLng(52.707755, -2.7540658); 27 | 28 | class AnnotationIconsBodyState extends State { 29 | late AppleMapController controller; 30 | BitmapDescriptor? _annotationIcon; 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | _createAnnotationImageFromAsset(context); 35 | return SafeArea( 36 | child: AppleMap( 37 | initialCameraPosition: const CameraPosition( 38 | target: _kMapCenter, 39 | zoom: 8, 40 | ), 41 | annotations: _createAnnotation(), 42 | onMapCreated: _onMapCreated, 43 | ), 44 | ); 45 | } 46 | 47 | Set _createAnnotation() { 48 | return [ 49 | Annotation( 50 | annotationId: AnnotationId("annotation_1"), 51 | anchor: Offset(0.5, -4), 52 | position: LatLng(52.707755, -2.7540658), 53 | icon: _annotationIcon ?? BitmapDescriptor.defaultAnnotation, 54 | ), 55 | ].toSet(); 56 | } 57 | 58 | Future _createAnnotationImageFromAsset(BuildContext context) async { 59 | if (_annotationIcon == null) { 60 | final ImageConfiguration imageConfiguration = 61 | new ImageConfiguration(devicePixelRatio: 1.0); 62 | BitmapDescriptor.fromAssetImage( 63 | imageConfiguration, 'assets/test_marker.png') 64 | .then(_updateBitmap); 65 | } 66 | } 67 | 68 | void _updateBitmap(BitmapDescriptor bitmap) { 69 | setState(() { 70 | _annotationIcon = bitmap; 71 | }); 72 | } 73 | 74 | void _onMapCreated(AppleMapController controllerParam) { 75 | setState(() { 76 | controller = controllerParam; 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter_example/animate_camera.dart'; 6 | import 'package:apple_maps_flutter_example/annotation_icons.dart'; 7 | import 'package:apple_maps_flutter_example/map_click.dart'; 8 | import 'package:apple_maps_flutter_example/map_coordinates.dart'; 9 | import 'package:apple_maps_flutter_example/map_ui.dart'; 10 | import 'package:apple_maps_flutter_example/map_update.dart'; 11 | import 'package:apple_maps_flutter_example/move_camera.dart'; 12 | import 'package:apple_maps_flutter_example/padding.dart'; 13 | import 'package:apple_maps_flutter_example/page.dart'; 14 | import 'package:apple_maps_flutter_example/place_annotation.dart'; 15 | import 'package:apple_maps_flutter_example/place_circle.dart'; 16 | import 'package:apple_maps_flutter_example/place_polygon.dart'; 17 | import 'package:apple_maps_flutter_example/place_polyline.dart'; 18 | import 'package:apple_maps_flutter_example/scrolling_map.dart'; 19 | import 'package:apple_maps_flutter_example/snapshot.dart'; 20 | import 'package:flutter/material.dart'; 21 | 22 | final List _allPages = [ 23 | MapUiPage(), 24 | MapCoordinatesPage(), 25 | MapClickPage(), 26 | AnimateCameraPage(), 27 | MoveCameraPage(), 28 | PaddingPage(), 29 | PlaceAnnotationPage(), 30 | AnnotationIconsPage(), 31 | PlacePolylinePage(), 32 | PlacePolygonPage(), 33 | PlaceCirclePage(), 34 | ScrollingMapPage(), 35 | MapUpdatePage(), 36 | SnapshotPage(), 37 | ]; 38 | 39 | class MapsDemo extends StatelessWidget { 40 | void _pushPage(BuildContext context, ExamplePage page) { 41 | Navigator.of(context).push(MaterialPageRoute( 42 | builder: (_) => Scaffold( 43 | appBar: AppBar(title: Text(page.title)), 44 | body: page, 45 | ))); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Scaffold( 51 | appBar: AppBar(title: const Text('AppleMaps examples')), 52 | body: ListView.builder( 53 | itemCount: _allPages.length, 54 | itemBuilder: (_, int index) => ListTile( 55 | leading: _allPages[index].leading, 56 | title: Text(_allPages[index].title), 57 | onTap: () => _pushPage(context, _allPages[index]), 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | 64 | void main() { 65 | runApp(MaterialApp(home: MapsDemo())); 66 | } 67 | -------------------------------------------------------------------------------- /example/lib/map_click.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | 9 | import 'page.dart'; 10 | 11 | const CameraPosition _kInitialPosition = CameraPosition( 12 | target: LatLng(-33.852, 151.211), 13 | zoom: 11, 14 | ); 15 | 16 | class MapClickPage extends ExamplePage { 17 | MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return SafeArea(child: const _MapClickBody()); 22 | } 23 | } 24 | 25 | class _MapClickBody extends StatefulWidget { 26 | const _MapClickBody(); 27 | 28 | @override 29 | State createState() => _MapClickBodyState(); 30 | } 31 | 32 | class _MapClickBodyState extends State<_MapClickBody> { 33 | _MapClickBodyState(); 34 | 35 | AppleMapController? mapController; 36 | LatLng? _lastTap; 37 | LatLng? _lastLongPress; 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | final AppleMap appleMap = AppleMap( 42 | onMapCreated: onMapCreated, 43 | initialCameraPosition: _kInitialPosition, 44 | onTap: (LatLng pos) { 45 | setState(() { 46 | _lastTap = pos; 47 | }); 48 | }, 49 | onLongPress: (LatLng pos) { 50 | setState(() { 51 | _lastLongPress = pos; 52 | }); 53 | }, 54 | ); 55 | 56 | final List columnChildren = [Expanded(child: appleMap)]; 57 | 58 | if (mapController != null) { 59 | final String lastTap = 'Tap:\n${_lastTap ?? ""}\n'; 60 | final String lastLongPress = 'Long press:\n${_lastLongPress ?? ""}'; 61 | columnChildren.add(Padding( 62 | padding: EdgeInsets.all(8), 63 | child: Column( 64 | children: [ 65 | Text(lastTap, textAlign: TextAlign.center), 66 | Text(lastLongPress, textAlign: TextAlign.center) 67 | ], 68 | ), 69 | )); 70 | } 71 | 72 | return Column(children: columnChildren); 73 | } 74 | 75 | void onMapCreated(AppleMapController controller) async { 76 | setState(() { 77 | mapController = controller; 78 | }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /example/lib/map_coordinates.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | 9 | import 'page.dart'; 10 | 11 | const CameraPosition _kInitialPosition = CameraPosition( 12 | target: LatLng(-33.852, 151.211), 13 | zoom: 11, 14 | ); 15 | 16 | class MapCoordinatesPage extends ExamplePage { 17 | MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return SafeArea(child: const _MapCoordinatesBody()); 22 | } 23 | } 24 | 25 | class _MapCoordinatesBody extends StatefulWidget { 26 | const _MapCoordinatesBody(); 27 | 28 | @override 29 | State createState() => _MapCoordinatesBodyState(); 30 | } 31 | 32 | class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { 33 | _MapCoordinatesBodyState(); 34 | 35 | AppleMapController? mapController; 36 | LatLngBounds _visibleRegion = LatLngBounds( 37 | southwest: const LatLng(0, 0), 38 | northeast: const LatLng(0, 0), 39 | ); 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | final AppleMap appleMap = AppleMap( 44 | onMapCreated: onMapCreated, 45 | initialCameraPosition: _kInitialPosition, 46 | ); 47 | 48 | final List columnChildren = [Expanded(child: appleMap)]; 49 | 50 | if (mapController != null) { 51 | final String currentVisibleRegion = 'VisibleRegion:' 52 | '\nnortheast: ${_visibleRegion.northeast},' 53 | '\nsouthwest: ${_visibleRegion.southwest}'; 54 | columnChildren.add(Padding( 55 | padding: EdgeInsets.all(8), 56 | child: Column( 57 | children: [ 58 | Center(child: Text(currentVisibleRegion)), 59 | _getVisibleRegionButton(), 60 | ], 61 | ), 62 | )); 63 | } 64 | 65 | return Column(children: columnChildren); 66 | } 67 | 68 | void onMapCreated(AppleMapController controller) async { 69 | final LatLngBounds visibleRegion = await controller.getVisibleRegion(); 70 | setState(() { 71 | mapController = controller; 72 | _visibleRegion = visibleRegion; 73 | }); 74 | } 75 | 76 | Widget _getVisibleRegionButton() { 77 | return Padding( 78 | padding: const EdgeInsets.all(8.0), 79 | child: ElevatedButton( 80 | child: const Text('Get Visible Region Bounds'), 81 | onPressed: () async { 82 | final LatLngBounds visibleRegion = 83 | (await mapController?.getVisibleRegion())!; 84 | setState(() { 85 | _visibleRegion = visibleRegion; 86 | }); 87 | }, 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /example/lib/map_ui.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'page.dart'; 9 | 10 | final LatLngBounds sydneyBounds = LatLngBounds( 11 | southwest: const LatLng(-34.022631, 150.620685), 12 | northeast: const LatLng(-33.571835, 151.325952), 13 | ); 14 | 15 | class MapUiPage extends ExamplePage { 16 | MapUiPage() : super(const Icon(Icons.map), 'User interface'); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return SafeArea(child: const MapUiBody()); 21 | } 22 | } 23 | 24 | class MapUiBody extends StatefulWidget { 25 | const MapUiBody(); 26 | 27 | @override 28 | State createState() => MapUiBodyState(); 29 | } 30 | 31 | class MapUiBodyState extends State { 32 | MapUiBodyState(); 33 | 34 | static final CameraPosition _kInitialPosition = const CameraPosition( 35 | target: LatLng(-33.852, 151.211), 36 | zoom: 11, 37 | ); 38 | 39 | CameraPosition _position = _kInitialPosition; 40 | bool _isMapCreated = false; 41 | bool _isMoving = false; 42 | bool _compassEnabled = true; 43 | bool _myLocationButtonEnabled = true; 44 | MinMaxZoomPreference _minMaxZoomPreference = MinMaxZoomPreference.unbounded; 45 | MapType _mapType = MapType.standard; 46 | bool _rotateGesturesEnabled = true; 47 | bool _scrollGesturesEnabled = true; 48 | bool _pitchGesturesEnabled = true; 49 | bool _zoomGesturesEnabled = true; 50 | bool _myLocationEnabled = true; 51 | TrackingMode _trackingMode = TrackingMode.none; 52 | @override 53 | void initState() { 54 | super.initState(); 55 | } 56 | 57 | @override 58 | void dispose() { 59 | super.dispose(); 60 | } 61 | 62 | Widget _compassToggler() { 63 | return TextButton( 64 | child: Text('${_compassEnabled ? 'disable' : 'enable'} compass'), 65 | onPressed: () { 66 | setState(() { 67 | _compassEnabled = !_compassEnabled; 68 | }); 69 | }, 70 | ); 71 | } 72 | 73 | Widget _zoomBoundsToggler() { 74 | return TextButton( 75 | child: Text(_minMaxZoomPreference.minZoom == null 76 | ? 'bound zoom' 77 | : 'release zoom'), 78 | onPressed: () { 79 | setState(() { 80 | _minMaxZoomPreference = _minMaxZoomPreference.minZoom == null 81 | ? const MinMaxZoomPreference(12.0, 16.0) 82 | : MinMaxZoomPreference.unbounded; 83 | }); 84 | }, 85 | ); 86 | } 87 | 88 | Widget _mapTypeCycler() { 89 | final MapType nextType = 90 | MapType.values[(_mapType.index + 1) % MapType.values.length]; 91 | return TextButton( 92 | child: Text('change map type to $nextType'), 93 | onPressed: () { 94 | setState(() { 95 | _mapType = nextType; 96 | }); 97 | }, 98 | ); 99 | } 100 | 101 | Widget _rotateToggler() { 102 | return TextButton( 103 | child: Text('${_rotateGesturesEnabled ? 'disable' : 'enable'} rotate'), 104 | onPressed: () { 105 | setState(() { 106 | _rotateGesturesEnabled = !_rotateGesturesEnabled; 107 | }); 108 | }, 109 | ); 110 | } 111 | 112 | Widget _scrollToggler() { 113 | return TextButton( 114 | child: Text('${_scrollGesturesEnabled ? 'disable' : 'enable'} scroll'), 115 | onPressed: () { 116 | setState(() { 117 | _scrollGesturesEnabled = !_scrollGesturesEnabled; 118 | }); 119 | }, 120 | ); 121 | } 122 | 123 | Widget _tiltToggler() { 124 | return TextButton( 125 | child: Text('${_pitchGesturesEnabled ? 'disable' : 'enable'} tilt'), 126 | onPressed: () { 127 | setState(() { 128 | _pitchGesturesEnabled = !_pitchGesturesEnabled; 129 | }); 130 | }, 131 | ); 132 | } 133 | 134 | Widget _zoomToggler() { 135 | return TextButton( 136 | child: Text('${_zoomGesturesEnabled ? 'disable' : 'enable'} zoom'), 137 | onPressed: () { 138 | setState(() { 139 | _zoomGesturesEnabled = !_zoomGesturesEnabled; 140 | }); 141 | }, 142 | ); 143 | } 144 | 145 | Widget _myLocationToggler() { 146 | return TextButton( 147 | child: Text( 148 | '${_myLocationEnabled ? 'disable' : 'enable'} my location annotation'), 149 | onPressed: () { 150 | setState(() { 151 | _myLocationEnabled = !_myLocationEnabled; 152 | }); 153 | }, 154 | ); 155 | } 156 | 157 | Widget _myLocationButtonToggler() { 158 | return TextButton( 159 | child: Text( 160 | '${_myLocationButtonEnabled ? 'disable' : 'enable'} my location button'), 161 | onPressed: () { 162 | setState(() { 163 | _myLocationButtonEnabled = !_myLocationButtonEnabled; 164 | }); 165 | }, 166 | ); 167 | } 168 | 169 | @override 170 | Widget build(BuildContext context) { 171 | final AppleMap appleMap = AppleMap( 172 | onMapCreated: onMapCreated, 173 | trackingMode: _trackingMode, 174 | initialCameraPosition: _kInitialPosition, 175 | compassEnabled: _compassEnabled, 176 | minMaxZoomPreference: _minMaxZoomPreference, 177 | mapType: _mapType, 178 | rotateGesturesEnabled: _rotateGesturesEnabled, 179 | scrollGesturesEnabled: _scrollGesturesEnabled, 180 | pitchGesturesEnabled: _pitchGesturesEnabled, 181 | zoomGesturesEnabled: _zoomGesturesEnabled, 182 | myLocationEnabled: _myLocationEnabled, 183 | myLocationButtonEnabled: _myLocationButtonEnabled, 184 | padding: const EdgeInsets.all(10), 185 | onCameraMove: _updateCameraPosition, 186 | ); 187 | 188 | final List columnChildren = [ 189 | Expanded(child: appleMap), 190 | ]; 191 | 192 | if (_isMapCreated) { 193 | columnChildren.addAll([ 194 | Padding( 195 | padding: const EdgeInsets.all(8.0), 196 | child: Column( 197 | children: [ 198 | Text('camera bearing: ${_position.heading}'), 199 | Text( 200 | 'camera target: ${_position.target.latitude.toStringAsFixed(4)},' 201 | '${_position.target.longitude.toStringAsFixed(4)}'), 202 | Text('camera zoom: ${_position.zoom}'), 203 | Text('camera tilt: ${_position.pitch}'), 204 | Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), 205 | Wrap( 206 | alignment: WrapAlignment.spaceEvenly, 207 | children: [ 208 | _compassToggler(), 209 | _mapTypeCycler(), 210 | _zoomBoundsToggler(), 211 | _rotateToggler(), 212 | _scrollToggler(), 213 | _tiltToggler(), 214 | _zoomToggler(), 215 | _myLocationToggler(), 216 | _myLocationButtonToggler(), 217 | ], 218 | ), 219 | ], 220 | ), 221 | ), 222 | ]); 223 | } 224 | return Column(children: columnChildren); 225 | } 226 | 227 | void _updateCameraPosition(CameraPosition position) { 228 | setState(() { 229 | _position = position; 230 | }); 231 | } 232 | 233 | void onMapCreated(AppleMapController controller) { 234 | setState(() { 235 | _isMapCreated = true; 236 | }); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /example/lib/map_update.dart: -------------------------------------------------------------------------------- 1 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 2 | import 'package:apple_maps_flutter_example/page.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class MapUpdatePage extends ExamplePage { 6 | MapUpdatePage() : super(const Icon(Icons.refresh), 'Update Map'); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return _MapUpdate(); 11 | } 12 | } 13 | 14 | class _MapUpdate extends StatefulWidget { 15 | _MapUpdate({Key? key}) : super(key: key); 16 | @override 17 | _MapUpdateState createState() => _MapUpdateState(); 18 | } 19 | 20 | class _MapUpdateState extends State<_MapUpdate> { 21 | @override 22 | Widget build(BuildContext context) { 23 | return AppleMap( 24 | initialCameraPosition: CameraPosition( 25 | target: LatLng(-33.852, 151.211), 26 | zoom: 11, 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example/lib/move_camera.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'page.dart'; 9 | 10 | class MoveCameraPage extends ExamplePage { 11 | MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return const MoveCamera(); 16 | } 17 | } 18 | 19 | class MoveCamera extends StatefulWidget { 20 | const MoveCamera(); 21 | @override 22 | State createState() => MoveCameraState(); 23 | } 24 | 25 | class MoveCameraState extends State { 26 | late AppleMapController mapController; 27 | 28 | void _onMapCreated(AppleMapController controller) { 29 | mapController = controller; 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return SafeArea( 35 | child: Column( 36 | children: [ 37 | Expanded( 38 | child: AppleMap( 39 | onMapCreated: _onMapCreated, 40 | initialCameraPosition: const CameraPosition( 41 | target: LatLng(0.0, 0.0), 42 | ), 43 | ), 44 | ), 45 | Padding( 46 | padding: const EdgeInsets.all(8.0), 47 | child: Wrap( 48 | alignment: WrapAlignment.spaceEvenly, 49 | children: [ 50 | TextButton( 51 | onPressed: () { 52 | mapController.moveCamera( 53 | CameraUpdate.newCameraPosition( 54 | const CameraPosition( 55 | heading: 270.0, 56 | target: LatLng(51.5160895, -0.1294527), 57 | pitch: 30.0, 58 | zoom: 17, 59 | ), 60 | ), 61 | ); 62 | }, 63 | child: const Text('newCameraPosition'), 64 | ), 65 | TextButton( 66 | onPressed: () { 67 | mapController.moveCamera( 68 | CameraUpdate.newLatLng( 69 | const LatLng(56.1725505, 10.1850512), 70 | ), 71 | ); 72 | }, 73 | child: const Text('newLatLng'), 74 | ), 75 | TextButton( 76 | onPressed: () { 77 | mapController.moveCamera( 78 | CameraUpdate.newLatLngZoom( 79 | const LatLng(37.4231613, -122.087159), 80 | 11.0, 81 | ), 82 | ); 83 | }, 84 | child: const Text('newLatLngZoom'), 85 | ), 86 | TextButton( 87 | onPressed: () { 88 | mapController.moveCamera( 89 | CameraUpdate.newLatLngBounds( 90 | LatLngBounds( 91 | southwest: LatLng(37.749146, -122.71829295684518), 92 | northeast: LatLng(37.94134713198227, -122.498971)), 93 | 11.0, 94 | ), 95 | ); 96 | }, 97 | child: const Text('newLatLngBounds'), 98 | ), 99 | TextButton( 100 | onPressed: () { 101 | mapController.moveCamera( 102 | CameraUpdate.zoomBy(-0.5, const Offset(30.0, 20.0)), 103 | ); 104 | }, 105 | child: const Text('zoomBy with focus'), 106 | ), 107 | TextButton( 108 | onPressed: () { 109 | mapController.moveCamera(CameraUpdate.zoomBy(-0.5)); 110 | }, 111 | child: const Text('zoomBy'), 112 | ), 113 | TextButton( 114 | onPressed: () { 115 | mapController.moveCamera(CameraUpdate.zoomIn()); 116 | }, 117 | child: const Text('zoomIn'), 118 | ), 119 | TextButton( 120 | onPressed: () { 121 | mapController.moveCamera(CameraUpdate.zoomOut()); 122 | }, 123 | child: const Text('zoomOut'), 124 | ), 125 | TextButton( 126 | onPressed: () { 127 | mapController.moveCamera(CameraUpdate.zoomTo(16.0)); 128 | }, 129 | child: const Text('zoomTo'), 130 | ), 131 | ], 132 | ), 133 | ), 134 | ], 135 | ), 136 | ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /example/lib/padding.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'page.dart'; 9 | 10 | class PaddingPage extends ExamplePage { 11 | PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SafeArea(child: const MarkerIconsBody()); 16 | } 17 | } 18 | 19 | class MarkerIconsBody extends StatefulWidget { 20 | const MarkerIconsBody(); 21 | 22 | @override 23 | State createState() => MarkerIconsBodyState(); 24 | } 25 | 26 | const LatLng _kMapCenter = LatLng(52.4478, -3.5402); 27 | 28 | class MarkerIconsBodyState extends State { 29 | late AppleMapController controller; 30 | 31 | EdgeInsets _padding = const EdgeInsets.all(0); 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | final AppleMap appleMap = AppleMap( 36 | onMapCreated: _onMapCreated, 37 | initialCameraPosition: const CameraPosition( 38 | target: _kMapCenter, 39 | zoom: 7.0, 40 | ), 41 | padding: _padding, 42 | ); 43 | 44 | final List columnChildren = [ 45 | Expanded(child: appleMap), 46 | Padding( 47 | padding: const EdgeInsets.all(8.0), 48 | child: Column( 49 | children: [ 50 | Text( 51 | "Enter Padding Below", 52 | style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), 53 | ), 54 | ], 55 | ), 56 | ), 57 | _paddingInput(), 58 | _buttons() 59 | ]; 60 | 61 | return Column(children: columnChildren); 62 | } 63 | 64 | void _onMapCreated(AppleMapController controllerParam) { 65 | setState(() { 66 | controller = controllerParam; 67 | }); 68 | } 69 | 70 | TextEditingController _topController = TextEditingController(); 71 | TextEditingController _bottomController = TextEditingController(); 72 | TextEditingController _leftController = TextEditingController(); 73 | TextEditingController _rightController = TextEditingController(); 74 | 75 | Widget _paddingInput() { 76 | return Padding( 77 | padding: const EdgeInsets.all(16.0), 78 | child: Row( 79 | children: [ 80 | Flexible( 81 | flex: 2, 82 | child: TextField( 83 | controller: _topController, 84 | keyboardType: TextInputType.number, 85 | textAlign: TextAlign.center, 86 | decoration: InputDecoration( 87 | hintText: "Top", 88 | ), 89 | ), 90 | ), 91 | Spacer(), 92 | Flexible( 93 | flex: 2, 94 | child: TextField( 95 | controller: _bottomController, 96 | keyboardType: TextInputType.number, 97 | textAlign: TextAlign.center, 98 | decoration: InputDecoration( 99 | hintText: "Bottom", 100 | ), 101 | ), 102 | ), 103 | Spacer(), 104 | Flexible( 105 | flex: 2, 106 | child: TextField( 107 | controller: _leftController, 108 | keyboardType: TextInputType.number, 109 | textAlign: TextAlign.center, 110 | decoration: InputDecoration( 111 | hintText: "Left", 112 | ), 113 | ), 114 | ), 115 | Spacer(), 116 | Flexible( 117 | flex: 2, 118 | child: TextField( 119 | controller: _rightController, 120 | keyboardType: TextInputType.number, 121 | textAlign: TextAlign.center, 122 | decoration: InputDecoration( 123 | hintText: "Right", 124 | ), 125 | ), 126 | ), 127 | ], 128 | ), 129 | ); 130 | } 131 | 132 | Widget _buttons() { 133 | return Padding( 134 | padding: const EdgeInsets.symmetric(horizontal: 16), 135 | child: Row( 136 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 137 | children: [ 138 | TextButton( 139 | child: const Text("Set Padding"), 140 | onPressed: () { 141 | setState(() { 142 | _padding = EdgeInsets.fromLTRB( 143 | double.tryParse(_leftController.value.text) ?? 50, 144 | double.tryParse(_topController.value.text) ?? 50, 145 | double.tryParse(_rightController.value.text) ?? 50, 146 | double.tryParse(_bottomController.value.text) ?? 50, 147 | ); 148 | }); 149 | }, 150 | ), 151 | TextButton( 152 | child: const Text("Reset Padding"), 153 | onPressed: () { 154 | setState(() { 155 | _topController.clear(); 156 | _bottomController.clear(); 157 | _leftController.clear(); 158 | _rightController.clear(); 159 | _padding = const EdgeInsets.all(0); 160 | }); 161 | }, 162 | ) 163 | ], 164 | ), 165 | ); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /example/lib/page.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | 7 | abstract class ExamplePage extends StatelessWidget { 8 | const ExamplePage(this.leading, this.title); 9 | 10 | final Widget leading; 11 | final String title; 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/place_circle.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'dart:typed_data'; 6 | 7 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 8 | // ignore_for_file: public_member_api_docs 9 | 10 | import 'package:flutter/material.dart'; 11 | 12 | import 'page.dart'; 13 | 14 | class PlaceCirclePage extends ExamplePage { 15 | PlaceCirclePage() : super(const Icon(Icons.linear_scale), 'Place circle'); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return const PlaceCircleBody(); 20 | } 21 | } 22 | 23 | class PlaceCircleBody extends StatefulWidget { 24 | const PlaceCircleBody(); 25 | 26 | @override 27 | State createState() => PlaceCircleBodyState(); 28 | } 29 | 30 | class PlaceCircleBodyState extends State { 31 | PlaceCircleBodyState(); 32 | 33 | late AppleMapController controller; 34 | Map circles = {}; 35 | int _circleIdCounter = 1; 36 | CircleId? selectedCircle; 37 | 38 | Uint8List? _imageBytes; 39 | 40 | // Values when toggling circle color 41 | int fillColorsIndex = 0; 42 | int strokeColorsIndex = 0; 43 | List colors = [ 44 | Colors.purple, 45 | Colors.red, 46 | Colors.green, 47 | Colors.pink, 48 | ]; 49 | 50 | // Values when toggling circle stroke width 51 | int widthsIndex = 0; 52 | List widths = [10, 20, 5]; 53 | 54 | void _onMapCreated(AppleMapController controller) { 55 | this.controller = controller; 56 | } 57 | 58 | @override 59 | void dispose() { 60 | super.dispose(); 61 | } 62 | 63 | void _onCircleTapped(CircleId circleId) { 64 | setState(() { 65 | selectedCircle = circleId; 66 | }); 67 | } 68 | 69 | void _remove() { 70 | setState(() { 71 | if (circles.containsKey(selectedCircle)) { 72 | circles.remove(selectedCircle); 73 | } 74 | selectedCircle = null; 75 | }); 76 | } 77 | 78 | void _add() { 79 | final int circleCount = circles.length; 80 | 81 | if (circleCount == 12) { 82 | return; 83 | } 84 | 85 | final String circleIdVal = 'circle_id_$_circleIdCounter'; 86 | _circleIdCounter++; 87 | final CircleId circleId = CircleId(circleIdVal); 88 | 89 | final Circle circle = Circle( 90 | circleId: circleId, 91 | consumeTapEvents: true, 92 | strokeColor: Colors.orange, 93 | fillColor: Colors.green, 94 | strokeWidth: 5, 95 | center: _createCenter(), 96 | radius: 50000, 97 | onTap: () { 98 | _onCircleTapped(circleId); 99 | }, 100 | ); 101 | 102 | setState(() { 103 | circles[circleId] = circle; 104 | }); 105 | } 106 | 107 | void _toggleVisible() { 108 | final Circle circle = circles[selectedCircle]!; 109 | setState(() { 110 | circles[selectedCircle!] = circle.copyWith( 111 | visibleParam: !circle.visible, 112 | ); 113 | }); 114 | } 115 | 116 | void _changeFillColor() { 117 | final Circle circle = circles[selectedCircle]!; 118 | setState(() { 119 | circles[selectedCircle!] = circle.copyWith( 120 | fillColorParam: colors[++fillColorsIndex % colors.length], 121 | ); 122 | }); 123 | } 124 | 125 | void _changeStrokeColor() { 126 | final Circle circle = circles[selectedCircle]!; 127 | setState(() { 128 | circles[selectedCircle!] = circle.copyWith( 129 | strokeColorParam: colors[++strokeColorsIndex % colors.length], 130 | ); 131 | }); 132 | } 133 | 134 | void _changeStrokeWidth() { 135 | final Circle circle = circles[selectedCircle]!; 136 | setState(() { 137 | circles[selectedCircle!] = circle.copyWith( 138 | strokeWidthParam: widths[++widthsIndex % widths.length], 139 | ); 140 | }); 141 | } 142 | 143 | @override 144 | Widget build(BuildContext context) { 145 | return SafeArea( 146 | child: Column( 147 | children: [ 148 | Expanded( 149 | child: AppleMap( 150 | initialCameraPosition: const CameraPosition( 151 | target: LatLng(52.4478, -3.5402), 152 | zoom: 7.0, 153 | ), 154 | circles: Set.of(circles.values), 155 | onMapCreated: _onMapCreated, 156 | ), 157 | ), 158 | Wrap( 159 | alignment: WrapAlignment.spaceEvenly, 160 | children: [ 161 | TextButton( 162 | child: const Text('add'), 163 | onPressed: _add, 164 | ), 165 | TextButton( 166 | child: const Text('remove'), 167 | onPressed: (selectedCircle == null) ? null : _remove, 168 | ), 169 | TextButton( 170 | child: const Text('toggle visible'), 171 | onPressed: (selectedCircle == null) ? null : _toggleVisible, 172 | ), 173 | TextButton( 174 | child: const Text('change zIndex'), 175 | onPressed: _changeZIndex, 176 | ), 177 | TextButton( 178 | child: const Text('change stroke width'), 179 | onPressed: (selectedCircle == null) ? null : _changeStrokeWidth, 180 | ), 181 | TextButton( 182 | child: const Text('change stroke color'), 183 | onPressed: (selectedCircle == null) ? null : _changeStrokeColor, 184 | ), 185 | TextButton( 186 | child: const Text('change fill color'), 187 | onPressed: (selectedCircle == null) ? null : _changeFillColor, 188 | ), 189 | TextButton( 190 | child: Text('Take a snapshot'), 191 | onPressed: () async { 192 | final imageBytes = await this 193 | .controller 194 | .takeSnapshot(SnapshotOptions(showOverlays: true)); 195 | setState(() { 196 | _imageBytes = imageBytes; 197 | }); 198 | }, 199 | ), 200 | Container( 201 | decoration: BoxDecoration(color: Colors.blueGrey[50]), 202 | height: 180, 203 | child: _imageBytes != null ? Image.memory(_imageBytes!) : null, 204 | ), 205 | ], 206 | ), 207 | ], 208 | ), 209 | ); 210 | } 211 | 212 | Future _changeZIndex() async { 213 | final Circle circle = circles[selectedCircle]!; 214 | final int current = circle.zIndex ?? 0; 215 | setState(() { 216 | circles[selectedCircle!] = circle.copyWith( 217 | zIndexParam: current == 12 ? 0 : current + 1, 218 | ); 219 | }); 220 | } 221 | 222 | LatLng _createCenter() { 223 | final double offset = _circleIdCounter.ceilToDouble(); 224 | return _createLatLng(51.4816 + offset * 0.2, -3.1791); 225 | } 226 | 227 | LatLng _createLatLng(double lat, double lng) { 228 | return LatLng(lat, lng); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /example/lib/scrolling_map.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | // ignore_for_file: public_member_api_docs 7 | 8 | import 'package:flutter/foundation.dart'; 9 | import 'package:flutter/gestures.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:flutter/rendering.dart'; 12 | 13 | import 'page.dart'; 14 | 15 | class ScrollingMapPage extends ExamplePage { 16 | ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const ScrollingMapBody(); 21 | } 22 | } 23 | 24 | class ScrollingMapBody extends StatelessWidget { 25 | const ScrollingMapBody(); 26 | 27 | final LatLng center = const LatLng(32.080664, 34.9563837); 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return SafeArea( 32 | bottom: false, 33 | child: ListView( 34 | children: [ 35 | Card( 36 | child: Padding( 37 | padding: const EdgeInsets.all(16), 38 | child: Column( 39 | children: [ 40 | const Padding( 41 | padding: EdgeInsets.only(bottom: 12.0), 42 | child: Text('This map consumes all touch events.'), 43 | ), 44 | SizedBox( 45 | height: 300.0, 46 | child: AppleMap( 47 | initialCameraPosition: CameraPosition( 48 | target: center, 49 | zoom: 11.0, 50 | ), 51 | gestureRecognizers: 52 | >[ 53 | Factory( 54 | () => EagerGestureRecognizer(), 55 | ), 56 | ].toSet(), 57 | ), 58 | ), 59 | ], 60 | ), 61 | ), 62 | ), 63 | Card( 64 | child: Padding( 65 | padding: const EdgeInsets.all(16), 66 | child: Column( 67 | children: [ 68 | const Text('This map doesn\'t consume the vertical drags.'), 69 | const Padding( 70 | padding: EdgeInsets.only(bottom: 12.0), 71 | child: Text( 72 | 'It still gets other gestures (e.g scale or tap).'), 73 | ), 74 | Center( 75 | child: SizedBox( 76 | height: 300.0, 77 | child: AppleMap( 78 | initialCameraPosition: CameraPosition( 79 | target: center, 80 | zoom: 11.0, 81 | ), 82 | annotations: Set.of( 83 | [ 84 | Annotation( 85 | annotationId: AnnotationId("test_Annotation_id"), 86 | position: LatLng( 87 | center.latitude, 88 | center.longitude, 89 | ), 90 | infoWindow: const InfoWindow( 91 | title: 'An interesting location', 92 | snippet: '*', 93 | ), 94 | ) 95 | ], 96 | ), 97 | gestureRecognizers: 98 | >[ 99 | Factory( 100 | () => ScaleGestureRecognizer(), 101 | ), 102 | ].toSet(), 103 | insetsLayoutMarginsFromSafeArea: false, 104 | ), 105 | ), 106 | ), 107 | ], 108 | ), 109 | ), 110 | ), 111 | ], 112 | ), 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /example/lib/snapshot.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // ignore_for_file: public_member_api_docs 6 | 7 | import 'dart:typed_data'; 8 | 9 | import 'package:apple_maps_flutter_example/page.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 12 | 13 | const CameraPosition _kInitialPosition = 14 | CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); 15 | 16 | class SnapshotPage extends ExamplePage { 17 | SnapshotPage() 18 | : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return _SnapshotBody(); 23 | } 24 | } 25 | 26 | class _SnapshotBody extends StatefulWidget { 27 | @override 28 | _SnapshotBodyState createState() => _SnapshotBodyState(); 29 | } 30 | 31 | class _SnapshotBodyState extends State<_SnapshotBody> { 32 | AppleMapController? _mapController; 33 | Uint8List? _imageBytes; 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Padding( 38 | padding: const EdgeInsets.all(16), 39 | child: Column( 40 | crossAxisAlignment: CrossAxisAlignment.stretch, 41 | children: [ 42 | SizedBox( 43 | height: 180, 44 | child: AppleMap( 45 | onMapCreated: onMapCreated, 46 | initialCameraPosition: _kInitialPosition, 47 | ), 48 | ), 49 | TextButton( 50 | child: Text('Take a snapshot'), 51 | onPressed: () async { 52 | final imageBytes = await _mapController?.takeSnapshot(); 53 | setState(() { 54 | _imageBytes = imageBytes; 55 | }); 56 | }, 57 | ), 58 | Container( 59 | decoration: BoxDecoration(color: Colors.blueGrey[50]), 60 | height: 180, 61 | child: _imageBytes != null ? Image.memory(_imageBytes!) : null, 62 | ), 63 | ], 64 | ), 65 | ); 66 | } 67 | 68 | void onMapCreated(AppleMapController controller) { 69 | _mapController = controller; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: apple_maps_flutter_example 2 | description: Demonstrates how to use the apple_maps_flutter plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.2 31 | 32 | dev_dependencies: 33 | apple_maps_flutter: 34 | path: ../ 35 | flutter_driver: 36 | sdk: flutter 37 | test: any 38 | 39 | # For information on the generic Dart part of this file, see the 40 | # following page: https://dart.dev/tools/pub/pubspec 41 | 42 | # The following section is specific to Flutter. 43 | flutter: 44 | 45 | # The following line ensures that the Material Icons font is 46 | # included with your application, so that you can use the icons in 47 | # the material Icons class. 48 | uses-material-design: true 49 | 50 | # To add assets to your application, add an assets section, like this: 51 | assets: 52 | - assets/ 53 | 54 | # An image asset can refer to one or more resolution-specific "variants", see 55 | # https://flutter.dev/assets-and-images/#resolution-aware. 56 | 57 | # For details regarding adding assets from package dependencies, see 58 | # https://flutter.dev/assets-and-images/#from-packages 59 | 60 | # To add custom fonts to your application, add a fonts section here, 61 | # in this "flutter" section. Each entry in this list should have a 62 | # "family" key with the font family name, and a "fonts" key with a 63 | # list giving the asset and other descriptors for the font. For 64 | # example: 65 | # fonts: 66 | # - family: Schyler 67 | # fonts: 68 | # - asset: fonts/Schyler-Regular.ttf 69 | # - asset: fonts/Schyler-Italic.ttf 70 | # style: italic 71 | # - family: Trajan Pro 72 | # fonts: 73 | # - asset: fonts/TrajanPro.ttf 74 | # - asset: fonts/TrajanPro_Bold.ttf 75 | # weight: 700 76 | # 77 | # For details regarding fonts from package dependencies, 78 | # see https://flutter.dev/custom-fonts/#from-packages 79 | -------------------------------------------------------------------------------- /example/test_driver/apple_map_inspector.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019, the Chromium project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:apple_maps_flutter/apple_maps_flutter.dart'; 6 | import 'package:flutter/services.dart'; 7 | 8 | /// Inspect Apple Maps state using the platform SDK. 9 | /// 10 | /// This class is primarily used for testing. The methods on this 11 | /// class should call "getters" on the AppleMap object or equivalent 12 | /// on the platform side. 13 | class AppleMapInspector { 14 | AppleMapInspector(this._channel); 15 | 16 | final MethodChannel _channel; 17 | 18 | Future isCompassEnabled() async { 19 | return await _channel.invokeMethod('map#isCompassEnabled'); 20 | } 21 | 22 | Future getMinMaxZoomLevels() async { 23 | final List zoomLevels = 24 | (await _channel.invokeMethod>('map#getMinMaxZoomLevels'))! 25 | .cast(); 26 | return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]); 27 | } 28 | 29 | Future isZoomGesturesEnabled() async { 30 | return await _channel.invokeMethod('map#isZoomGesturesEnabled'); 31 | } 32 | 33 | Future isRotateGesturesEnabled() async { 34 | return await _channel.invokeMethod('map#isRotateGesturesEnabled'); 35 | } 36 | 37 | Future isPitchGesturesEnabled() async { 38 | return await _channel.invokeMethod('map#isPitchGesturesEnabled'); 39 | } 40 | 41 | Future isScrollGesturesEnabled() async { 42 | return await _channel.invokeMethod('map#isScrollGesturesEnabled'); 43 | } 44 | 45 | Future isMyLocationButtonEnabled() async { 46 | return await _channel.invokeMethod('map#isMyLocationButtonEnabled'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/test_driver/apple_maps_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019, the Chromium project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'dart:async'; 6 | 7 | import 'package:flutter_driver/flutter_driver.dart'; 8 | 9 | Future main() async { 10 | final FlutterDriver driver = await FlutterDriver.connect(); 11 | await driver.requestData(null, timeout: const Duration(minutes: 1)); 12 | driver.close(); 13 | } 14 | -------------------------------------------------------------------------------- /example/test_driver/test_widgets.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019, the Chromium project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'dart:async'; 6 | 7 | import 'package:flutter/widgets.dart'; 8 | 9 | Future pumpWidget(Widget widget) { 10 | runApp(widget); 11 | return WidgetsBinding.instance!.endOfFrame; 12 | } 13 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/apple_maps_flutter/2848c569b9b45ecb1268b845a188deebee7f79ae/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/Annotations/AnnotationDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnnotationDelegate.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 01.07.21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | protocol AnnotationDelegate: AnyObject { 12 | func getAnnotationView(annotation: FlutterAnnotation) -> MKAnnotationView 13 | func annotationsToAdd(annotations :NSArray) 14 | func annotationsToChange(annotations: NSArray) 15 | func annotationsIdsToRemove(annotationIds: NSArray) 16 | func onAnnotationClick(annotation :MKAnnotation) 17 | func selectAnnotation(with id: String) 18 | func hideAnnotation(with id: String) 19 | func isAnnotationSelected(with id: String) -> Bool 20 | func removeAllAnnotations() 21 | } 22 | -------------------------------------------------------------------------------- /ios/Classes/Annotations/AnnotationIcon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnnotationIcon.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 07.03.20. 6 | // 7 | 8 | import Foundation 9 | 10 | enum IconType { 11 | case PIN, MARKER, CUSTOM_FROM_ASSET, CUSTOM_FROM_BYTES 12 | } 13 | 14 | class AnnotationIcon: Equatable { 15 | 16 | var iconType: IconType 17 | var id: String 18 | var image: UIImage? 19 | var hueColor: Double? 20 | 21 | public init(id: String, iconType: IconType) { 22 | self.iconType = iconType 23 | self.id = id 24 | } 25 | 26 | public init(id: String, iconType: IconType, hueColor: Double) { 27 | self.iconType = iconType 28 | self.id = id 29 | self.hueColor = hueColor 30 | } 31 | 32 | public init(withAsset name: String, id: String, iconScale: CGFloat? = 1.0) { 33 | self.iconType = .CUSTOM_FROM_ASSET 34 | self.id = id 35 | if let uiImage: UIImage = UIImage.init(named: name) { 36 | self.image = self.scaleImage(image: uiImage, scale: iconScale!) 37 | } 38 | } 39 | 40 | public init(fromBytes bytes: FlutterStandardTypedData, id: String) { 41 | let screenScale = UIScreen.main.scale 42 | let image = UIImage.init(data: bytes.data, scale: screenScale) 43 | self.image = image 44 | self.iconType = .CUSTOM_FROM_BYTES 45 | self.id = id 46 | } 47 | 48 | public convenience init() { 49 | self.init(id: "", iconType: .PIN) 50 | } 51 | 52 | private func scaleImage(image: UIImage, scale: CGFloat) -> UIImage { 53 | guard let cgImage = image.cgImage else { 54 | return image 55 | } 56 | guard abs(scale - 1) >= 0 else { 57 | return image 58 | } 59 | return UIImage.init(cgImage: cgImage, scale: 4.0, orientation: image.imageOrientation) 60 | } 61 | 62 | static func == (lhs: AnnotationIcon, rhs: AnnotationIcon) -> Bool { 63 | return lhs.iconType == rhs.iconType && lhs.id == rhs.id && lhs.image == rhs.image 64 | } 65 | 66 | static func != (lhs: AnnotationIcon, rhs: AnnotationIcon) -> Bool { 67 | return !(lhs == rhs) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ios/Classes/Annotations/FlutterAnnotation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlutterAnnotation.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 07.03.20. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | class FlutterAnnotation: NSObject, MKAnnotation { 12 | @objc dynamic var coordinate: CLLocationCoordinate2D 13 | var id: String! 14 | var title: String? 15 | var subtitle: String? 16 | var infoWindowConsumesTapEvents: Bool = false 17 | var image: UIImage? 18 | var alpha: Double? 19 | var anchor: Offset = Offset() 20 | var isDraggable: Bool? 21 | var wasDragged: Bool = false 22 | var isVisible: Bool? = true 23 | var zIndex: Double = -1 24 | var calloutOffset: Offset = Offset() 25 | var icon: AnnotationIcon = AnnotationIcon.init() 26 | var selectedProgrammatically: Bool = false 27 | 28 | public init(fromDictionary annotationData: Dictionary, registrar: FlutterPluginRegistrar) { 29 | let position: Array = annotationData["position"] as! Array 30 | let infoWindow: Dictionary = annotationData["infoWindow"] as! Dictionary 31 | let lat: Double = position[0] 32 | let long: Double = position[1] 33 | self.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long) 34 | self.title = infoWindow["title"] as? String 35 | self.subtitle = infoWindow["snippet"] as? String 36 | self.infoWindowConsumesTapEvents = infoWindow["consumesTapEvents"] as? Bool ?? false 37 | self.id = annotationData["annotationId"] as? String 38 | self.isVisible = annotationData["visible"] as? Bool 39 | self.isDraggable = annotationData["draggable"] as? Bool 40 | if let zIndex = annotationData["zIndex"] as? Double { 41 | self.zIndex = zIndex 42 | } 43 | 44 | if let alpha: Double = annotationData["alpha"] as? Double { 45 | self.alpha = alpha 46 | } 47 | 48 | if let anchorJSON: Array = annotationData["anchor"] as? Array { 49 | self.anchor = Offset(from: anchorJSON) 50 | } 51 | 52 | if let iconData: Array = annotationData["icon"] as? Array { 53 | self.icon = FlutterAnnotation.getAnnotationIcon(iconData: iconData, registrar: registrar, annotationId: id) 54 | } 55 | 56 | if let calloutOffsetJSON = infoWindow["anchor"] as? Array { 57 | self.calloutOffset = Offset(from: calloutOffsetJSON) 58 | } 59 | } 60 | 61 | 62 | static private func getAnnotationIcon(iconData: Array, registrar: FlutterPluginRegistrar, annotationId: String) -> AnnotationIcon { 63 | let iconTypeMap: Dictionary = ["fromAssetImage": .CUSTOM_FROM_ASSET, "fromBytes": .CUSTOM_FROM_BYTES, "defaultAnnotation": .PIN, "markerAnnotation": .MARKER] 64 | let iconType: IconType = iconTypeMap[iconData[0] as! String] ?? .PIN 65 | var icon: AnnotationIcon = AnnotationIcon(id: annotationId, iconType: iconType) 66 | var scaleParam: CGFloat? 67 | 68 | if iconType == .CUSTOM_FROM_ASSET { 69 | let assetPath: String = iconData[1] as! String 70 | scaleParam = CGFloat(iconData[2] as? Double ?? 1.0) 71 | icon = AnnotationIcon(withAsset: registrar.lookupKey(forAsset: assetPath), id: annotationId, iconScale: scaleParam) 72 | } else if iconType == .CUSTOM_FROM_BYTES { 73 | icon = AnnotationIcon(fromBytes: iconData[1] as! FlutterStandardTypedData, id: annotationId) 74 | } else if iconData.count > 1 { 75 | icon = AnnotationIcon(id: annotationId, iconType: iconType, hueColor: iconData[1] as! Double) 76 | 77 | } 78 | return icon 79 | } 80 | 81 | static func == (lhs: FlutterAnnotation, rhs: FlutterAnnotation) -> Bool { 82 | return lhs.id == rhs.id && lhs.title == rhs.title && lhs.subtitle == rhs.subtitle && lhs.image == rhs.image && lhs.alpha == rhs.alpha && lhs.isDraggable == rhs.isDraggable && lhs.wasDragged == rhs.wasDragged && lhs.isVisible == rhs.isVisible && lhs.icon == rhs.icon && lhs.coordinate.latitude == rhs.coordinate.latitude && lhs.coordinate.longitude == rhs.coordinate.longitude && lhs.infoWindowConsumesTapEvents == rhs.infoWindowConsumesTapEvents && lhs.anchor == rhs.anchor && lhs.calloutOffset == rhs.calloutOffset && lhs.coordinate.latitude == rhs.coordinate.latitude && lhs.coordinate.longitude == rhs.coordinate.longitude && lhs.zIndex == rhs.zIndex 83 | } 84 | 85 | static func != (lhs: FlutterAnnotation, rhs: FlutterAnnotation) -> Bool { 86 | return !(lhs == rhs) 87 | } 88 | } 89 | 90 | struct Offset { 91 | let x: Double 92 | let y: Double 93 | 94 | public init(from json: Array) { 95 | self.x = json[0] 96 | self.y = json[1] 97 | } 98 | 99 | public init() { 100 | self.x = 0 101 | self.y = 0 102 | } 103 | 104 | static func == (lhs: Offset, rhs: Offset) -> Bool { 105 | return lhs.x == rhs.x && lhs.y == rhs.y 106 | } 107 | 108 | static func != (lhs: Offset, rhs: Offset) -> Bool { 109 | return !(lhs == rhs) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /ios/Classes/Annotations/FlutterAnnotationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlutterAnnotationView.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 30.03.21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | protocol ZPositionableAnnotation { 12 | var stickyZPosition: CGFloat { 13 | get 14 | set 15 | } 16 | } 17 | 18 | class FlutterAnnotationView: MKAnnotationView { 19 | 20 | /// Override the layer factory for this class to return a custom CALayer class 21 | override class var layerClass: AnyClass { 22 | return ZPositionableLayer.self 23 | } 24 | 25 | /// convenience accessor for setting zPosition 26 | var stickyZPosition: CGFloat { 27 | get { 28 | return (self.layer as! ZPositionableLayer).stickyZPosition 29 | } 30 | set { 31 | (self.layer as! ZPositionableLayer).stickyZPosition = newValue 32 | } 33 | } 34 | } 35 | 36 | @available(iOS 11.0, *) 37 | class FlutterMarkerAnnotationView: MKMarkerAnnotationView { 38 | /// Override the layer factory for this class to return a custom CALayer class 39 | override class var layerClass: AnyClass { 40 | return ZPositionableLayer.self 41 | } 42 | } 43 | 44 | @available(iOS 11.0, *) 45 | extension FlutterMarkerAnnotationView: ZPositionableAnnotation { 46 | /// convenience accessor for setting zPosition 47 | var stickyZPosition: CGFloat { 48 | get { 49 | return (self.layer as! ZPositionableLayer).stickyZPosition 50 | } 51 | set { 52 | (self.layer as! ZPositionableLayer).stickyZPosition = newValue 53 | } 54 | } 55 | } 56 | 57 | /// iOS 11 automagically manages the CALayer zPosition, which breaks manual z-ordering. 58 | /// This subclass just throws away any values which the OS sets for zPosition, and provides 59 | /// a specialized accessor for setting the zPosition 60 | private class ZPositionableLayer: CALayer { 61 | 62 | /// no-op accessor for setting the zPosition 63 | override var zPosition: CGFloat { 64 | get { 65 | return super.zPosition 66 | } 67 | set { 68 | // do nothing 69 | } 70 | } 71 | 72 | /// specialized accessor for setting the zPosition 73 | var stickyZPosition: CGFloat { 74 | get { 75 | return super.zPosition 76 | } 77 | set { 78 | super.zPosition = newValue 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ios/Classes/AppleMapsFlutterPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppleMapsFlutterPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/AppleMapsFlutterPlugin.m: -------------------------------------------------------------------------------- 1 | #import "AppleMapsFlutterPlugin.h" 2 | #import 3 | 4 | @implementation AppleMapsFlutterPlugin 5 | + (void)registerWithRegistrar:(NSObject*)registrar { 6 | [SwiftAppleMapsFlutterPlugin registerWithRegistrar:registrar]; 7 | } 8 | @end 9 | -------------------------------------------------------------------------------- /ios/Classes/MapView/AppleMapsViewFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppleMapsViewFactory.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 01.07.21. 6 | // 7 | 8 | import Foundation 9 | 10 | public class AppleMapViewFactory: NSObject, FlutterPlatformViewFactory { 11 | 12 | var registrar: FlutterPluginRegistrar 13 | 14 | public init(withRegistrar registrar: FlutterPluginRegistrar){ 15 | self.registrar = registrar 16 | super.init() 17 | } 18 | 19 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 20 | let argsDictionary = args as! Dictionary 21 | 22 | return AppleMapController(withFrame: frame, withRegistrar: registrar, withargs: argsDictionary, withId: viewId) 23 | } 24 | 25 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 26 | return FlutterStandardMessageCodec(readerWriter: FlutterStandardReaderWriter()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/FlutterOverlay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlutterOverlay.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 13.02.22. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | protocol FlutterOverlay: MKOverlay { 12 | func getCAShapeLayer(snapshot: MKMapSnapshotter.Snapshot) -> CAShapeLayer; 13 | } 14 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/Polygons/FlutterPolygon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlutterPolygon.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 07.03.20. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | class FlutterPolygon: MKPolygon { 12 | var strokeColor: UIColor? 13 | var fillColor: UIColor? 14 | var isConsumingTapEvents: Bool? 15 | var width: CGFloat? 16 | var isVisible: Bool? 17 | var id: String? 18 | var zIndex: Int? = -1 19 | var coordinates: [CLLocationCoordinate2D]? 20 | 21 | convenience init(fromDictionaray polygonData: Dictionary) { 22 | let points = polygonData["points"] as! NSArray 23 | var _points: [CLLocationCoordinate2D] = [] 24 | for point in points { 25 | if let _point: NSArray = point as? NSArray { 26 | _points.append(CLLocationCoordinate2D.init(latitude: _point[0] as! CLLocationDegrees, longitude: _point[1] as! CLLocationDegrees)) 27 | } 28 | } 29 | self.init(coordinates: _points, count: points.count) 30 | self.coordinates = _points 31 | self.strokeColor = JsonConversions.convertColor(data: polygonData["strokeColor"] as! NSNumber) 32 | self.fillColor = JsonConversions.convertColor(data: polygonData["fillColor"] as! NSNumber) 33 | self.isConsumingTapEvents = polygonData["consumeTapEvents"] as? Bool 34 | self.width = polygonData["strokeWidth"] as? CGFloat 35 | self.id = polygonData["polygonId"] as? String 36 | self.isVisible = polygonData["visible"] as? Bool 37 | self.zIndex = polygonData["zIndex"] as? Int 38 | } 39 | 40 | static func == (lhs: FlutterPolygon, rhs: FlutterPolygon) -> Bool { 41 | return lhs.strokeColor == rhs.strokeColor && lhs.fillColor == rhs.fillColor && lhs.isConsumingTapEvents == rhs.isConsumingTapEvents && lhs.width == rhs.width && lhs.isVisible == rhs.isVisible && lhs.zIndex == rhs.zIndex && lhs.coordinate.latitude == rhs.coordinate.latitude && lhs.coordinate.longitude == rhs.coordinate.longitude 42 | } 43 | 44 | static func != (lhs: FlutterPolygon, rhs: FlutterPolygon) -> Bool { 45 | return !(lhs == rhs) 46 | } 47 | } 48 | 49 | extension FlutterPolygon: FlutterOverlay { 50 | func getCAShapeLayer(snapshot: MKMapSnapshotter.Snapshot) -> CAShapeLayer { 51 | let path = UIBezierPath() 52 | let shapeLayer = CAShapeLayer() 53 | 54 | if !(self.isVisible ?? true) { 55 | return shapeLayer 56 | } 57 | 58 | 59 | // Thus we use snapshot.point() to save the pain. 60 | path.move(to: snapshot.point(for: self.coordinates![0])) 61 | for coordinate in self.coordinates! { 62 | path.addLine(to: snapshot.point(for: coordinate)) 63 | } 64 | 65 | path.addLine(to: snapshot.point(for: self.coordinates![0])) 66 | path.close() 67 | 68 | shapeLayer.path = path.cgPath 69 | shapeLayer.lineWidth = self.width ?? 0 70 | shapeLayer.strokeColor = self.strokeColor?.cgColor ?? UIColor.clear.cgColor 71 | shapeLayer.fillColor = self.fillColor?.cgColor ?? UIColor.clear.cgColor 72 | shapeLayer.lineCap = .round 73 | shapeLayer.lineJoin = .round 74 | 75 | return shapeLayer 76 | } 77 | } 78 | 79 | 80 | public extension MKPolygon { 81 | func contains(coordinate: CLLocationCoordinate2D) -> Bool { 82 | let polygonRenderer = MKPolygonRenderer(polygon: self) 83 | let currentMapPoint: MKMapPoint = MKMapPoint(coordinate) 84 | let polygonViewPoint: CGPoint = polygonRenderer.point(for: currentMapPoint) 85 | if polygonRenderer.path == nil { 86 | return false 87 | } else{ 88 | return polygonRenderer.path.contains(polygonViewPoint) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/Polygons/PolygonController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolygonController.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 07.03.20. 6 | // 7 | 8 | 9 | import Foundation 10 | import MapKit 11 | 12 | extension AppleMapController: PolygonDelegate { 13 | func polygonRenderer(overlay: MKOverlay) -> MKOverlayRenderer { 14 | // Make sure we are rendering a polygon. 15 | guard let polygon = overlay as? MKPolygon else { 16 | return MKOverlayRenderer() 17 | } 18 | let polygonRenderer = MKPolygonRenderer(overlay: polygon) 19 | 20 | if let flutterPolygon: FlutterPolygon = overlay as? FlutterPolygon { 21 | if flutterPolygon.isVisible! { 22 | polygonRenderer.strokeColor = flutterPolygon.strokeColor 23 | polygonRenderer.fillColor = flutterPolygon.fillColor 24 | polygonRenderer.lineWidth = flutterPolygon.width ?? 1.0 25 | } else { 26 | polygonRenderer.strokeColor = UIColor.clear 27 | polygonRenderer.lineWidth = 0.0 28 | } 29 | } 30 | return polygonRenderer 31 | } 32 | 33 | func addPolygons(polygonData data: NSArray) { 34 | for _polygon in data { 35 | let polygonData :Dictionary = _polygon as! Dictionary 36 | let polygon = FlutterPolygon(fromDictionaray: polygonData) 37 | addPolygon(polygon: polygon) 38 | } 39 | } 40 | 41 | func changePolygons(polygonData data: NSArray) { 42 | let oldOverlays: [MKOverlay] = self.mapView.overlays 43 | for oldOverlay in oldOverlays { 44 | if oldOverlay is FlutterPolygon { 45 | let oldFlutterPolygon = oldOverlay as! FlutterPolygon 46 | for _polygon in data { 47 | let polygonData :Dictionary = _polygon as! Dictionary 48 | if oldFlutterPolygon.id == (polygonData["polygonId"] as! String) { 49 | let newPolygon = FlutterPolygon.init(fromDictionaray: polygonData) 50 | if oldFlutterPolygon != newPolygon { 51 | updatePolygonsOnMap(oldPolygon: oldFlutterPolygon, newPolygon: newPolygon) 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | func removePolygons(polygonIds: NSArray) { 60 | for overlay in self.mapView.overlays { 61 | if let polygon = overlay as? FlutterPolygon { 62 | if polygonIds.contains(polygon.id!) { 63 | self.mapView.removeOverlay(polygon) 64 | } 65 | } 66 | } 67 | } 68 | 69 | func removeAllPolygons() { 70 | for overlay in self.mapView.overlays { 71 | if let polygon = overlay as? FlutterPolygon { 72 | self.mapView.removeOverlay(polygon) 73 | } 74 | } 75 | } 76 | 77 | private func updatePolygonsOnMap(oldPolygon: FlutterPolygon, newPolygon: FlutterPolygon) { 78 | self.mapView.removeOverlay(oldPolygon) 79 | addPolygon(polygon: newPolygon) 80 | } 81 | 82 | private func addPolygon(polygon: FlutterPolygon) { 83 | if polygon.zIndex == nil || polygon.zIndex == -1 { 84 | self.mapView.addOverlay(polygon) 85 | } else { 86 | self.mapView.insertOverlay(polygon, at: polygon.zIndex ?? 0) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/Polygons/PolygonDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolygonDelegate.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 01.07.21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | protocol PolygonDelegate { 12 | func polygonRenderer(overlay: MKOverlay) -> MKOverlayRenderer 13 | func addPolygons(polygonData data: NSArray) 14 | func changePolygons(polygonData data: NSArray) 15 | func removePolygons(polygonIds: NSArray) 16 | func removeAllPolygons() 17 | } 18 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/Polylines/PolylineController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolylineController.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 12.11.19. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | extension AppleMapController: PolylineDelegate { 12 | 13 | func polylineRenderer(overlay: MKOverlay) -> MKOverlayRenderer { 14 | // Make sure we are rendering a polyline. 15 | guard let polyline = overlay as? MKPolyline else { 16 | return MKOverlayRenderer() 17 | } 18 | let polylineRenderer = MKPolylineRenderer(overlay: polyline) 19 | 20 | if let flutterPolyline: FlutterPolyline = overlay as? FlutterPolyline { 21 | if flutterPolyline.isVisible! { 22 | polylineRenderer.strokeColor = flutterPolyline.color 23 | polylineRenderer.lineWidth = flutterPolyline.width ?? 1.0 24 | polylineRenderer.lineDashPattern = flutterPolyline.pattern 25 | polylineRenderer.lineJoin = flutterPolyline.lineJoin! 26 | polylineRenderer.lineCap = flutterPolyline.capType! 27 | } else { 28 | polylineRenderer.strokeColor = UIColor.clear 29 | polylineRenderer.lineWidth = 0.0 30 | } 31 | } 32 | return polylineRenderer 33 | } 34 | 35 | func addPolylines(polylineData data: NSArray) { 36 | for _polyline in data { 37 | let polylineData :Dictionary = _polyline as! Dictionary 38 | let polyline = FlutterPolyline(fromDictionaray: polylineData) 39 | addPolyline(polyline: polyline) 40 | } 41 | } 42 | 43 | func changePolylines(polylineData data: NSArray) { 44 | let oldOverlays: [MKOverlay] = self.mapView.overlays 45 | for oldOverlay in oldOverlays { 46 | if oldOverlay is FlutterPolyline { 47 | let oldFlutterPolyline = oldOverlay as! FlutterPolyline 48 | for _polyline in data { 49 | let polylineData :Dictionary = _polyline as! Dictionary 50 | if oldFlutterPolyline.id == (polylineData["polylineId"] as! String) { 51 | let newPolyline = FlutterPolyline.init(fromDictionaray: polylineData) 52 | if oldFlutterPolyline != newPolyline { 53 | updatePolylinesOnMap(oldPolyline: oldFlutterPolyline, newPolyline: newPolyline) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | func removePolylines(polylineIds: NSArray) { 62 | for overlay in self.mapView.overlays { 63 | if let polyline = overlay as? FlutterPolyline { 64 | if polylineIds.contains(polyline.id!) { 65 | self.mapView.removeOverlay(polyline) 66 | } 67 | } 68 | } 69 | } 70 | 71 | func removeAllPolylines() { 72 | for overlay in self.mapView.overlays { 73 | if let polyline = overlay as? FlutterPolyline { 74 | self.mapView.removeOverlay(polyline) 75 | } 76 | } 77 | } 78 | 79 | private func updatePolylinesOnMap(oldPolyline: FlutterPolyline, newPolyline: FlutterPolyline) { 80 | self.mapView.removeOverlay(oldPolyline) 81 | addPolyline(polyline: newPolyline) 82 | } 83 | 84 | private func addPolyline(polyline: FlutterPolyline) { 85 | if polyline.zIndex == nil || polyline.zIndex == -1 { 86 | self.mapView.addOverlay(polyline) 87 | } else { 88 | self.mapView.insertOverlay(polyline, at: polyline.zIndex ?? 0) 89 | } 90 | } 91 | 92 | private func linePatternToArray(patternData: NSArray?, lineWidth: CGFloat?) -> [NSNumber] { 93 | var finalPattern: [NSNumber] = [] 94 | var isDot: Bool = false 95 | if patternData == nil { 96 | return finalPattern 97 | } 98 | for pattern in patternData! { 99 | if let _pattern: NSArray = pattern as? NSArray { 100 | if _pattern.count > 0 { 101 | if let identifier: String = _pattern[0] as? String { 102 | if identifier == "dot" { 103 | isDot = true 104 | finalPattern.append(0) 105 | } else if identifier == "dash" { 106 | isDot = false 107 | finalPattern.append(NSNumber(value: lround((_pattern[1] as! Double) * 1/3.5))) 108 | } else if identifier == "gap" { 109 | if let length = _pattern[1] as? Double { 110 | if isDot { 111 | finalPattern.append(NSNumber(value: lround(Double((lineWidth ?? 0) * 1.5)))) 112 | } else { 113 | finalPattern.append(NSNumber(value: lround(length * 1/3.5))) 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | return finalPattern 122 | } 123 | 124 | private func getLineCapForLinePattern(linePatternData: NSArray?) -> CGLineCap { 125 | if linePatternData == nil { 126 | return CGLineCap.butt 127 | } 128 | for pattern in linePatternData! { 129 | if let _pattern = pattern as? NSArray { 130 | if _pattern.contains("dot") { 131 | return CGLineCap.round 132 | } else if _pattern.contains("dash") { 133 | return CGLineCap.butt 134 | } 135 | } 136 | } 137 | return CGLineCap.butt 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/Polylines/PolylineDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolylineDelegate.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 01.07.21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | protocol PolylineDelegate { 12 | func polylineRenderer(overlay: MKOverlay) -> MKOverlayRenderer 13 | func addPolylines(polylineData data: NSArray) 14 | func changePolylines(polylineData data: NSArray) 15 | func removePolylines(polylineIds: NSArray) 16 | func removeAllPolylines() 17 | } 18 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/circles/CircleController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleController.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 08.03.20. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | extension AppleMapController: CircleDelegate { 12 | 13 | func circleRenderer(overlay: MKOverlay) -> MKOverlayRenderer { 14 | // Make sure we are rendering a circle. 15 | guard let circle = overlay as? MKCircle else { 16 | return MKOverlayRenderer() 17 | } 18 | let circleRenderer = MKCircleRenderer(overlay: circle) 19 | 20 | if let flutterCircle: FlutterCircle = overlay as? FlutterCircle { 21 | if flutterCircle.isVisible! { 22 | circleRenderer.strokeColor = flutterCircle.strokeColor 23 | circleRenderer.fillColor = flutterCircle.fillColor 24 | circleRenderer.lineWidth = flutterCircle.strokeWidth ?? 1.0 25 | } else { 26 | circleRenderer.strokeColor = UIColor.clear 27 | circleRenderer.lineWidth = 0.0 28 | } 29 | } 30 | return circleRenderer 31 | } 32 | 33 | func addCircles(circleData data: NSArray) { 34 | for _circle in data { 35 | let circleData :Dictionary = _circle as! Dictionary 36 | let circle = FlutterCircle(fromDictionaray: circleData) 37 | addCircle(circle: circle) 38 | } 39 | } 40 | 41 | private func addCircle(circle: FlutterCircle) { 42 | if circle.zIndex == nil || circle.zIndex == -1 { 43 | self.mapView.addOverlay(circle) 44 | } else { 45 | self.mapView.insertOverlay(circle, at: circle.zIndex ?? 0) 46 | } 47 | } 48 | 49 | func changeCircles(circleData data: NSArray) { 50 | let oldOverlays: [MKOverlay] = self.mapView.overlays 51 | for oldOverlay in oldOverlays { 52 | if oldOverlay is FlutterCircle { 53 | let oldFlutterCircle = oldOverlay as! FlutterCircle 54 | for _circle in data { 55 | let circleData :Dictionary = _circle as! Dictionary 56 | if oldFlutterCircle.id == (circleData["circleId"] as! String) { 57 | let newCircle = FlutterCircle.init(fromDictionaray: circleData) 58 | if oldFlutterCircle != newCircle { 59 | updateCirclesOnMap(oldCircle: oldFlutterCircle, newCircle: newCircle) 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | func removeCircles(circleIds: NSArray) { 68 | for overlay in self.mapView.overlays { 69 | if let circle = overlay as? FlutterCircle { 70 | if circleIds.contains(circle.id!) { 71 | self.mapView.removeOverlay(circle) 72 | } 73 | } 74 | } 75 | } 76 | 77 | func removeAllCircles() { 78 | for overlay in self.mapView.overlays { 79 | if let circle = overlay as? FlutterCircle { 80 | self.mapView.removeOverlay(circle) 81 | } 82 | } 83 | } 84 | 85 | private func updateCirclesOnMap(oldCircle: FlutterCircle, newCircle: FlutterCircle) { 86 | self.mapView.removeOverlay(oldCircle) 87 | addCircle(circle: newCircle) 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/circles/CircleDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleDelegate.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 01.07.21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | protocol CircleDelegate { 12 | func circleRenderer(overlay: MKOverlay) -> MKOverlayRenderer 13 | func addCircles(circleData data: NSArray) 14 | func changeCircles(circleData data: NSArray) 15 | func removeCircles(circleIds: NSArray) 16 | func removeAllCircles() 17 | } 18 | -------------------------------------------------------------------------------- /ios/Classes/Overlays/circles/FlutterCircle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlutterCircle.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 08.03.20. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | class FlutterCircle: MKCircle { 12 | var strokeColor: UIColor? 13 | var fillColor: UIColor? 14 | var isConsumingTapEvents: Bool? 15 | var strokeWidth: CGFloat? 16 | var isVisible: Bool? 17 | var id: String? 18 | var zIndex: Int? = -1 19 | var circleRadius: Double? 20 | 21 | convenience init(fromDictionaray circleData: Dictionary) { 22 | let _center = circleData["center"] as! NSArray 23 | let centerCoordinates: CLLocationCoordinate2D = CLLocationCoordinate2D.init(latitude: _center[0] as! CLLocationDegrees, longitude: _center[1] as! CLLocationDegrees) 24 | let radius = circleData["radius"] as? Double ?? 10 25 | self.init(center: centerCoordinates, radius: radius) 26 | self.circleRadius = radius 27 | self.strokeColor = JsonConversions.convertColor(data: circleData["strokeColor"] as! NSNumber) 28 | self.fillColor = JsonConversions.convertColor(data: circleData["fillColor"] as! NSNumber) 29 | self.isConsumingTapEvents = circleData["consumeTapEvents"] as? Bool 30 | self.strokeWidth = circleData["strokeWidth"] as? CGFloat 31 | self.id = circleData["circleId"] as? String 32 | self.isVisible = circleData["visible"] as? Bool 33 | self.zIndex = circleData["zIndex"] as? Int 34 | } 35 | 36 | static func == (lhs: FlutterCircle, rhs: FlutterCircle) -> Bool { 37 | return lhs.strokeColor == rhs.strokeColor && lhs.fillColor == rhs.fillColor && lhs.isConsumingTapEvents == rhs.isConsumingTapEvents && lhs.strokeWidth == rhs.strokeWidth && lhs.isVisible == rhs.isVisible && lhs.zIndex == rhs.zIndex && lhs.coordinate.latitude == rhs.coordinate.latitude && lhs.coordinate.longitude == rhs.coordinate.longitude 38 | } 39 | 40 | static func != (lhs: FlutterCircle, rhs: FlutterCircle) -> Bool { 41 | return !(lhs == rhs) 42 | } 43 | } 44 | 45 | extension FlutterCircle: FlutterOverlay { 46 | func getCAShapeLayer(snapshot: MKMapSnapshotter.Snapshot) -> CAShapeLayer { 47 | let shapeLayer = CAShapeLayer() 48 | 49 | if !(self.isVisible ?? true) { 50 | return shapeLayer 51 | } 52 | 53 | let centerPoint = snapshot.point(for: self.coordinate) 54 | 55 | let offsetPoint = snapshot.point(for: Utils.coordinateWithLAtitudeOffset(coordinate: self.coordinate, meters: radius)) 56 | 57 | let radius = centerPoint.y - offsetPoint.y 58 | 59 | let circlePath = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true) 60 | 61 | // Thus we use snapshot.point() to save the pain. 62 | shapeLayer.path = circlePath.cgPath 63 | shapeLayer.lineWidth = self.strokeWidth ?? 0 64 | shapeLayer.strokeColor = self.strokeColor?.cgColor ?? UIColor.clear.cgColor 65 | shapeLayer.fillColor = self.fillColor?.cgColor ?? UIColor.clear.cgColor 66 | 67 | return shapeLayer 68 | } 69 | } 70 | 71 | 72 | public extension MKCircle { 73 | func contains(coordinate: CLLocationCoordinate2D) -> Bool { 74 | let circleRenderer = MKCircleRenderer(circle: self) 75 | let currentMapPoint: MKMapPoint = MKMapPoint(coordinate) 76 | let circleViewPoint: CGPoint = circleRenderer.point(for: currentMapPoint) 77 | if circleRenderer.path == nil { 78 | return false 79 | } else{ 80 | return circleRenderer.path.contains(circleViewPoint) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ios/Classes/SwiftAppleMapsFlutterPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftAppleMapsFlutterPlugin: NSObject, FlutterPlugin { 5 | var factory: AppleMapViewFactory 6 | public init(with registrar: FlutterPluginRegistrar) { 7 | factory = AppleMapViewFactory(withRegistrar: registrar) 8 | registrar.register(factory, withId: "apple_maps_plugin.luisthein.de/apple_maps", gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded) 9 | } 10 | 11 | public static func register(with registrar: FlutterPluginRegistrar) { 12 | registrar.addApplicationDelegate(SwiftAppleMapsFlutterPlugin(with: registrar)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/Classes/Utils/JsonConversion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JsonConversion.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 26.11.19. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | class JsonConversions { 12 | 13 | static func convertLocation(data: Any?) -> CLLocationCoordinate2D? { 14 | if let updatedPosition = data as? Array { 15 | let lat: Double = updatedPosition[0] 16 | let lon: Double = updatedPosition[1] 17 | 18 | return CLLocationCoordinate2D(latitude: lat, longitude: lon) 19 | } 20 | return nil 21 | } 22 | 23 | static func convertColor(data: Any?) -> UIColor? { 24 | if let value = data as? CUnsignedLong { 25 | return UIColor(red: CGFloat(Float(((value & 0xFF0000) >> 16)) / 255.0), 26 | green: CGFloat(Float(((value & 0xFF00) >> 8)) / 255.0), 27 | blue: CGFloat(Float(((value & 0xFF))) / 255.0), 28 | alpha: CGFloat(Float(((value & 0xFF000000) >> 24)) / 255.0) 29 | ) 30 | } 31 | return nil 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ios/Classes/Utils/TouchHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TouchHandler.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 09.03.20. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | class TouchHandler { 12 | 13 | static func handleMapTaps(tap: UITapGestureRecognizer, overlays: [MKOverlay], channel: FlutterMethodChannel?, in view: MKMapView) { 14 | let locationInView = tap.location(in: view) 15 | let touchPt: CGPoint = tap.location(in: view) 16 | let coord: CLLocationCoordinate2D = view.convert(touchPt, toCoordinateFrom: view) 17 | var didOverlayConsumeTapEvent: Bool = false 18 | for overlay: MKOverlay in overlays { 19 | if let flutterPolyline: FlutterPolyline = overlay as? FlutterPolyline { 20 | if flutterPolyline.isConsumingTapEvents ?? false && flutterPolyline.contains(coordinate: coord, mapView: view) { 21 | channel?.invokeMethod("polyline#onTap", arguments: ["polylineId": flutterPolyline.id]) 22 | didOverlayConsumeTapEvent = true 23 | } 24 | } else if let flutterPolygon: FlutterPolygon = overlay as? FlutterPolygon { 25 | if flutterPolygon.isConsumingTapEvents ?? false && flutterPolygon.contains(coordinate: coord) { 26 | channel?.invokeMethod("polygon#onTap", arguments: ["polygonId": flutterPolygon.id]) 27 | didOverlayConsumeTapEvent = true 28 | } 29 | } else if let flutterCircle: FlutterCircle = overlay as? FlutterCircle { 30 | if flutterCircle.isConsumingTapEvents ?? false && flutterCircle.contains(coordinate: coord) { 31 | channel?.invokeMethod("circle#onTap", arguments: ["circleId": flutterCircle.id]) 32 | didOverlayConsumeTapEvent = true 33 | } 34 | } 35 | } 36 | if !didOverlayConsumeTapEvent { 37 | let locationOnMap = view.convert(locationInView, toCoordinateFrom: view) 38 | channel?.invokeMethod("map#onTap", arguments: ["position": [locationOnMap.latitude, locationOnMap.longitude]]) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ios/Classes/Utils/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Converter.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 19.02.22. 6 | // 7 | 8 | import Foundation 9 | import CoreLocation 10 | 11 | enum MapViewConstants: Double { 12 | case MERCATOR_OFFSET = 268435456.0 13 | case MERCATOR_RADIUS = 85445659.44705395 14 | } 15 | 16 | class Utils { 17 | static func longitudeToPixelSpaceX(longitude: Double) -> Double { 18 | return round(MapViewConstants.MERCATOR_OFFSET.rawValue + MapViewConstants.MERCATOR_RADIUS.rawValue * longitude * .pi / 180.0) 19 | } 20 | 21 | static func latitudeToPixelSpaceY(latitude: Double) -> Double { 22 | return round(Double(Float(MapViewConstants.MERCATOR_OFFSET.rawValue) - Float(MapViewConstants.MERCATOR_RADIUS.rawValue) * logf((1 + sinf(Float(latitude * .pi / 180.0))) / (1 - sinf(Float(latitude * .pi / 180.0)))) / Float(2.0))) 23 | } 24 | 25 | static func pixelSpaceXToLongitude(pixelX: Double) -> Double { 26 | return ((round(pixelX) - MapViewConstants.MERCATOR_OFFSET.rawValue) / MapViewConstants.MERCATOR_RADIUS.rawValue) * 180.0 / .pi 27 | } 28 | 29 | static func pixelSpaceYToLatitude(pixelY: Double) -> Double { 30 | return (.pi / 2.0 - 2.0 * atan(exp((round(pixelY) - MapViewConstants.MERCATOR_OFFSET.rawValue) / MapViewConstants.MERCATOR_RADIUS.rawValue))) * 180.0 / .pi 31 | } 32 | 33 | static func coordinateWithLAtitudeOffset(coordinate: CLLocationCoordinate2D, meters: Double) -> CLLocationCoordinate2D { 34 | 35 | // number of km per degree = ~111km (111.32 in google maps, but range varies 36 | // between 110.567km at the equator and 111.699km at the poles) 37 | // 1km in degree = 1 / 111.32km = 0.0089 38 | // 1m in degree = 0.0089 / 1000 = 0.0000089 39 | let dLat = meters * 0.0000089 40 | 41 | // OffsetPosition, decimal degrees 42 | let latO = coordinate.latitude + dLat 43 | 44 | return CLLocationCoordinate2D(latitude: latO, longitude: coordinate.longitude) 45 | } 46 | 47 | static func logC(val: Double, forBase base: Double) -> Double { 48 | return log(val)/log(base) 49 | } 50 | 51 | static func deg2rad(_ number: Double) -> Float { 52 | return Float(number * .pi / 180) 53 | } 54 | 55 | static func roundToTwoDecimalPlaces(number: Double) -> Double { 56 | let doubleStr = String(format: "%.2f", ceil(number*100)/100) 57 | return Double(doubleStr)! 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ios/Classes/models/SnapshotOptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapshotOptions.swift 3 | // apple_maps_flutter 4 | // 5 | // Created by Luis Thein on 06.07.21. 6 | // 7 | 8 | import Foundation 9 | 10 | class SnapshotOptions { 11 | let showBuildings: Bool 12 | let showPointsOfInterest: Bool 13 | let showAnnotations: Bool 14 | let showOverlays: Bool 15 | 16 | init(options: Dictionary) { 17 | self.showBuildings = options["showBuildings"] as? Bool ?? true 18 | self.showPointsOfInterest = options["showPointsOfInterest"] as? Bool ?? true 19 | self.showAnnotations = options["showAnnotations"] as? Bool ?? true 20 | self.showOverlays = options["showOverlays"] as? Bool ?? true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/apple_maps_flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'apple_maps_flutter' 6 | s.version = '0.0.1' 7 | s.summary = 'A new flutter plugin project.' 8 | s.description = <<-DESC 9 | A new flutter plugin project. 10 | DESC 11 | s.homepage = 'http://example.com' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'Your Company' => 'email@example.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | 19 | s.ios.deployment_target = '9.0' 20 | s.swift_version = '5.0' 21 | end 22 | -------------------------------------------------------------------------------- /lib/apple_maps_flutter.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | library apple_maps_flutter; 6 | 7 | import 'dart:async'; 8 | import 'dart:typed_data'; 9 | import 'dart:ui'; 10 | 11 | import 'package:flutter/foundation.dart'; 12 | import 'package:flutter/gestures.dart'; 13 | import 'package:flutter/material.dart'; 14 | import 'package:flutter/rendering.dart'; 15 | import 'package:flutter/services.dart'; 16 | 17 | part 'src/annotation.dart'; 18 | part 'src/annotation_updates.dart'; 19 | part 'src/apple_map.dart'; 20 | part 'src/bitmap.dart'; 21 | part 'src/cap.dart'; 22 | part 'src/callbacks.dart'; 23 | part 'src/camera.dart'; 24 | part 'src/circle.dart'; 25 | part 'src/circle_updates.dart'; 26 | part 'src/controller.dart'; 27 | part 'src/joint_type.dart'; 28 | part 'src/location.dart'; 29 | part 'src/pattern_item.dart'; 30 | part 'src/polyline.dart'; 31 | part 'src/polyline_updates.dart'; 32 | part 'src/polygon.dart'; 33 | part 'src/polygon_updates.dart'; 34 | part 'src/ui.dart'; 35 | part 'src/snapshot_options.dart'; 36 | -------------------------------------------------------------------------------- /lib/src/annotation_updates.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// [Annotation] update events to be applied to the [AppleMap]. 8 | /// 9 | /// Used in [AppleMapController] when the map is updated. 10 | class _AnnotationUpdates { 11 | /// Computes [_AnnotationUpdates] given previous and current [Annotation]s. 12 | _AnnotationUpdates.from(Set? previous, Set? current) { 13 | if (previous == null) { 14 | previous = Set.identity(); 15 | } 16 | 17 | if (current == null) { 18 | current = Set.identity(); 19 | } 20 | 21 | final Map previousAnnotations = 22 | _keyByAnnotationId(previous); 23 | final Map currentAnnotations = 24 | _keyByAnnotationId(current); 25 | 26 | final Set prevAnnotationIds = 27 | previousAnnotations.keys.toSet(); 28 | final Set currentAnnotationIds = 29 | currentAnnotations.keys.toSet(); 30 | 31 | Annotation idToCurrentAnnotation(AnnotationId id) { 32 | return currentAnnotations[id]!; 33 | } 34 | 35 | final Set _annotationIdsToRemove = 36 | prevAnnotationIds.difference(currentAnnotationIds); 37 | 38 | final Set _annotationsToAdd = currentAnnotationIds 39 | .difference(prevAnnotationIds) 40 | .map(idToCurrentAnnotation) 41 | .toSet(); 42 | 43 | final Set _annotationsToChange = currentAnnotationIds 44 | .intersection(prevAnnotationIds) 45 | .map(idToCurrentAnnotation) 46 | .toSet(); 47 | 48 | annotationsToAdd = _annotationsToAdd; 49 | annotationIdsToRemove = _annotationIdsToRemove; 50 | annotationsToChange = _annotationsToChange; 51 | } 52 | 53 | late Set annotationsToAdd; 54 | late Set annotationIdsToRemove; 55 | late Set annotationsToChange; 56 | 57 | Map _toMap() { 58 | final Map updateMap = {}; 59 | 60 | void addIfNonNull(String fieldName, dynamic value) { 61 | if (value != null) { 62 | updateMap[fieldName] = value; 63 | } 64 | } 65 | 66 | addIfNonNull('annotationsToAdd', _serializeAnnotationSet(annotationsToAdd)); 67 | addIfNonNull( 68 | 'annotationsToChange', _serializeAnnotationSet(annotationsToChange)); 69 | addIfNonNull( 70 | 'annotationIdsToRemove', 71 | annotationIdsToRemove 72 | .map((AnnotationId m) => m.value) 73 | .toList()); 74 | 75 | return updateMap; 76 | } 77 | 78 | @override 79 | bool operator ==(Object other) { 80 | if (identical(this, other)) return true; 81 | if (other is! _AnnotationUpdates) return false; 82 | final _AnnotationUpdates typedOther = other; 83 | return setEquals(annotationsToAdd, typedOther.annotationsToAdd) && 84 | setEquals(annotationIdsToRemove, typedOther.annotationIdsToRemove) && 85 | setEquals(annotationsToChange, typedOther.annotationsToChange); 86 | } 87 | 88 | @override 89 | int get hashCode => 90 | Object.hash(annotationsToAdd, annotationIdsToRemove, annotationsToChange); 91 | 92 | @override 93 | String toString() { 94 | return '_AnnotationUpdates{annotationsToAdd: $annotationsToAdd, ' 95 | 'annotationIdsToRemove: $annotationIdsToRemove, ' 96 | 'annotationsToChange: $annotationsToChange}'; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/bitmap.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Defines a bitmap image. For a annotation, this class can be used to set the 8 | /// image of the annotation icon. For a ground overlay, it can be used to set the 9 | /// image to place on the surface of the earth. 10 | class BitmapDescriptor { 11 | const BitmapDescriptor._(this._json); 12 | 13 | /// Convenience hue value representing red. 14 | static const double hueRed = 0.0; 15 | 16 | /// Convenience hue value representing orange. 17 | static const double hueOrange = 30.0; 18 | 19 | /// Convenience hue value representing yellow. 20 | static const double hueYellow = 60.0; 21 | 22 | /// Convenience hue value representing green. 23 | static const double hueGreen = 120.0; 24 | 25 | /// Convenience hue value representing cyan. 26 | static const double hueCyan = 180.0; 27 | 28 | /// Convenience hue value representing azure. 29 | static const double hueAzure = 210.0; 30 | 31 | /// Convenience hue value representing blue. 32 | static const double hueBlue = 240.0; 33 | 34 | /// Convenience hue value representing violet. 35 | static const double hueViolet = 270.0; 36 | 37 | /// Convenience hue value representing magenta. 38 | static const double hueMagenta = 300.0; 39 | 40 | /// Convenience hue value representing rose. 41 | static const double hueRose = 330.0; 42 | 43 | /// Creates a BitmapDescriptor that refers to the default/Pin annotation image. 44 | static const BitmapDescriptor defaultAnnotation = 45 | BitmapDescriptor._(['defaultAnnotation']); 46 | 47 | /// Creates a BitmapDescriptor that refers to the marker annotation image. 48 | static const BitmapDescriptor markerAnnotation = 49 | BitmapDescriptor._(['markerAnnotation']); 50 | 51 | /// Creates a BitmapDescriptor that refers to a colorization of the default 52 | /// marker image. For convenience, there is a predefined set of hue values. 53 | /// See e.g. [hueYellow]. 54 | static BitmapDescriptor markerAnnotationWithHue(double hue) { 55 | assert(0.0 <= hue && hue < 360.0); 56 | double iosCompatibleHue = hue / 360.0; 57 | return BitmapDescriptor._(['markerAnnotation', iosCompatibleHue]); 58 | } 59 | 60 | /// Creates a BitmapDescriptor that refers to a colorization of the default/Pin 61 | /// annotation image. For convenience, there is a predefined set of hue values. 62 | /// See e.g. [hueCyan]. 63 | static BitmapDescriptor defaultAnnotationWithHue(double hue) { 64 | assert(0.0 <= hue && hue < 360.0); 65 | double iosCompatibleHue = hue / 360.0; 66 | return BitmapDescriptor._(['defaultAnnotation', iosCompatibleHue]); 67 | } 68 | 69 | /// Creates a [BitmapDescriptor] from an asset image. 70 | /// 71 | /// Asset images in flutter are stored per: 72 | /// https://flutter.dev/docs/development/ui/assets-and-images#declaring-resolution-aware-image-assets 73 | /// This method takes into consideration various asset resolutions 74 | /// and scales the images to the right resolution depending on the dpi. 75 | static Future fromAssetImage( 76 | ImageConfiguration configuration, 77 | String assetName, { 78 | AssetBundle? bundle, 79 | String? package, 80 | bool mipmaps = true, 81 | }) async { 82 | if (!mipmaps && configuration.devicePixelRatio != null) { 83 | return BitmapDescriptor._([ 84 | 'fromAssetImage', 85 | assetName, 86 | configuration.devicePixelRatio, 87 | ]); 88 | } 89 | final AssetImage assetImage = 90 | AssetImage(assetName, package: package, bundle: bundle); 91 | final AssetBundleImageKey assetBundleImageKey = 92 | await assetImage.obtainKey(configuration); 93 | return BitmapDescriptor._([ 94 | 'fromAssetImage', 95 | assetBundleImageKey.name, 96 | assetBundleImageKey.scale, 97 | ]); 98 | } 99 | 100 | /// Creates a BitmapDescriptor using an array of bytes that must be encoded 101 | /// as PNG. 102 | static BitmapDescriptor fromBytes(Uint8List byteData) { 103 | return BitmapDescriptor._(['fromBytes', byteData]); 104 | } 105 | 106 | final dynamic _json; 107 | 108 | dynamic _toJson() => _json; 109 | } 110 | -------------------------------------------------------------------------------- /lib/src/callbacks.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Callback function taking a single argument. 8 | typedef void ArgumentCallback(T argument); 9 | 10 | /// Mutable collection of [ArgumentCallback] instances, itself an [ArgumentCallback]. 11 | /// 12 | /// Additions and removals happening during a single [call] invocation do not 13 | /// change who gets a callback until the next such invocation. 14 | /// 15 | /// Optimized for the singleton case. 16 | class ArgumentCallbacks { 17 | final List> _callbacks = >[]; 18 | 19 | /// Callback method. Invokes the corresponding method on each callback 20 | /// in this collection. 21 | /// 22 | /// The list of callbacks being invoked is computed at the start of the 23 | /// method and is unaffected by any changes subsequently made to this 24 | /// collection. 25 | void call(T argument) { 26 | final int length = _callbacks.length; 27 | if (length == 1) { 28 | _callbacks[0].call(argument); 29 | } else if (0 < length) { 30 | for (ArgumentCallback callback 31 | in List>.from(_callbacks)) { 32 | callback(argument); 33 | } 34 | } 35 | } 36 | 37 | /// Adds a callback to this collection. 38 | void add(ArgumentCallback callback) { 39 | _callbacks.add(callback); 40 | } 41 | 42 | /// Removes a callback from this collection. 43 | /// 44 | /// Does nothing, if the callback was not present. 45 | void remove(ArgumentCallback callback) { 46 | _callbacks.remove(callback); 47 | } 48 | 49 | /// Whether this collection is empty. 50 | bool get isEmpty => _callbacks.isEmpty; 51 | 52 | /// Whether this collection is non-empty. 53 | bool get isNotEmpty => _callbacks.isNotEmpty; 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/camera.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// The position of the map "camera", the view point from which the world is 8 | /// shown in the map view. Aggregates the camera's [target] geographical 9 | /// location, its [zoom] level, [pitch] angle, and [heading]. 10 | class CameraPosition { 11 | const CameraPosition({ 12 | required this.target, 13 | this.heading = 0.0, 14 | this.pitch = 0.0, 15 | this.zoom = 0, 16 | }); 17 | 18 | /// The camera's bearing in degrees, measured clockwise from north. 19 | /// 20 | /// A bearing of 0.0, the default, means the camera points north. 21 | /// A bearing of 90.0 means the camera points east. 22 | final double heading; 23 | 24 | /// The geographical location that the camera is pointing at. 25 | final LatLng target; 26 | 27 | // In degrees where 0 is looking straight down. Pitch may be clamped to an appropriate value. 28 | final double pitch; 29 | 30 | /// The zoom level of the camera. 31 | /// 32 | /// A zoom of 0.0, the default, means the screen width of the world is 256. 33 | /// Adding 1.0 to the zoom level doubles the screen width of the map. So at 34 | /// zoom level 3.0, the screen width of the world is 2³x256=2048. 35 | /// 36 | /// Larger zoom levels thus means the camera is placed closer to the surface 37 | /// of the Earth, revealing more detail in a narrower geographical region. 38 | /// 39 | /// The supported zoom level range depends on the map data and device. Values 40 | /// beyond the supported range are allowed, but on applying them to a map they 41 | /// will be silently clamped to the supported range. 42 | final double zoom; 43 | 44 | dynamic _toMap() => { 45 | 'target': target._toJson(), 46 | 'heading': heading, 47 | 'pitch': pitch, 48 | 'zoom': zoom, 49 | }; 50 | 51 | @visibleForTesting 52 | static CameraPosition? fromMap(dynamic json) { 53 | if (json == null) { 54 | return null; 55 | } 56 | return CameraPosition( 57 | heading: json['heading'], 58 | target: LatLng._fromJson(json['target'])!, 59 | pitch: json['pitch'], 60 | zoom: json['zoom'], 61 | ); 62 | } 63 | 64 | @override 65 | bool operator ==(dynamic other) { 66 | if (identical(this, other)) return true; 67 | if (runtimeType != other.runtimeType) return false; 68 | final CameraPosition typedOther = other; 69 | return heading == typedOther.heading && 70 | target == typedOther.target && 71 | pitch == typedOther.pitch && 72 | zoom == typedOther.zoom; 73 | } 74 | 75 | @override 76 | int get hashCode => Object.hash(heading, target, pitch, zoom); 77 | 78 | @override 79 | String toString() => 80 | 'CameraPosition(bearing: $heading, target: $target, tilt: $pitch, zoom: $zoom)'; 81 | } 82 | 83 | /// Defines a camera move, supporting absolute moves as well as moves relative 84 | /// the current position. 85 | class CameraUpdate { 86 | CameraUpdate._(this._json); 87 | 88 | /// Returns a camera update that moves the camera to the specified position. 89 | static CameraUpdate newCameraPosition(CameraPosition cameraPosition) { 90 | return CameraUpdate._( 91 | ['newCameraPosition', cameraPosition._toMap()], 92 | ); 93 | } 94 | 95 | /// Returns a camera update that moves the camera target to the specified 96 | /// geographical location. 97 | static CameraUpdate newLatLng(LatLng latLng) { 98 | return CameraUpdate._(['newLatLng', latLng._toJson()]); 99 | } 100 | 101 | /// Returns a camera update that moves the camera target to the specified 102 | /// geographical location and zoom level. 103 | static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { 104 | return CameraUpdate._( 105 | ['newLatLngZoom', latLng._toJson(), zoom], 106 | ); 107 | } 108 | 109 | /// Returns a camera update that transforms the camera so that the specified 110 | /// geographical bounding box is centered in the map view at the greatest 111 | /// possible zoom level. A non-zero [padding] insets the bounding box from the 112 | /// map view's edges. The camera's new tilt and bearing will both be 0.0. 113 | static CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) { 114 | return CameraUpdate._([ 115 | 'newLatLngBounds', 116 | bounds._toJson(), 117 | padding, 118 | ]); 119 | } 120 | 121 | /// Returns a camera update that modifies the camera zoom level by the 122 | /// specified amount. The optional [focus] is a screen point whose underlying 123 | /// geographical location should be invariant, if possible, by the movement. 124 | static CameraUpdate zoomBy(double amount, [Offset? focus]) { 125 | if (focus == null) { 126 | return CameraUpdate._(['zoomBy', amount]); 127 | } else { 128 | return CameraUpdate._([ 129 | 'zoomBy', 130 | amount, 131 | [focus.dx, focus.dy], 132 | ]); 133 | } 134 | } 135 | 136 | /// Returns a camera update that zooms the camera in, bringing the camera 137 | /// closer to the surface of the Earth. 138 | /// 139 | /// Equivalent to the result of calling `zoomBy(1.0)`. 140 | static CameraUpdate zoomIn() { 141 | return CameraUpdate._(['zoomIn']); 142 | } 143 | 144 | /// Returns a camera update that zooms the camera out, bringing the camera 145 | /// further away from the surface of the Earth. 146 | /// 147 | /// Equivalent to the result of calling `zoomBy(-1.0)`. 148 | static CameraUpdate zoomOut() { 149 | return CameraUpdate._(['zoomOut']); 150 | } 151 | 152 | /// Returns a camera update that sets the camera zoom level. 153 | static CameraUpdate zoomTo(double zoom) { 154 | return CameraUpdate._(['zoomTo', zoom]); 155 | } 156 | 157 | final dynamic _json; 158 | 159 | dynamic _toJson() => _json; 160 | } 161 | -------------------------------------------------------------------------------- /lib/src/cap.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Cap that can be applied at the start or end vertex of a [Polyline]. 8 | @immutable 9 | class Cap { 10 | const Cap._(this._json); 11 | 12 | /// Cap that is squared off exactly at the start or end vertex of a [Polyline] 13 | /// with solid stroke pattern, equivalent to having no additional cap beyond 14 | /// the start or end vertex. 15 | /// 16 | /// This is the default cap type at start and end vertices of Polylines with 17 | /// solid stroke pattern. 18 | static const Cap buttCap = Cap._('buttCap'); 19 | 20 | /// Cap that is a semicircle with radius equal to half the stroke width, 21 | /// centered at the start or end vertex of a [Polyline] with solid stroke 22 | /// pattern. 23 | static const Cap roundCap = Cap._('roundCap'); 24 | 25 | /// Cap that is squared off after extending half the stroke width beyond the 26 | /// start or end vertex of a [Polyline] with solid stroke pattern. 27 | static const Cap squareCap = Cap._('squareCap'); 28 | 29 | final dynamic _json; 30 | 31 | dynamic _toJson() => _json; 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/circle.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Uniquely identifies a [Circle] among [AppleMap] circles. 8 | /// 9 | /// This does not have to be globally unique, only unique among the list. 10 | @immutable 11 | class CircleId { 12 | /// Creates an immutable identifier for a [Circle]. 13 | CircleId(this.value); 14 | 15 | /// value of the [CircleId]. 16 | final String value; 17 | 18 | @override 19 | bool operator ==(Object other) { 20 | if (identical(this, other)) return true; 21 | if (other is! CircleId) return false; 22 | final CircleId typedOther = other; 23 | return value == typedOther.value; 24 | } 25 | 26 | @override 27 | int get hashCode => value.hashCode; 28 | 29 | @override 30 | String toString() { 31 | return 'CircleId{value: $value}'; 32 | } 33 | } 34 | 35 | /// Draws a circle on the map. 36 | @immutable 37 | class Circle { 38 | /// Creates an immutable representation of a [Circle] to draw on [AppleMap]. 39 | const Circle({ 40 | required this.circleId, 41 | this.consumeTapEvents = false, 42 | this.fillColor = Colors.transparent, 43 | this.center = const LatLng(0.0, 0.0), 44 | this.radius = 0, 45 | this.strokeColor = Colors.black, 46 | this.strokeWidth = 10, 47 | this.visible = true, 48 | this.zIndex, 49 | this.onTap, 50 | }); 51 | 52 | /// Uniquely identifies a [Circle]. 53 | final CircleId circleId; 54 | 55 | /// True if the [Circle] consumes tap events. 56 | /// 57 | /// If this is false, [onTap] callback will not be triggered. 58 | final bool consumeTapEvents; 59 | 60 | /// Fill color in ARGB format, the same format used by Color. The default value is transparent (0x00000000). 61 | final Color fillColor; 62 | 63 | /// Geographical location of the circle center. 64 | final LatLng center; 65 | 66 | /// Radius of the circle in meters; must be positive. The default value is 0. 67 | final double radius; 68 | 69 | /// Fill color in ARGB format, the same format used by Color. The default value is black (0xff000000). 70 | final Color strokeColor; 71 | 72 | /// The width of the circle's outline in screen points. 73 | /// 74 | /// The width is constant and independent of the camera's zoom level. 75 | /// The default value is 10. 76 | /// Setting strokeWidth to 0 results in no stroke. 77 | final int strokeWidth; 78 | 79 | /// True if the circle is visible. 80 | final bool visible; 81 | 82 | /// The z-index of the circle, used to determine relative drawing order of 83 | /// map overlays. 84 | /// 85 | /// Overlays are drawn in order of z-index, so that lower values means drawn 86 | /// earlier, and thus appearing to be closer to the surface of the Earth. 87 | final int? zIndex; 88 | 89 | /// Callbacks to receive tap events for circle placed on this map. 90 | final VoidCallback? onTap; 91 | 92 | /// Creates a new [Circle] object whose values are the same as this instance, 93 | /// unless overwritten by the specified parameters. 94 | Circle copyWith({ 95 | bool? consumeTapEventsParam, 96 | Color? fillColorParam, 97 | LatLng? centerParam, 98 | double? radiusParam, 99 | Color? strokeColorParam, 100 | int? strokeWidthParam, 101 | bool? visibleParam, 102 | int? zIndexParam, 103 | VoidCallback? onTapParam, 104 | }) { 105 | return Circle( 106 | circleId: circleId, 107 | consumeTapEvents: consumeTapEventsParam ?? consumeTapEvents, 108 | fillColor: fillColorParam ?? fillColor, 109 | center: centerParam ?? center, 110 | radius: radiusParam ?? radius, 111 | strokeColor: strokeColorParam ?? strokeColor, 112 | strokeWidth: strokeWidthParam ?? strokeWidth, 113 | visible: visibleParam ?? visible, 114 | zIndex: zIndexParam ?? zIndex, 115 | onTap: onTapParam ?? onTap, 116 | ); 117 | } 118 | 119 | /// Creates a new [Circle] object whose values are the same as this instance. 120 | Circle clone() => copyWith(); 121 | 122 | dynamic _toJson() { 123 | final Map json = {}; 124 | 125 | void addIfPresent(String fieldName, dynamic value) { 126 | if (value != null) { 127 | json[fieldName] = value; 128 | } 129 | } 130 | 131 | addIfPresent('circleId', circleId.value); 132 | addIfPresent('consumeTapEvents', consumeTapEvents); 133 | addIfPresent('fillColor', fillColor.value); 134 | addIfPresent('center', center._toJson()); 135 | addIfPresent('radius', radius); 136 | addIfPresent('strokeColor', strokeColor.value); 137 | addIfPresent('strokeWidth', strokeWidth); 138 | addIfPresent('visible', visible); 139 | addIfPresent('zIndex', zIndex); 140 | 141 | return json; 142 | } 143 | 144 | @override 145 | bool operator ==(Object other) { 146 | if (identical(this, other)) return true; 147 | if (other is! Circle) return false; 148 | final Circle typedOther = other; 149 | return circleId == typedOther.circleId && 150 | consumeTapEvents == typedOther.consumeTapEvents && 151 | fillColor == typedOther.fillColor && 152 | center == typedOther.center && 153 | radius == typedOther.radius && 154 | strokeColor == typedOther.strokeColor && 155 | strokeWidth == typedOther.strokeWidth && 156 | visible == typedOther.visible && 157 | zIndex == typedOther.zIndex && 158 | onTap == typedOther.onTap; 159 | } 160 | 161 | @override 162 | int get hashCode => circleId.hashCode; 163 | } 164 | 165 | Map _keyByCircleId(Iterable? circles) { 166 | if (circles == null) { 167 | return {}; 168 | } 169 | return Map.fromEntries(circles.map((Circle circle) => 170 | MapEntry(circle.circleId, circle.clone()))); 171 | } 172 | 173 | List>? _serializeCircleSet(Set? circles) { 174 | if (circles == null) { 175 | return null; 176 | } 177 | return circles.map>((Circle p) => p._toJson()).toList(); 178 | } 179 | -------------------------------------------------------------------------------- /lib/src/circle_updates.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// [Circle] update events to be applied to the [AppleMap]. 8 | /// 9 | /// Used in [AppleMapController] when the map is updated. 10 | class _CircleUpdates { 11 | /// Computes [_CircleUpdates] given previous and current [Circle]s. 12 | _CircleUpdates.from(Set? previous, Set? current) { 13 | if (previous == null) { 14 | previous = Set.identity(); 15 | } 16 | 17 | if (current == null) { 18 | current = Set.identity(); 19 | } 20 | 21 | final Map previousCircles = _keyByCircleId(previous); 22 | final Map currentCircles = _keyByCircleId(current); 23 | 24 | final Set prevCircleIds = previousCircles.keys.toSet(); 25 | final Set currentCircleIds = currentCircles.keys.toSet(); 26 | 27 | Circle idToCurrentCircle(CircleId id) { 28 | return currentCircles[id]!; 29 | } 30 | 31 | final Set _circleIdsToRemove = 32 | prevCircleIds.difference(currentCircleIds); 33 | 34 | final Set _circlesToAdd = currentCircleIds 35 | .difference(prevCircleIds) 36 | .map(idToCurrentCircle) 37 | .toSet(); 38 | 39 | /// Returns `true` if [current] is not equals to previous one with the 40 | /// same id. 41 | bool hasChanged(Circle current) { 42 | final Circle? previous = previousCircles[current.circleId]; 43 | return current != previous; 44 | } 45 | 46 | final Set _circlesToChange = currentCircleIds 47 | .intersection(prevCircleIds) 48 | .map(idToCurrentCircle) 49 | .where(hasChanged) 50 | .toSet(); 51 | 52 | circlesToAdd = _circlesToAdd; 53 | circleIdsToRemove = _circleIdsToRemove; 54 | circlesToChange = _circlesToChange; 55 | } 56 | 57 | late Set circlesToAdd; 58 | late Set circleIdsToRemove; 59 | late Set circlesToChange; 60 | 61 | Map _toMap() { 62 | final Map updateMap = {}; 63 | 64 | void addIfNonNull(String fieldName, dynamic value) { 65 | if (value != null) { 66 | updateMap[fieldName] = value; 67 | } 68 | } 69 | 70 | addIfNonNull('circlesToAdd', _serializeCircleSet(circlesToAdd)); 71 | addIfNonNull('circlesToChange', _serializeCircleSet(circlesToChange)); 72 | addIfNonNull('circleIdsToRemove', 73 | circleIdsToRemove.map((CircleId m) => m.value).toList()); 74 | 75 | return updateMap; 76 | } 77 | 78 | @override 79 | bool operator ==(Object other) { 80 | if (identical(this, other)) return true; 81 | if (other is! _CircleUpdates) return false; 82 | final _CircleUpdates typedOther = other; 83 | return setEquals(circlesToAdd, typedOther.circlesToAdd) && 84 | setEquals(circleIdsToRemove, typedOther.circleIdsToRemove) && 85 | setEquals(circlesToChange, typedOther.circlesToChange); 86 | } 87 | 88 | @override 89 | int get hashCode => 90 | Object.hash(circlesToAdd, circleIdsToRemove, circlesToChange); 91 | 92 | @override 93 | String toString() { 94 | return '_CircleUpdates{circlesToAdd: $circlesToAdd, ' 95 | 'circleIdsToRemove: $circleIdsToRemove, ' 96 | 'circlesToChange: $circlesToChange}'; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/joint_type.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Joint types for [Polyline]. 8 | @immutable 9 | class JointType { 10 | const JointType._(this.value); 11 | 12 | /// The value representing the [JointType] on the sdk. 13 | final int value; 14 | 15 | /// Mitered joint, with fixed pointed extrusion equal to half the stroke width on the outside of the joint. 16 | /// 17 | /// Constant Value: 0 18 | static const JointType mitered = JointType._(0); 19 | 20 | /// Flat bevel on the outside of the joint. 21 | /// 22 | /// Constant Value: 1 23 | static const JointType bevel = JointType._(1); 24 | 25 | /// Rounded on the outside of the joint by an arc of radius equal to half the stroke width, centered at the vertex. 26 | /// 27 | /// Constant Value: 2 28 | static const JointType round = JointType._(2); 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/location.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// A pair of latitude and longitude coordinates, stored as degrees. 8 | class LatLng { 9 | /// Creates a geographical location specified in degrees [latitude] and 10 | /// [longitude]. 11 | /// 12 | /// The latitude is clamped to the inclusive interval from -90.0 to +90.0. 13 | /// 14 | /// The longitude is normalized to the half-open interval from -180.0 15 | /// (inclusive) to +180.0 (exclusive) 16 | const LatLng(double latitude, double longitude) 17 | : latitude = 18 | (latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude)), 19 | longitude = (longitude + 180.0) % 360.0 - 180.0; 20 | 21 | /// The latitude in degrees between -90.0 and 90.0, both inclusive. 22 | final double latitude; 23 | 24 | /// The longitude in degrees between -180.0 (inclusive) and 180.0 (exclusive). 25 | final double longitude; 26 | 27 | dynamic _toJson() { 28 | return [latitude, longitude]; 29 | } 30 | 31 | static LatLng? _fromJson(dynamic json) { 32 | if (json == null) { 33 | return null; 34 | } 35 | return LatLng(json[0], json[1]); 36 | } 37 | 38 | @override 39 | String toString() => '$runtimeType($latitude, $longitude)'; 40 | 41 | @override 42 | bool operator ==(Object o) { 43 | return o is LatLng && o.latitude == latitude && o.longitude == longitude; 44 | } 45 | 46 | @override 47 | int get hashCode => Object.hash(latitude, longitude); 48 | } 49 | 50 | /// A latitude/longitude aligned rectangle. 51 | /// 52 | /// The rectangle conceptually includes all points (lat, lng) where 53 | /// * lat ∈ [`southwest.latitude`, `northeast.latitude`] 54 | /// * lng ∈ [`southwest.longitude`, `northeast.longitude`], 55 | /// if `southwest.longitude` ≤ `northeast.longitude`, 56 | /// * lng ∈ [-180, `northeast.longitude`] ∪ [`southwest.longitude`, 180[, 57 | /// if `northeast.longitude` < `southwest.longitude` 58 | class LatLngBounds { 59 | /// Creates geographical bounding box with the specified corners. 60 | /// 61 | /// The latitude of the southwest corner cannot be larger than the 62 | /// latitude of the northeast corner. 63 | LatLngBounds({required this.southwest, required this.northeast}) 64 | : assert(southwest.latitude <= northeast.latitude); 65 | 66 | /// The southwest corner of the rectangle. 67 | final LatLng southwest; 68 | 69 | /// The northeast corner of the rectangle. 70 | final LatLng northeast; 71 | 72 | /// Returns whether this rectangle contains the given [LatLng]. 73 | bool contains(LatLng point) { 74 | return _containsLatitude(point.latitude) && 75 | _containsLongitude(point.longitude); 76 | } 77 | 78 | bool _containsLatitude(double lat) { 79 | return (southwest.latitude <= lat) && (lat <= northeast.latitude); 80 | } 81 | 82 | bool _containsLongitude(double lng) { 83 | if (southwest.longitude <= northeast.longitude) { 84 | return southwest.longitude <= lng && lng <= northeast.longitude; 85 | } else { 86 | return southwest.longitude <= lng || lng <= northeast.longitude; 87 | } 88 | } 89 | 90 | @visibleForTesting 91 | static LatLngBounds? fromList(dynamic json) { 92 | if (json == null) { 93 | return null; 94 | } 95 | return LatLngBounds( 96 | southwest: LatLng._fromJson(json[0])!, 97 | northeast: LatLng._fromJson(json[1])!, 98 | ); 99 | } 100 | 101 | @override 102 | String toString() { 103 | return '$runtimeType($southwest, $northeast)'; 104 | } 105 | 106 | @override 107 | bool operator ==(Object o) { 108 | return o is LatLngBounds && 109 | o.southwest == southwest && 110 | o.northeast == northeast; 111 | } 112 | 113 | /// Converts this object to something serializable in JSON. 114 | dynamic _toJson() { 115 | return [southwest._toJson(), northeast._toJson()]; 116 | } 117 | 118 | @override 119 | int get hashCode => Object.hash(southwest, northeast); 120 | } 121 | -------------------------------------------------------------------------------- /lib/src/pattern_item.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Item used in the stroke pattern for a Polyline. 8 | @immutable 9 | class PatternItem { 10 | const PatternItem._(this._json); 11 | 12 | static const PatternItem dot = PatternItem._(['dot']); 13 | 14 | /// A dash used in the stroke pattern for a [Polyline]. 15 | /// 16 | /// [length] has to be non-negative. 17 | static PatternItem dash(double length) { 18 | assert(length >= 0.0); 19 | return PatternItem._(['dash', length]); 20 | } 21 | 22 | /// A gap used in the stroke pattern for a [Polyline]. 23 | /// 24 | /// [length] has to be non-negative. 25 | static PatternItem gap(double length) { 26 | assert(length >= 0.0); 27 | return PatternItem._(['gap', length]); 28 | } 29 | 30 | final dynamic _json; 31 | 32 | dynamic _toJson() => _json; 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/polygon.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Uniquely identifies a [Polygon] among [AppleMap] polygons. 8 | /// 9 | /// This does not have to be globally unique, only unique among the list. 10 | @immutable 11 | class PolygonId { 12 | /// Creates an immutable identifier for a [Polygon]. 13 | PolygonId(this.value); 14 | 15 | /// value of the [PolygonId]. 16 | final String value; 17 | 18 | @override 19 | bool operator ==(Object other) { 20 | if (identical(this, other)) return true; 21 | if (other is! PolygonId) return false; 22 | final PolygonId typedOther = other; 23 | return value == typedOther.value; 24 | } 25 | 26 | @override 27 | int get hashCode => value.hashCode; 28 | 29 | @override 30 | String toString() { 31 | return 'PolygonId{value: $value}'; 32 | } 33 | } 34 | 35 | /// Draws a polygon through geographical locations on the map. 36 | @immutable 37 | class Polygon { 38 | /// Creates an immutable representation of a polygon through geographical locations on the map. 39 | const Polygon({ 40 | required this.polygonId, 41 | this.consumeTapEvents = false, 42 | this.fillColor = Colors.black, 43 | this.points = const [], 44 | this.strokeColor = Colors.black, 45 | this.strokeWidth = 10, 46 | this.visible = true, 47 | this.zIndex, 48 | this.onTap, 49 | }); 50 | 51 | /// Uniquely identifies a [Polygon]. 52 | final PolygonId polygonId; 53 | 54 | /// True if the [Polygon] consumes tap events. 55 | /// 56 | /// If this is false, [onTap] callback will not be triggered. 57 | final bool consumeTapEvents; 58 | 59 | /// Fill color in ARGB format, the same format used by Color. The default value is black (0xff000000). 60 | final Color fillColor; 61 | 62 | /// The vertices of the polygon to be drawn. 63 | /// 64 | /// Line segments are drawn between consecutive points. A polygon is not closed by 65 | /// default; to form a closed polygon, the start and end points must be the same. 66 | final List points; 67 | 68 | /// True if the marker is visible. 69 | final bool visible; 70 | 71 | /// The z-index of the polygon, used to determine relative drawing order of 72 | /// map overlays. 73 | /// 74 | /// Overlays are drawn in order of z-index, so that lower values means drawn 75 | /// earlier, and thus appearing to be closer to the surface of the Earth. 76 | final int? zIndex; 77 | 78 | /// Line color in ARGB format, the same format used by Color. The default value is black (0xff000000). 79 | final Color strokeColor; 80 | 81 | /// Width of the polygon, used to define the width of the line to be drawn. 82 | /// 83 | /// The width is constant and independent of the camera's zoom level. 84 | /// The default value is 10. 85 | final int strokeWidth; 86 | 87 | /// Callbacks to receive tap events for polygon placed on this map. 88 | final VoidCallback? onTap; 89 | 90 | /// Creates a new [Polygon] object whose values are the same as this instance, 91 | /// unless overwritten by the specified parameters. 92 | Polygon copyWith({ 93 | bool? consumeTapEventsParam, 94 | List? pointsParam, 95 | Color? strokeColorParam, 96 | Color? fillColorParam, 97 | int? strokeWidthParam, 98 | bool? visibleParam, 99 | int? zIndexParam, 100 | VoidCallback? onTapParam, 101 | }) { 102 | return Polygon( 103 | polygonId: polygonId, 104 | consumeTapEvents: consumeTapEventsParam ?? consumeTapEvents, 105 | points: pointsParam ?? points, 106 | strokeColor: strokeColorParam ?? strokeColor, 107 | fillColor: fillColorParam ?? fillColor, 108 | strokeWidth: strokeWidthParam ?? strokeWidth, 109 | visible: visibleParam ?? visible, 110 | zIndex: zIndexParam ?? zIndex, 111 | onTap: onTapParam ?? onTap, 112 | ); 113 | } 114 | 115 | /// Creates a new [Polygon] object whose values are the same as this instance. 116 | Polygon clone() { 117 | return copyWith(pointsParam: List.of(points)); 118 | } 119 | 120 | dynamic _toJson() { 121 | final Map json = {}; 122 | 123 | void addIfPresent(String fieldName, dynamic value) { 124 | if (value != null) { 125 | json[fieldName] = value; 126 | } 127 | } 128 | 129 | addIfPresent('polygonId', polygonId.value); 130 | addIfPresent('consumeTapEvents', consumeTapEvents); 131 | addIfPresent('fillColor', fillColor.value); 132 | addIfPresent('strokeColor', strokeColor.value); 133 | addIfPresent('strokeWidth', strokeWidth); 134 | addIfPresent('visible', visible); 135 | addIfPresent('zIndex', zIndex); 136 | 137 | json['points'] = _pointsToJson(); 138 | 139 | return json; 140 | } 141 | 142 | @override 143 | bool operator ==(Object other) { 144 | if (identical(this, other)) return true; 145 | if (other is! Polygon) return false; 146 | final Polygon typedOther = other; 147 | return polygonId == typedOther.polygonId && 148 | consumeTapEvents == typedOther.consumeTapEvents && 149 | fillColor == typedOther.fillColor && 150 | listEquals(points, typedOther.points) && 151 | visible == typedOther.visible && 152 | strokeColor == typedOther.strokeColor && 153 | strokeWidth == typedOther.strokeWidth && 154 | zIndex == typedOther.zIndex && 155 | onTap == typedOther.onTap; 156 | } 157 | 158 | @override 159 | int get hashCode => polygonId.hashCode; 160 | 161 | dynamic _pointsToJson() { 162 | final List result = []; 163 | for (final LatLng point in points) { 164 | result.add(point._toJson()); 165 | } 166 | return result; 167 | } 168 | } 169 | 170 | Map _keyByPolygonId(Iterable? polygons) { 171 | if (polygons == null) { 172 | return {}; 173 | } 174 | return Map.fromEntries(polygons.map((Polygon polygon) => 175 | MapEntry(polygon.polygonId, polygon.clone()))); 176 | } 177 | 178 | List>? _serializePolygonSet(Set? polygons) { 179 | if (polygons == null) { 180 | return null; 181 | } 182 | return polygons 183 | .map>((Polygon p) => p._toJson()) 184 | .toList(); 185 | } 186 | -------------------------------------------------------------------------------- /lib/src/polygon_updates.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// [Polygon] update events to be applied to the [AppleMap]. 8 | /// 9 | /// Used in [AppleMapController] when the map is updated. 10 | class _PolygonUpdates { 11 | /// Computes [_PolygonUpdates] given previous and current [Polygon]s. 12 | _PolygonUpdates.from(Set? previous, Set? current) { 13 | if (previous == null) { 14 | previous = Set.identity(); 15 | } 16 | 17 | if (current == null) { 18 | current = Set.identity(); 19 | } 20 | 21 | final Map previousPolygons = _keyByPolygonId(previous); 22 | final Map currentPolygons = _keyByPolygonId(current); 23 | 24 | final Set prevPolygonIds = previousPolygons.keys.toSet(); 25 | final Set currentPolygonIds = currentPolygons.keys.toSet(); 26 | 27 | Polygon idToCurrentPolygon(PolygonId id) { 28 | return currentPolygons[id]!; 29 | } 30 | 31 | final Set _polygonIdsToRemove = 32 | prevPolygonIds.difference(currentPolygonIds); 33 | 34 | final Set _polygonsToAdd = currentPolygonIds 35 | .difference(prevPolygonIds) 36 | .map(idToCurrentPolygon) 37 | .toSet(); 38 | 39 | /// Returns `true` if [current] is not equals to previous one with the 40 | /// same id. 41 | bool hasChanged(Polygon current) { 42 | final Polygon? previous = previousPolygons[current.polygonId]; 43 | return current != previous; 44 | } 45 | 46 | final Set _polygonsToChange = currentPolygonIds 47 | .intersection(prevPolygonIds) 48 | .map(idToCurrentPolygon) 49 | .where(hasChanged) 50 | .toSet(); 51 | 52 | polygonsToAdd = _polygonsToAdd; 53 | polygonIdsToRemove = _polygonIdsToRemove; 54 | polygonsToChange = _polygonsToChange; 55 | } 56 | 57 | late Set polygonsToAdd; 58 | late Set polygonIdsToRemove; 59 | late Set polygonsToChange; 60 | 61 | Map _toMap() { 62 | final Map updateMap = {}; 63 | 64 | void addIfNonNull(String fieldName, dynamic value) { 65 | if (value != null) { 66 | updateMap[fieldName] = value; 67 | } 68 | } 69 | 70 | addIfNonNull('polygonsToAdd', _serializePolygonSet(polygonsToAdd)); 71 | addIfNonNull('polygonsToChange', _serializePolygonSet(polygonsToChange)); 72 | addIfNonNull('polygonIdsToRemove', 73 | polygonIdsToRemove.map((PolygonId m) => m.value).toList()); 74 | 75 | return updateMap; 76 | } 77 | 78 | @override 79 | bool operator ==(Object other) { 80 | if (identical(this, other)) return true; 81 | if (other is! _PolygonUpdates) return false; 82 | final _PolygonUpdates typedOther = other; 83 | return setEquals(polygonsToAdd, typedOther.polygonsToAdd) && 84 | setEquals(polygonIdsToRemove, typedOther.polygonIdsToRemove) && 85 | setEquals(polygonsToChange, typedOther.polygonsToChange); 86 | } 87 | 88 | @override 89 | int get hashCode => 90 | Object.hash(polygonsToAdd, polygonIdsToRemove, polygonsToChange); 91 | 92 | @override 93 | String toString() { 94 | return '_PolygonUpdates{polygonsToAdd: $polygonsToAdd, ' 95 | 'polygonIdsToRemove: $polygonIdsToRemove, ' 96 | 'polygonsToChange: $polygonsToChange}'; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/polyline_updates.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// [Polyline] update events to be applied to the [AppleMap]. 8 | /// 9 | /// Used in [AppleMapController] when the map is updated. 10 | class _PolylineUpdates { 11 | /// Computes [_PolylineUpdates] given previous and current [Polyline]s. 12 | _PolylineUpdates.from(Set? previous, Set? current) { 13 | if (previous == null) { 14 | previous = Set.identity(); 15 | } 16 | 17 | if (current == null) { 18 | current = Set.identity(); 19 | } 20 | 21 | final Map previousPolylines = 22 | _keyByPolylineId(previous); 23 | final Map currentPolylines = 24 | _keyByPolylineId(current); 25 | 26 | final Set prevPolylineIds = previousPolylines.keys.toSet(); 27 | final Set currentPolylineIds = currentPolylines.keys.toSet(); 28 | 29 | Polyline idToCurrentPolyline(PolylineId id) { 30 | return currentPolylines[id]!; 31 | } 32 | 33 | final Set _polylineIdsToRemove = 34 | prevPolylineIds.difference(currentPolylineIds); 35 | 36 | final Set _polylinesToAdd = currentPolylineIds 37 | .difference(prevPolylineIds) 38 | .map(idToCurrentPolyline) 39 | .toSet(); 40 | 41 | final Set _polylinesToChange = currentPolylineIds 42 | .intersection(prevPolylineIds) 43 | .map(idToCurrentPolyline) 44 | .toSet(); 45 | 46 | polylinesToAdd = _polylinesToAdd; 47 | polylineIdsToRemove = _polylineIdsToRemove; 48 | polylinesToChange = _polylinesToChange; 49 | } 50 | 51 | late Set polylinesToAdd; 52 | late Set polylineIdsToRemove; 53 | late Set polylinesToChange; 54 | 55 | Map _toMap() { 56 | final Map updateMap = {}; 57 | 58 | void addIfNonNull(String fieldName, dynamic value) { 59 | if (value != null) { 60 | updateMap[fieldName] = value; 61 | } 62 | } 63 | 64 | addIfNonNull('polylinesToAdd', _serializePolylineSet(polylinesToAdd)); 65 | addIfNonNull('polylinesToChange', _serializePolylineSet(polylinesToChange)); 66 | addIfNonNull('polylineIdsToRemove', 67 | polylineIdsToRemove.map((PolylineId m) => m.value).toList()); 68 | 69 | return updateMap; 70 | } 71 | 72 | @override 73 | bool operator ==(Object other) { 74 | if (identical(this, other)) return true; 75 | if (other is! _PolylineUpdates) return false; 76 | final _PolylineUpdates typedOther = other; 77 | return setEquals(polylinesToAdd, typedOther.polylinesToAdd) && 78 | setEquals(polylineIdsToRemove, typedOther.polylineIdsToRemove) && 79 | setEquals(polylinesToChange, typedOther.polylinesToChange); 80 | } 81 | 82 | @override 83 | int get hashCode => 84 | Object.hash(polylinesToAdd, polylineIdsToRemove, polylinesToChange); 85 | 86 | @override 87 | String toString() { 88 | return '_PolylineUpdates{polylinesToAdd: $polylinesToAdd, ' 89 | 'polylineIdsToRemove: $polylineIdsToRemove, ' 90 | 'polylinesToChange: $polylinesToChange}'; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/src/snapshot_options.dart: -------------------------------------------------------------------------------- 1 | part of apple_maps_flutter; 2 | 3 | class SnapshotOptions { 4 | const SnapshotOptions({ 5 | this.showBuildings = true, 6 | this.showPointsOfInterest = true, 7 | this.showAnnotations = true, 8 | this.showOverlays = true, 9 | }); 10 | 11 | final bool showBuildings; 12 | final bool showPointsOfInterest; 13 | final bool showAnnotations; 14 | final bool showOverlays; 15 | 16 | dynamic _toMap() => { 17 | 'showBuildings': showBuildings, 18 | 'showPointsOfInterest': showPointsOfInterest, 19 | 'showAnnotations': showAnnotations, 20 | 'showOverlays': showOverlays, 21 | }; 22 | 23 | @visibleForTesting 24 | static SnapshotOptions? fromMap(dynamic json) { 25 | if (json == null) { 26 | return null; 27 | } 28 | return SnapshotOptions( 29 | showBuildings: json['showBuildings'], 30 | showPointsOfInterest: json['showPointsOfInterest'], 31 | showAnnotations: json['showAnnotations'], 32 | showOverlays: json['showOverlays'], 33 | ); 34 | } 35 | 36 | @override 37 | bool operator ==(dynamic other) { 38 | if (identical(this, other)) return true; 39 | if (runtimeType != other.runtimeType) return false; 40 | final SnapshotOptions typedOther = other; 41 | return showBuildings == typedOther.showBuildings && 42 | showPointsOfInterest == typedOther.showPointsOfInterest && 43 | showAnnotations == typedOther.showAnnotations && 44 | showOverlays == typedOther.showOverlays; 45 | } 46 | 47 | @override 48 | int get hashCode => Object.hash(showBuildings, showPointsOfInterest); 49 | 50 | @override 51 | String toString() => 52 | 'SnapshotOptions(showBuildings: $showBuildings, showPointsOfInterest: $showPointsOfInterest, showAnnotations: $showAnnotations, showOverlays: $showOverlays)'; 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/ui.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | part of apple_maps_flutter; 6 | 7 | /// Type of map tiles to display. 8 | enum MapType { 9 | /// Normal tiles (traffic and labels, subtle terrain information). 10 | standard, 11 | 12 | /// Satellite imaging tiles (aerial photos) 13 | satellite, 14 | 15 | /// Hybrid tiles (satellite images with some labels/overlays) 16 | hybrid, 17 | } 18 | 19 | enum TrackingMode { 20 | // the user's location is not followed 21 | none, 22 | 23 | // the map follows the user's location 24 | follow, 25 | 26 | // the map follows the user's location and heading 27 | followWithHeading, 28 | } 29 | 30 | /// Bounds for the map camera target. 31 | // Used with [AppleMapOptions] to wrap a [LatLngBounds] value. This allows 32 | // distinguishing between specifying an unbounded target (null `LatLngBounds`) 33 | // from not specifying anything (null `CameraTargetBounds`). 34 | class CameraTargetBounds { 35 | /// Creates a camera target bounds with the specified bounding box, or null 36 | /// to indicate that the camera target is not bounded. 37 | const CameraTargetBounds(this.bounds); 38 | 39 | /// The geographical bounding box for the map camera target. 40 | /// 41 | /// A null value means the camera target is unbounded. 42 | final LatLngBounds? bounds; 43 | 44 | /// Unbounded camera target. 45 | static const CameraTargetBounds unbounded = CameraTargetBounds(null); 46 | 47 | @override 48 | bool operator ==(dynamic other) { 49 | if (identical(this, other)) return true; 50 | if (runtimeType != other.runtimeType) return false; 51 | final CameraTargetBounds typedOther = other; 52 | return bounds == typedOther.bounds; 53 | } 54 | 55 | @override 56 | int get hashCode => bounds.hashCode; 57 | 58 | @override 59 | String toString() { 60 | return 'CameraTargetBounds(bounds: $bounds)'; 61 | } 62 | } 63 | 64 | class MinMaxZoomPreference { 65 | const MinMaxZoomPreference(this.minZoom, this.maxZoom) 66 | : assert(minZoom == null || maxZoom == null || minZoom <= maxZoom); 67 | 68 | /// The preferred minimum zoom level or null, if unbounded from below. 69 | final double? minZoom; 70 | 71 | /// The preferred maximum zoom level or null, if unbounded from above. 72 | final double? maxZoom; 73 | 74 | /// Unbounded zooming. 75 | static const MinMaxZoomPreference unbounded = 76 | MinMaxZoomPreference(null, null); 77 | 78 | dynamic _toJson() => [minZoom, maxZoom]; 79 | 80 | @override 81 | bool operator ==(dynamic other) { 82 | if (identical(this, other)) return true; 83 | if (runtimeType != other.runtimeType) return false; 84 | final MinMaxZoomPreference typedOther = other; 85 | return minZoom == typedOther.minZoom && maxZoom == typedOther.maxZoom; 86 | } 87 | 88 | @override 89 | int get hashCode => Object.hash(minZoom, maxZoom); 90 | 91 | @override 92 | String toString() { 93 | return 'MinMaxZoomPreference(minZoom: $minZoom, maxZoom: $maxZoom)'; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: apple_maps_flutter 2 | description: This plugin uses the Flutter platform view to display an Apple Maps widget. 3 | version: 1.4.0 4 | homepage: https://luisthein.de 5 | repository: https://github.com/LuisThein/apple_maps_flutter 6 | issue_tracker: https://github.com/LuisThein/apple_maps_flutter/issues 7 | 8 | environment: 9 | sdk: ">=2.14.0 <3.0.0" 10 | flutter: ">=1.17.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | # For information on the generic Dart part of this file, see the 21 | # following page: https://dart.dev/tools/pub/pubspec 22 | 23 | # The following section is specific to Flutter. 24 | flutter: 25 | # This section identifies this Flutter project as a plugin project. 26 | # The androidPackage and pluginClass identifiers should not ordinarily 27 | # be modified. They are used by the tooling to maintain consistency when 28 | # adding or updating assets for this project. 29 | plugin: 30 | platforms: 31 | ios: 32 | pluginClass: AppleMapsFlutterPlugin 33 | # To add assets to your plugin package, add an assets section, like this: 34 | # assets: 35 | # - images/a_dot_burr.jpeg 36 | # - images/a_dot_ham.jpeg 37 | # 38 | # For details regarding assets in packages, see 39 | # https://flutter.dev/assets-and-images/#from-packages 40 | # 41 | # An image asset can refer to one or more resolution-specific "variants", see 42 | # https://flutter.dev/assets-and-images/#resolution-aware. 43 | # To add custom fonts to your plugin package, add a fonts section here, 44 | # in this "flutter" section. Each entry in this list should have a 45 | # "family" key with the font family name, and a "fonts" key with a 46 | # list giving the asset and other descriptors for the font. For 47 | # example: 48 | # fonts: 49 | # - family: Schyler 50 | # fonts: 51 | # - asset: fonts/Schyler-Regular.ttf 52 | # - asset: fonts/Schyler-Italic.ttf 53 | # style: italic 54 | # - family: Trajan Pro 55 | # fonts: 56 | # - asset: fonts/TrajanPro.ttf 57 | # - asset: fonts/TrajanPro_Bold.ttf 58 | # weight: 700 59 | # 60 | # For details regarding fonts in packages, see 61 | # https://flutter.dev/custom-fonts/#from-packages 62 | --------------------------------------------------------------------------------