├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── flutter_build.yml
├── .gitignore
├── .metadata
├── .vscode
└── launch.json
├── AUTHORS
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── assets
└── images
│ ├── marker_a.png
│ └── marker_b.png
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── 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
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── assets
│ └── images
│ │ └── map-marker-warehouse.png
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── 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
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ └── main.dart
├── pubspec.yaml
└── web
│ └── index.html
├── lib
├── flutter_google_maps.dart
└── src
│ ├── core
│ ├── google_map.dart
│ ├── google_map.state.dart
│ ├── map_items.dart
│ ├── map_operations.dart
│ ├── map_preferences.dart
│ └── utils.dart
│ ├── mobile
│ ├── google_map.state.dart
│ └── utils.dart
│ └── web
│ ├── google_map.state.dart
│ └── utils.dart
├── publish_commands.txt
├── pubspec.yaml
└── test
└── flutter_google_maps_test.dart
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: I have a problem with my Flutter application.
3 | about: You are writing an application with Flutter but the application is crashing
4 | or throws an exception, a widget is buggy, or something looks wrong.
5 | title: ''
6 | labels: ''
7 | assignees: ''
8 |
9 | ---
10 |
11 |
23 |
24 | ## Steps to Reproduce
25 |
26 |
27 |
28 | 1. Run `flutter create bug`.
29 | 2. Update the files as follows: ...
30 | 3. ...
31 |
32 | **Expected results:**
33 |
34 | **Actual results:**
35 |
36 |
37 | Logs
38 |
39 |
45 |
46 | ```
47 | ```
48 |
49 |
53 |
54 | ```
55 | ```
56 |
57 |
58 |
59 | ```
60 | ```
61 |
62 |
63 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest a new idea for Flutter.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
22 |
23 | ## Use case
24 |
25 |
35 |
36 | ## Proposal
37 |
38 |
47 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | *Replace this paragraph with a description of what this PR is doing. If you're modifying existing behavior, describe the existing behavior, how this PR is changing it, and what motivated the change. If you're changing visual properties, consider including before/after screenshots (and runnable code snippets to reproduce them).*
4 |
5 | ## Related Issues
6 |
7 | *Replace this paragraph with a list of issues related to this PR from our issue database. Indicate, which of these issues are resolved or fixed by this PR. There should be at least one issue listed here.*
8 |
9 | ## Tests
10 |
11 | I added the following tests:
12 |
13 | *Replace this with a list of the tests that you added as part of this PR. A change in behaviour with no test covering it
14 | will likely get reverted accidentally sooner or later. PRs must include tests for all changed/updated/fixed behaviors. See [Test Coverage].*
15 |
16 | ## Checklist
17 |
18 | Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (`[x]`). This will ensure a smooth and quick review process.
19 |
20 | - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
21 | - [ ] I updated/added relevant documentation (doc comments with `///`).
22 | - [ ] All existing and new tests are passing.
23 | - [ ] The analyzer (`flutter analyze --flutter-repo`) does not report any problems on my PR.
24 | - [ ] I am willing to follow-up on review comments in a timely manner.
25 |
26 | ## Breaking Change
27 |
28 | Did any tests fail when you ran them?
29 |
30 | - [ ] No, no existing tests failed, so this is *not* a breaking change.
31 | - [ ] Yes, this is a breaking change. *If not, delete the remainder of this section.*
32 | - [ ] I wrote a design doc: https://flutter.dev/go/template *Replace this with a link to your design doc's short link*
33 | - [ ] I got input from the developer relations team, specifically from: *Replace with the names of who gave advice*
34 | - [ ] I wrote a migration guide: *Replace with a link to your migration guide*
35 |
36 |
37 | [Test Coverage]: https://github.com/flutter/flutter/wiki/Test-coverage-for-package%3Aflutter
38 | [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
39 | [Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
40 |
--------------------------------------------------------------------------------
/.github/workflows/flutter_build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - develop
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v1
16 | - uses: subosito/flutter-action@v1
17 | with:
18 | channel: 'beta'
19 | - run: flutter pub get
20 |
--------------------------------------------------------------------------------
/.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 | *.lock
33 |
34 | # Android related
35 | **/android/**/gradle-wrapper.jar
36 | **/android/.gradle
37 | **/android/captures/
38 | **/android/gradlew
39 | **/android/gradlew.bat
40 | **/android/local.properties
41 | **/android/**/GeneratedPluginRegistrant.java
42 |
43 | # iOS/XCode related
44 | **/ios/**/*.mode1v3
45 | **/ios/**/*.mode2v3
46 | **/ios/**/*.moved-aside
47 | **/ios/**/*.pbxuser
48 | **/ios/**/*.perspectivev3
49 | **/ios/**/*sync/
50 | **/ios/**/.sconsign.dblite
51 | **/ios/**/.tags*
52 | **/ios/**/.vagrant/
53 | **/ios/**/DerivedData/
54 | **/ios/**/Icon?
55 | **/ios/**/Pods/
56 | **/ios/**/.symlinks/
57 | **/ios/**/profile
58 | **/ios/**/xcuserdata
59 | **/ios/.generated/
60 | **/ios/Flutter/App.framework
61 | **/ios/Flutter/Flutter.framework
62 | **/ios/Flutter/Flutter.podspec
63 | **/ios/Flutter/Generated.xcconfig
64 | **/ios/Flutter/app.flx
65 | **/ios/Flutter/app.zip
66 | **/ios/Flutter/flutter_assets/
67 | **/ios/Flutter/flutter_export_environment.sh
68 | **/ios/ServiceDefinitions.json
69 | **/ios/Runner/GeneratedPluginRegistrant.*
70 |
71 | # Exceptions to above rules.
72 | !**/ios/**/default.mode1v3
73 | !**/ios/**/default.mode2v3
74 | !**/ios/**/default.pbxuser
75 | !**/ios/**/default.perspectivev3
76 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
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: 2b4b6eef5d361b4114ee4198aa15075890717440
8 | channel: unknown
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Flutter",
9 | "request": "launch",
10 | "type": "dart"
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # Below is a list of people and organizations that have contributed
2 | # to the MarchDev Toolkit projects. Names should be added to the list like so:
3 | #
4 | # Name/Organization
5 |
6 | MarchDev Toolkit
7 | - Oleh Marchenko
8 | - Elena Marchenko
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 4.0.1
4 |
5 | * TypeError: this.widget.markers is not iterable (thanks to [slovnicki](https://github.com/slovnicki))
6 |
7 | ## 4.0.0
8 |
9 | * Added Circles support (thanks to [jan-pavlovsky](https://github.com/jan-pavlovsky))
10 |
11 | ## 3.8.0
12 |
13 | * Added moveCameraBounds (old moveCamera), moveCamera (with zooming ability) and zoomCamera (thanks to [travisjayday](https://github.com/travisjayday) for zoomControlsEnabled on mobile)
14 | * Added center getter for center coordinates of the map
15 | * Fixed issue when onTap/onLongPress was not specified
16 |
17 | ## 3.7.1
18 |
19 | * Fixed bug when app crash, when there's no any marker on mobile map (thanks to [Chojecki](https://github.com/Chojecki))
20 |
21 | ## 3.7.0
22 |
23 | * Changed onTap VoidCallback to onTap ValueChanged with marker ID as an argument
24 | * Added onTap for polygon with polygon ID as an argument
25 | * Added option for marker icon to be a path to an asset as well as to be a ByteString
26 |
27 | ### BREAKING CHANGE
28 | * addMarker now is addMarkerRaw
29 | * addMarker now accepts instance of a Marker object
30 |
31 | ## 3.6.0
32 |
33 | * Added infoSnippet as an argument to addMarker method
34 |
35 | ## 3.5.0
36 |
37 | * Fixed animateCamera issue and possible changeMapStyle issue
38 |
39 | ## 3.4.0
40 |
41 | * Added rotate/scroll/tilt/zoom gestures to mobile map preferences
42 | * Added map callbacks onTap, onLongPress
43 | * Added onInfoWindowTap for addMarker method on web
44 |
45 | ## 3.3.0
46 |
47 | * Added onInfoWindowTap for addMarker method (works only on Android/iOS)
48 | * Added map iteractivity setup
49 |
50 | ## 3.2.0
51 |
52 | * Added onTap VoidCallback for addMarker method
53 |
54 | ## 3.1.0
55 |
56 | * Added gesture boolean to WebMapPreferences, that indicates whether to enable gestures or not
57 |
58 | ## 3.0.0
59 |
60 | * Implemented setting up Map Style
61 |
62 | ## 2.0.0
63 |
64 | * Fixed roadmap/normal Map Type
65 |
66 | ## 1.1.0
67 |
68 | * Update docs
69 |
70 | ## 1.0.0
71 |
72 | * Created wrapper for Mobile map and Web map
73 | * Created common interface
74 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at eo.march.dev+support@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 | build.
12 | 2. Update the README.md and CHANGELOG.md with details of changes to the interface, this includes new environment
13 | variables, exposed ports, useful file locations and container parameters.
14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this
15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16 | 4. You may merge the Pull Request in once you have the sign-off of one other developer, or if you
17 | do not have permission to do that, you may request the second reviewer to merge it for you.
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, march.dev
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 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_google_maps
2 |
3 | 
4 | [](https://pub.dartlang.org/packages/flutter_google_maps)
5 | 
6 | 
7 |
8 | A Flutter plugin for integrating Google Maps in iOS, Android and Web applications. It is a wrapper of google_maps_flutter for Mobile and google_maps for Web.
9 |
10 | ## Getting Started
11 |
12 | * Get an API key at .
13 |
14 | * Enable Google Map SDK for each platform.
15 | * Go to [Google Developers Console](https://console.cloud.google.com/).
16 | * Choose the project that you want to enable Google Maps on.
17 | * Select the navigation menu and then select "Google Maps".
18 | * Select "APIs" under the Google Maps menu.
19 | * To enable Google Maps for Android, select "Maps SDK for Android" in the "Additional APIs" section, then select "ENABLE".
20 | * To enable Google Maps for iOS, select "Maps SDK for iOS" in the "Additional APIs" section, then select "ENABLE".
21 | * Make sure the APIs you enabled are under the "Enabled APIs" section.
22 |
23 | * You can also find detailed steps to get start with Google Maps Platform [here](https://developers.google.com/maps/gmp-get-started).
24 |
25 | ### Web
26 |
27 | ```html
28 |
29 |
30 |
31 |
32 | ```
33 |
34 | ### Android
35 |
36 | Specify your API key in the application manifest `android/app/src/main/AndroidManifest.xml`:
37 |
38 | ```xml
39 |
43 | ```
44 |
45 | ### iOS
46 |
47 | Specify your API key in the application delegate `ios/Runner/AppDelegate.m`:
48 |
49 | ```objectivec
50 | #include "AppDelegate.h"
51 | #include "GeneratedPluginRegistrant.h"
52 | #import "GoogleMaps/GoogleMaps.h"
53 |
54 | @implementation AppDelegate
55 |
56 | - (BOOL)application:(UIApplication *)application
57 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
58 | [GMSServices provideAPIKey:@"YOUR KEY HERE"];
59 | [GeneratedPluginRegistrant registerWithRegistry:self];
60 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
61 | }
62 | @end
63 | ```
64 |
65 | Or in your swift code, specify your API key in the application delegate `ios/Runner/AppDelegate.swift`:
66 |
67 | ```swift
68 | import UIKit
69 | import Flutter
70 | import GoogleMaps
71 |
72 | @UIApplicationMain
73 | @objc class AppDelegate: FlutterAppDelegate {
74 | override func application(
75 | _ application: UIApplication,
76 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
77 | ) -> Bool {
78 | GMSServices.provideAPIKey("YOUR KEY HERE")
79 | GeneratedPluginRegistrant.register(with: self)
80 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
81 | }
82 | }
83 | ```
84 | Opt-in to the embedded views preview by adding a boolean property to the app's `Info.plist` file
85 | with the key `io.flutter.embedded_views_preview` and the value `YES`.
86 |
87 | #### Android/iOS Directions API
88 |
89 | Add in your `main.dart` within `main` function `GoogleMap.init('API_KEY');` before running the app.
90 |
91 | ```dart
92 | void main() {
93 | GoogleMap.init('API_KEY');
94 | WidgetsFlutterBinding.ensureInitialized();
95 | runApp(MyApp());
96 | }
97 | ```
98 |
99 | For more info about **mobile** map setup, view [google_maps_flutter](https://pub.dev/packages/google_maps_flutter) plugin.
100 |
101 | ### Add GoogleMap Widget
102 |
103 | ```dart
104 | import 'package:flutter/material.dart';
105 | import 'package:flutter_google_maps/flutter_google_maps.dart';
106 |
107 | ...
108 | GlobalKey _key = GlobalKey();
109 |
110 | @override
111 | Widget build(BuildContext context) => GoogleMap(
112 | key: _key,
113 | ),
114 | ...
115 | ```
116 |
117 | And now you're ready to go.
118 |
119 | ## Examples
120 |
121 | ### GoogleMap widget can be configured with:
122 |
123 | | Property | Type | Description |
124 | | :---------------: | :--------------------: | :------------------------------------------------------------------------------: |
125 | | initialPosition | GeoCoord | The initial position of the map's camera |
126 | | initialZoom | double | The initial zoom of the map's camera |
127 | | mapType | MapType | Type of map tiles to be rendered |
128 | | minZoom | double | The preferred minimum zoom level or null, if unbounded from below |
129 | | maxZoom | double | The preferred maximum zoom level or null, if unbounded from above |
130 | | mapStyle | String | Sets the styling of the base map |
131 | | mobilePreferences | MobileMapPreferences | Set of mobile map preferences |
132 | | webPreferences | WebMapPreferences | Set of web map preferences |
133 | | interactive | bool | Defines whether map is interactive or not |
134 | | onTap | ValueChanged | Called every time a GoogleMap is tapped |
135 | | onLongPress | ValueChanged | Called every time a GoogleMap is long pressed (for web when right mouse clicked) |
136 | | markers | Set | Markers to be placed on the map |
137 |
138 | **`MapType` is one of following variants:**
139 |
140 | * `none` -> do not display map tiles
141 | * `roadmap` -> normal tiles (traffic and labels, subtle terrain information)
142 | * `satellite` -> satellite imaging tiles (aerial photos)
143 | * `terrain` -> terrain tiles (indicates type and height of terrain)
144 | * `hybrid` -> hybrid tiles (satellite images with some labels/overlays)
145 |
146 | **`MobileMapPreferences` can be configured with:**
147 |
148 | | Property | Type | Description |
149 | | :----------------------: | :--------: | :--------------------------------------------------------------------------------: |
150 | | compassEnabled | bool | True if the map should show a compass when rotated |
151 | | mapToolbarEnabled | bool | True if the map should show a toolbar when you interact with the map. Android only |
152 | | myLocationEnabled | bool | True if a "My Location" layer should be shown on the map |
153 | | myLocationButtonEnabled | bool | Enables or disables the my-location button |
154 | | indoorViewEnabled | bool | Enables or disables the indoor view from the map |
155 | | trafficEnabled | bool | Enables or disables the traffic layer of the map |
156 | | buildingsEnabled | bool | Enables or disables showing 3D buildings where available |
157 | | padding | EdgeInsets | Padding to be set on mapdetails |
158 | | rotateGesturesEnabled | bool | True if the map view should respond to rotate gestures |
159 | | scrollGesturesEnabled | bool | True if the map view should respond to scroll gestures |
160 | | zoomGesturesEnabled | bool | True if the map view should respond to zoom gestures |
161 | | tiltGesturesEnabled | bool | True if the map view should respond to tilt gestures |
162 |
163 | **`WebMapPreferences` can be configured with:**
164 |
165 | | Property | Type | Description |
166 | | :----------------: | :--: | :---------------------------------------: |
167 | | streetViewControl | bool | Enables or disables streetViewControl |
168 | | fullscreenControl | bool | Enables or disables fullscreenControl |
169 | | mapTypeControl | bool | Enables or disables mapTypeControl |
170 | | scrollwheel | bool | Enables or disables scrollwheel |
171 | | panControl | bool | Enables or disables panControl |
172 | | overviewMapControl | bool | Enables or disables overviewMapControl |
173 | | rotateControl | bool | Enables or disables rotateControl |
174 | | scaleControl | bool | Enables or disables scaleControl |
175 | | zoomControl | bool | Enables or disables zoomControl |
176 | | dragGestures | bool | Enables or disables flutter drag gestures |
177 |
178 | ### To prepare for interacting with GoogleMap you will need to:
179 |
180 | Create a `key` and assign it to the `GoogleMap` widget.
181 |
182 | ### GoogleMap widget has 2 static methods, they are:
183 |
184 | * MapOperations of(GlobalKey key);
185 |
186 | Gets [MapOperations] interface via provided `key` of [GoogleMapStateBase] state.
187 |
188 |
189 | * void init(String apiKey);
190 |
191 | Initializer of [GoogleMap]. `Required` if `Directions API` will be needed. For other cases, could be ignored.
192 |
193 |
194 | ### To interact with GoogleMap you'll need to:
195 |
196 | **Use static `of` method**
197 |
198 | Here's list of interactions:
199 |
200 | * Move camera to the new bounds
201 | ```dart
202 | void moveCameraBounds(
203 | GeoCoordBounds newBounds, {
204 | double padding = 0,
205 | bool animated = true,
206 | bool waitUntilReady = true,
207 | });
208 | ```
209 |
210 | * Move camera to the new coordinates
211 | ```dart
212 | void moveCamera(
213 | GeoCoord latLng, {
214 | bool animated = true,
215 | bool waitUntilReady = true,
216 | double zoom,
217 | });
218 | ```
219 |
220 | * Zoom camera
221 | ```dart
222 | void zoomCamera(
223 | double zoom, {
224 | bool animated = true,
225 | bool waitUntilReady = true,
226 | });
227 | ```
228 |
229 | * Get center coordinates of the map
230 | ```dart
231 | FutureOr get center;
232 | ```
233 |
234 | * Change Map Style.
235 |
236 | The style string can be generated using [map style tool](https://mapstyle.withgoogle.com/).
237 | Also, refer [iOS](https://developers.google.com/maps/documentation/ios-sdk/style-reference)
238 | and [Android](https://developers.google.com/maps/documentation/android-sdk/style-reference)
239 | style reference for more information regarding the supported styles.
240 |
241 | ```dart
242 | void changeMapStyle(String mapStyle);
243 | ```
244 |
245 | * Add marker to the map by given [position]
246 | ```dart
247 | void addMarkerRaw(
248 | GeoCoord position, {
249 | String label,
250 | String icon,
251 | String info,
252 | ValueChanged onTap,
253 | VOidCallback onInfoWindowTap,
254 | });
255 | ```
256 | **Please note:** [icon] could be a *path to an image asset* or it could be an instance of *ByteString*.
257 |
258 | * Add marker to the map by given [marker] object
259 | ```dart
260 | void addMarker(Marker marker);
261 | ```
262 |
263 | * Remove marker from the map by given [position]
264 | ```dart
265 | void removeMarker(GeoCoord position);
266 | ```
267 |
268 | * Remove all markers from the map
269 | ```dart
270 | void clearMarkers();
271 | ```
272 |
273 | * Add direction to the map by given [origin] and [destination] coordinates
274 | ```dart
275 | void addDirection(
276 | dynamic origin,
277 | dynamic destination, {
278 | String startLabel,
279 | String startIcon,
280 | String startInfo,
281 | String endLabel,
282 | String endIcon,
283 | String endInfo,
284 | });
285 | ```
286 |
287 | * Remove direction from the map by given [origin] and [destination] coordinates
288 | ```dart
289 | void removeDirection(dynamic origin, dynamic destination);
290 | ```
291 |
292 | * Remove all directions from the map
293 | ```dart
294 | void clearDirections();
295 | ```
296 |
297 | * Add polygon to the map by given [id] and [points]
298 | ```dart
299 | void addPolygon(
300 | String id,
301 | Iterable points, {
302 | ValueChanged onTap,
303 | Color strokeColor = const Color(0x000000),
304 | double strokeOpacity = 0.8,
305 | double strokeWidth = 1,
306 | Color fillColor = const Color(0x000000),
307 | double fillOpacity = 0.35,
308 | });
309 | ```
310 |
311 | * Edit polygon on the map by given [id] and [points]
312 | ```dart
313 | void editPolygon(
314 | String id,
315 | Iterable points, {
316 | ValueChanged onTap,
317 | Color strokeColor = const Color(0x000000),
318 | double strokeOpacity = 0.8,
319 | double strokeWeight = 1,
320 | Color fillColor = const Color(0x000000),
321 | double fillOpacity = 0.35,
322 | });
323 | ```
324 |
325 | * Remove polygon from the map by given [id].
326 | ```dart
327 | void removePolygon(String id);
328 | ```
329 |
330 | * Remove all polygones from the map.
331 | ```dart
332 | void clearPolygons();
333 | ```
334 |
335 | ## Feature requests and Bug reports
336 |
337 | Feel free to post a feature requests or report a bug [here](https://github.com/marchdev-tk/flutter_google_maps/issues).
338 |
339 | ## TODO
340 |
341 | * Add circles support
342 | * Add polyline support
343 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:pedantic/analysis_options.1.8.0.yaml
2 |
3 | analyzer:
4 | errors:
5 | undefined_prefixed_name: ignore
6 |
--------------------------------------------------------------------------------
/assets/images/marker_a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/assets/images/marker_a.png
--------------------------------------------------------------------------------
/assets/images/marker_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/assets/images/marker_b.png
--------------------------------------------------------------------------------
/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 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 | *.lock
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Exceptions to above rules.
38 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
39 |
--------------------------------------------------------------------------------
/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: 2b4b6eef5d361b4114ee4198aa15075890717440
8 | channel: unknown
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_google_maps_example
2 |
3 | Demonstrates how to use the flutter_google_maps package.
4 |
5 | ## Usage
6 |
7 | ```dart
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter/foundation.dart';
10 | import 'package:flutter_google_maps/flutter_google_maps.dart';
11 |
12 | void main() {
13 | GoogleMap.init('API_KEY');
14 | WidgetsFlutterBinding.ensureInitialized();
15 | runApp(MyApp());
16 | }
17 |
18 | class MyApp extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return MaterialApp(
22 | title: 'Google Map Demo',
23 | theme: ThemeData(
24 | primarySwatch: Colors.blue,
25 | ),
26 | home: MyHomePage(),
27 | );
28 | }
29 | }
30 |
31 | class MyHomePage extends StatefulWidget {
32 | @override
33 | _MyHomePageState createState() => _MyHomePageState();
34 | }
35 |
36 | class _MyHomePageState extends State {
37 | final _scaffoldKey = GlobalKey();
38 | final _key = GlobalKey();
39 | bool _polygonAdded = false;
40 | bool _darkMapStyle = false;
41 | String _mapStyle;
42 |
43 | List _buildClearButtons() => [
44 | RaisedButton.icon(
45 | color: Colors.red,
46 | textColor: Colors.white,
47 | icon: Icon(Icons.bubble_chart),
48 | label: Text('CLEAR POLYGONS'),
49 | onPressed: () {
50 | GoogleMap.of(_key).clearPolygons();
51 | setState(() => _polygonAdded = false);
52 | },
53 | ),
54 | const SizedBox(width: 16),
55 | RaisedButton.icon(
56 | color: Colors.red,
57 | textColor: Colors.white,
58 | icon: Icon(Icons.pin_drop),
59 | label: Text('CLEAR MARKERS'),
60 | onPressed: () {
61 | GoogleMap.of(_key).clearMarkers();
62 | },
63 | ),
64 | const SizedBox(width: 16),
65 | RaisedButton.icon(
66 | color: Colors.red,
67 | textColor: Colors.white,
68 | icon: Icon(Icons.directions),
69 | label: Text('CLEAR DIRECTIONS'),
70 | onPressed: () {
71 | GoogleMap.of(_key).clearDirections();
72 | },
73 | ),
74 | ];
75 |
76 | List _buildAddButtons() => [
77 | FloatingActionButton(
78 | child: Icon(_polygonAdded ? Icons.edit : Icons.bubble_chart),
79 | backgroundColor: _polygonAdded ? Colors.orange : null,
80 | onPressed: () {
81 | if (!_polygonAdded) {
82 | GoogleMap.of(_key).addPolygon(
83 | '1',
84 | polygon,
85 | onTap: (polygonId) async {
86 | await showDialog(
87 | context: context,
88 | builder: (context) => AlertDialog(
89 | content: Text(
90 | 'This dialog was opened by tapping on the polygon!\n'
91 | 'Polygon ID is $polygonId',
92 | ),
93 | actions: [
94 | FlatButton(
95 | onPressed: Navigator.of(context).pop,
96 | child: Text('CLOSE'),
97 | ),
98 | ],
99 | ),
100 | );
101 | },
102 | );
103 | } else {
104 | GoogleMap.of(_key).editPolygon(
105 | '1',
106 | polygon,
107 | fillColor: Colors.purple,
108 | strokeColor: Colors.purple,
109 | );
110 | }
111 |
112 | setState(() => _polygonAdded = true);
113 | },
114 | ),
115 | const SizedBox(width: 16),
116 | FloatingActionButton(
117 | child: Icon(Icons.pin_drop),
118 | onPressed: () {
119 | GoogleMap.of(_key).addMarkerRaw(
120 | GeoCoord(33.875513, -117.550257),
121 | info: 'test info',
122 | onInfoWindowTap: () async {
123 | await showDialog(
124 | context: context,
125 | builder: (context) => AlertDialog(
126 | content: Text(
127 | 'This dialog was opened by tapping on the InfoWindow!'),
128 | actions: [
129 | FlatButton(
130 | onPressed: Navigator.of(context).pop,
131 | child: Text('CLOSE'),
132 | ),
133 | ],
134 | ),
135 | );
136 | },
137 | );
138 | GoogleMap.of(_key).addMarkerRaw(
139 | GeoCoord(33.775513, -117.450257),
140 | icon: 'assets/images/map-marker-warehouse.png',
141 | info: contentString,
142 | );
143 | },
144 | ),
145 | const SizedBox(width: 16),
146 | FloatingActionButton(
147 | child: Icon(Icons.directions),
148 | onPressed: () {
149 | GoogleMap.of(_key).addDirection(
150 | 'San Francisco, CA',
151 | 'San Jose, CA',
152 | startLabel: '1',
153 | startInfo: 'San Francisco, CA',
154 | endIcon: 'assets/images/map-marker-warehouse.png',
155 | endInfo: 'San Jose, CA',
156 | );
157 | },
158 | ),
159 | ];
160 |
161 | @override
162 | Widget build(BuildContext context) => Scaffold(
163 | key: _scaffoldKey,
164 | appBar: AppBar(
165 | title: Text('Google Map'),
166 | ),
167 | body: Stack(
168 | children: [
169 | Positioned.fill(
170 | child: GoogleMap(
171 | key: _key,
172 | markers: {
173 | Marker(
174 | GeoCoord(34.0469058, -118.3503948),
175 | ),
176 | },
177 | initialZoom: 12,
178 | initialPosition:
179 | GeoCoord(34.0469058, -118.3503948), // Los Angeles, CA
180 | mapType: MapType.roadmap,
181 | mapStyle: _mapStyle,
182 | interactive: true,
183 | onTap: (coord) =>
184 | _scaffoldKey.currentState.showSnackBar(SnackBar(
185 | content: Text(coord?.toString()),
186 | duration: const Duration(seconds: 2),
187 | )),
188 | mobilePreferences: const MobileMapPreferences(
189 | trafficEnabled: true,
190 | zoomControlsEnabled: false,
191 | ),
192 | webPreferences: WebMapPreferences(
193 | fullscreenControl: true,
194 | zoomControl: true,
195 | ),
196 | ),
197 | ),
198 | Positioned(
199 | top: 16,
200 | left: 16,
201 | child: FloatingActionButton(
202 | child: Icon(Icons.person_pin_circle),
203 | onPressed: () {
204 | final bounds = GeoCoordBounds(
205 | northeast: GeoCoord(34.021307, -117.432317),
206 | southwest: GeoCoord(33.835745, -117.712785),
207 | );
208 | GoogleMap.of(_key).moveCameraBounds(bounds);
209 | GoogleMap.of(_key).addMarkerRaw(
210 | GeoCoord(
211 | (bounds.northeast.latitude + bounds.southwest.latitude) /
212 | 2,
213 | (bounds.northeast.longitude +
214 | bounds.southwest.longitude) /
215 | 2,
216 | ),
217 | onTap: (markerId) async {
218 | await showDialog(
219 | context: context,
220 | builder: (context) => AlertDialog(
221 | content: Text(
222 | 'This dialog was opened by tapping on the marker!\n'
223 | 'Marker ID is $markerId',
224 | ),
225 | actions: [
226 | FlatButton(
227 | onPressed: Navigator.of(context).pop,
228 | child: Text('CLOSE'),
229 | ),
230 | ],
231 | ),
232 | );
233 | },
234 | );
235 | },
236 | ),
237 | ),
238 | Positioned(
239 | top: 16,
240 | right: kIsWeb ? 60 : 16,
241 | child: FloatingActionButton(
242 | onPressed: () {
243 | if (_darkMapStyle) {
244 | GoogleMap.of(_key).changeMapStyle(null);
245 | _mapStyle = null;
246 | } else {
247 | GoogleMap.of(_key).changeMapStyle(darkMapStyle);
248 | _mapStyle = darkMapStyle;
249 | }
250 |
251 | setState(() => _darkMapStyle = !_darkMapStyle);
252 | },
253 | backgroundColor: _darkMapStyle ? Colors.black : Colors.white,
254 | child: Icon(
255 | _darkMapStyle ? Icons.wb_sunny : Icons.brightness_3,
256 | color: _darkMapStyle ? Colors.white : Colors.black,
257 | ),
258 | ),
259 | ),
260 | Positioned(
261 | left: 16,
262 | right: kIsWeb ? 60 : 16,
263 | bottom: 16,
264 | child: Row(
265 | children: [
266 | LayoutBuilder(
267 | builder: (context, constraints) =>
268 | constraints.maxWidth < 1000
269 | ? Row(children: _buildClearButtons())
270 | : Column(
271 | crossAxisAlignment: CrossAxisAlignment.start,
272 | children: _buildClearButtons(),
273 | ),
274 | ),
275 | Spacer(),
276 | ..._buildAddButtons(),
277 | ],
278 | ),
279 | ),
280 | ],
281 | ),
282 | );
283 | }
284 |
285 | const darkMapStyle = r'''
286 | [
287 | {
288 | "elementType": "geometry",
289 | "stylers": [
290 | {
291 | "color": "#212121"
292 | }
293 | ]
294 | },
295 | {
296 | "elementType": "labels.icon",
297 | "stylers": [
298 | {
299 | "visibility": "off"
300 | }
301 | ]
302 | },
303 | {
304 | "elementType": "labels.text.fill",
305 | "stylers": [
306 | {
307 | "color": "#757575"
308 | }
309 | ]
310 | },
311 | {
312 | "elementType": "labels.text.stroke",
313 | "stylers": [
314 | {
315 | "color": "#212121"
316 | }
317 | ]
318 | },
319 | {
320 | "featureType": "administrative",
321 | "elementType": "geometry",
322 | "stylers": [
323 | {
324 | "color": "#757575"
325 | }
326 | ]
327 | },
328 | {
329 | "featureType": "administrative.country",
330 | "elementType": "labels.text.fill",
331 | "stylers": [
332 | {
333 | "color": "#9e9e9e"
334 | }
335 | ]
336 | },
337 | {
338 | "featureType": "administrative.land_parcel",
339 | "stylers": [
340 | {
341 | "visibility": "off"
342 | }
343 | ]
344 | },
345 | {
346 | "featureType": "administrative.locality",
347 | "elementType": "labels.text.fill",
348 | "stylers": [
349 | {
350 | "color": "#bdbdbd"
351 | }
352 | ]
353 | },
354 | {
355 | "featureType": "poi",
356 | "elementType": "labels.text.fill",
357 | "stylers": [
358 | {
359 | "color": "#757575"
360 | }
361 | ]
362 | },
363 | {
364 | "featureType": "poi.park",
365 | "elementType": "geometry",
366 | "stylers": [
367 | {
368 | "color": "#181818"
369 | }
370 | ]
371 | },
372 | {
373 | "featureType": "poi.park",
374 | "elementType": "labels.text.fill",
375 | "stylers": [
376 | {
377 | "color": "#616161"
378 | }
379 | ]
380 | },
381 | {
382 | "featureType": "poi.park",
383 | "elementType": "labels.text.stroke",
384 | "stylers": [
385 | {
386 | "color": "#1b1b1b"
387 | }
388 | ]
389 | },
390 | {
391 | "featureType": "road",
392 | "elementType": "geometry.fill",
393 | "stylers": [
394 | {
395 | "color": "#2c2c2c"
396 | }
397 | ]
398 | },
399 | {
400 | "featureType": "road",
401 | "elementType": "labels.text.fill",
402 | "stylers": [
403 | {
404 | "color": "#8a8a8a"
405 | }
406 | ]
407 | },
408 | {
409 | "featureType": "road.arterial",
410 | "elementType": "geometry",
411 | "stylers": [
412 | {
413 | "color": "#373737"
414 | }
415 | ]
416 | },
417 | {
418 | "featureType": "road.highway",
419 | "elementType": "geometry",
420 | "stylers": [
421 | {
422 | "color": "#3c3c3c"
423 | }
424 | ]
425 | },
426 | {
427 | "featureType": "road.highway.controlled_access",
428 | "elementType": "geometry",
429 | "stylers": [
430 | {
431 | "color": "#4e4e4e"
432 | }
433 | ]
434 | },
435 | {
436 | "featureType": "road.local",
437 | "elementType": "labels.text.fill",
438 | "stylers": [
439 | {
440 | "color": "#616161"
441 | }
442 | ]
443 | },
444 | {
445 | "featureType": "transit",
446 | "elementType": "labels.text.fill",
447 | "stylers": [
448 | {
449 | "color": "#757575"
450 | }
451 | ]
452 | },
453 | {
454 | "featureType": "water",
455 | "elementType": "geometry",
456 | "stylers": [
457 | {
458 | "color": "#000000"
459 | }
460 | ]
461 | },
462 | {
463 | "featureType": "water",
464 | "elementType": "labels.text.fill",
465 | "stylers": [
466 | {
467 | "color": "#3d3d3d"
468 | }
469 | ]
470 | }
471 | ]
472 | ''';
473 |
474 | const contentString = r'''
475 |
476 |
477 |
Uluru
478 |
479 |
480 | Uluru, also referred to as Ayers Rock, is a large
481 | sandstone rock formation in the southern part of the
482 | Northern Territory, central Australia. It lies 335 km (208 mi)
483 | south west of the nearest large town, Alice Springs; 450 km
484 | (280 mi) by road. Kata Tjuta and Uluru are the two major
485 | features of the Uluru - Kata Tjuta National Park. Uluru is
486 | sacred to the Pitjantjatjara and Yankunytjatjara, the
487 | Aboriginal people of the area. It has many springs, waterholes,
488 | rock caves and ancient paintings. Uluru is listed as a World
489 | Heritage Site.
490 |
491 |
492 | Attribution: Uluru,
493 |
494 | http://en.wikipedia.org/w/index.php?title=Uluru
495 |
496 | (last visited June 22, 2009).
497 |
498 |
499 |
500 | ''';
501 |
502 | const polygon = [
503 | GeoCoord(32.707868, -117.191018),
504 | GeoCoord(32.705645, -117.191096),
505 | GeoCoord(32.697756, -117.166664),
506 | GeoCoord(32.686486, -117.163206),
507 | GeoCoord(32.675876, -117.169452),
508 | GeoCoord(32.674726, -117.165233),
509 | GeoCoord(32.679833, -117.158487),
510 | GeoCoord(32.677571, -117.153893),
511 | GeoCoord(32.671987, -117.160079),
512 | GeoCoord(32.667547, -117.160477),
513 | GeoCoord(32.654748, -117.147579),
514 | GeoCoord(32.651933, -117.150312),
515 | GeoCoord(32.649676, -117.144334),
516 | GeoCoord(32.631665, -117.138201),
517 | GeoCoord(32.632033, -117.132249),
518 | GeoCoord(32.630156, -117.137234),
519 | GeoCoord(32.628072, -117.136479),
520 | GeoCoord(32.630315, -117.131443),
521 | GeoCoord(32.625930, -117.135312),
522 | GeoCoord(32.623754, -117.131664),
523 | GeoCoord(32.627465, -117.130883),
524 | GeoCoord(32.622598, -117.128791),
525 | GeoCoord(32.622622, -117.133183),
526 | GeoCoord(32.618690, -117.133634),
527 | GeoCoord(32.618980, -117.128403),
528 | GeoCoord(32.609847, -117.132502),
529 | GeoCoord(32.604198, -117.125333),
530 | GeoCoord(32.588260, -117.122032),
531 | GeoCoord(32.591164, -117.116851),
532 | GeoCoord(32.587601, -117.105968),
533 | GeoCoord(32.583792, -117.104434),
534 | GeoCoord(32.570566, -117.101382),
535 | GeoCoord(32.569256, -117.122378),
536 | GeoCoord(32.560825, -117.122903),
537 | GeoCoord(32.557753, -117.131040),
538 | GeoCoord(32.542737, -117.124883),
539 | GeoCoord(32.534156, -117.126062),
540 | GeoCoord(32.563255, -117.134963),
541 | GeoCoord(32.584055, -117.134263),
542 | GeoCoord(32.619405, -117.140001),
543 | GeoCoord(32.655293, -117.157349),
544 | GeoCoord(32.669944, -117.169624),
545 | GeoCoord(32.682710, -117.189445),
546 | GeoCoord(32.685297, -117.208773),
547 | GeoCoord(32.679814, -117.224882),
548 | GeoCoord(32.697212, -117.227058),
549 | GeoCoord(32.707701, -117.219816),
550 | GeoCoord(32.711931, -117.214107),
551 | GeoCoord(32.715026, -117.196521),
552 | GeoCoord(32.713053, -117.189703),
553 | GeoCoord(32.707868, -117.191018),
554 | ];
555 | ```
556 |
557 | ## Getting Started
558 |
559 | For help getting started with Flutter, view our
560 | [online documentation](https://flutter.dev/docs), which offers tutorials,
561 | samples, guidance on mobile development, and a full API reference.
562 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/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 plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | applicationId "marchdev.tk.flutter_google_maps_example"
41 | minSdkVersion 16
42 | targetSdkVersion 28
43 | versionCode flutterVersionCode.toInteger()
44 | versionName flutterVersionName
45 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
46 | }
47 |
48 | buildTypes {
49 | release {
50 | // Signing with the debug keys for now, so `flutter run --release` works.
51 | signingConfig signingConfigs.debug
52 | }
53 | }
54 | }
55 |
56 | flutter {
57 | source '../..'
58 | }
59 |
60 | dependencies {
61 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
62 | testImplementation 'junit:junit:4.12'
63 | androidTestImplementation 'androidx.test:runner:1.1.1'
64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
65 | }
66 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
29 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package marchdev.tk.flutter_google_maps_example
2 |
3 | import androidx.annotation.NonNull;
4 | import io.flutter.embedding.android.FlutterActivity
5 | import io.flutter.embedding.engine.FlutterEngine
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity: FlutterActivity() {
9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
10 | GeneratedPluginRegistrant.registerWith(flutterEngine);
11 | }
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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/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-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/assets/images/map-marker-warehouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/example/assets/images/map-marker-warehouse.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 | $(DEVELOPMENT_LANGUAGE)
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 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
17 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
18 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
19 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXCopyFilesBuildPhase section */
23 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
24 | isa = PBXCopyFilesBuildPhase;
25 | buildActionMask = 2147483647;
26 | dstPath = "";
27 | dstSubfolderSpec = 10;
28 | files = (
29 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
30 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
31 | );
32 | name = "Embed Frameworks";
33 | runOnlyForDeploymentPostprocessing = 0;
34 | };
35 | /* End PBXCopyFilesBuildPhase section */
36 |
37 | /* Begin PBXFileReference section */
38 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
39 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
40 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
41 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
42 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
43 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
44 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
45 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
46 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
47 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
48 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
49 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
50 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
51 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
52 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
61 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
62 | );
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | /* End PBXFrameworksBuildPhase section */
66 |
67 | /* Begin PBXGroup section */
68 | 9740EEB11CF90186004384FC /* Flutter */ = {
69 | isa = PBXGroup;
70 | children = (
71 | 3B80C3931E831B6300D905FE /* App.framework */,
72 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
73 | 9740EEBA1CF902C7004384FC /* Flutter.framework */,
74 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
75 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
76 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
77 | );
78 | name = Flutter;
79 | sourceTree = "";
80 | };
81 | 97C146E51CF9000F007C117D = {
82 | isa = PBXGroup;
83 | children = (
84 | 9740EEB11CF90186004384FC /* Flutter */,
85 | 97C146F01CF9000F007C117D /* Runner */,
86 | 97C146EF1CF9000F007C117D /* Products */,
87 | );
88 | sourceTree = "";
89 | };
90 | 97C146EF1CF9000F007C117D /* Products */ = {
91 | isa = PBXGroup;
92 | children = (
93 | 97C146EE1CF9000F007C117D /* Runner.app */,
94 | );
95 | name = Products;
96 | sourceTree = "";
97 | };
98 | 97C146F01CF9000F007C117D /* Runner */ = {
99 | isa = PBXGroup;
100 | children = (
101 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
102 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
103 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
104 | 97C147021CF9000F007C117D /* Info.plist */,
105 | 97C146F11CF9000F007C117D /* Supporting Files */,
106 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
107 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
108 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
109 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
110 | );
111 | path = Runner;
112 | sourceTree = "";
113 | };
114 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
115 | isa = PBXGroup;
116 | children = (
117 | );
118 | name = "Supporting Files";
119 | sourceTree = "";
120 | };
121 | /* End PBXGroup section */
122 |
123 | /* Begin PBXNativeTarget section */
124 | 97C146ED1CF9000F007C117D /* Runner */ = {
125 | isa = PBXNativeTarget;
126 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
127 | buildPhases = (
128 | 9740EEB61CF901F6004384FC /* Run Script */,
129 | 97C146EA1CF9000F007C117D /* Sources */,
130 | 97C146EB1CF9000F007C117D /* Frameworks */,
131 | 97C146EC1CF9000F007C117D /* Resources */,
132 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
133 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
134 | );
135 | buildRules = (
136 | );
137 | dependencies = (
138 | );
139 | name = Runner;
140 | productName = Runner;
141 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
142 | productType = "com.apple.product-type.application";
143 | };
144 | /* End PBXNativeTarget section */
145 |
146 | /* Begin PBXProject section */
147 | 97C146E61CF9000F007C117D /* Project object */ = {
148 | isa = PBXProject;
149 | attributes = {
150 | LastUpgradeCheck = 1020;
151 | ORGANIZATIONNAME = "";
152 | TargetAttributes = {
153 | 97C146ED1CF9000F007C117D = {
154 | CreatedOnToolsVersion = 7.3.1;
155 | LastSwiftMigration = 1100;
156 | };
157 | };
158 | };
159 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
160 | compatibilityVersion = "Xcode 3.2";
161 | developmentRegion = en;
162 | hasScannedForEncodings = 0;
163 | knownRegions = (
164 | en,
165 | Base,
166 | );
167 | mainGroup = 97C146E51CF9000F007C117D;
168 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
169 | projectDirPath = "";
170 | projectRoot = "";
171 | targets = (
172 | 97C146ED1CF9000F007C117D /* Runner */,
173 | );
174 | };
175 | /* End PBXProject section */
176 |
177 | /* Begin PBXResourcesBuildPhase section */
178 | 97C146EC1CF9000F007C117D /* Resources */ = {
179 | isa = PBXResourcesBuildPhase;
180 | buildActionMask = 2147483647;
181 | files = (
182 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
183 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
184 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
185 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
186 | );
187 | runOnlyForDeploymentPostprocessing = 0;
188 | };
189 | /* End PBXResourcesBuildPhase section */
190 |
191 | /* Begin PBXShellScriptBuildPhase section */
192 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
193 | isa = PBXShellScriptBuildPhase;
194 | buildActionMask = 2147483647;
195 | files = (
196 | );
197 | inputPaths = (
198 | );
199 | name = "Thin Binary";
200 | outputPaths = (
201 | );
202 | runOnlyForDeploymentPostprocessing = 0;
203 | shellPath = /bin/sh;
204 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
205 | };
206 | 9740EEB61CF901F6004384FC /* Run Script */ = {
207 | isa = PBXShellScriptBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | );
211 | inputPaths = (
212 | );
213 | name = "Run Script";
214 | outputPaths = (
215 | );
216 | runOnlyForDeploymentPostprocessing = 0;
217 | shellPath = /bin/sh;
218 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
219 | };
220 | /* End PBXShellScriptBuildPhase section */
221 |
222 | /* Begin PBXSourcesBuildPhase section */
223 | 97C146EA1CF9000F007C117D /* Sources */ = {
224 | isa = PBXSourcesBuildPhase;
225 | buildActionMask = 2147483647;
226 | files = (
227 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
228 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | };
232 | /* End PBXSourcesBuildPhase section */
233 |
234 | /* Begin PBXVariantGroup section */
235 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
236 | isa = PBXVariantGroup;
237 | children = (
238 | 97C146FB1CF9000F007C117D /* Base */,
239 | );
240 | name = Main.storyboard;
241 | sourceTree = "";
242 | };
243 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
244 | isa = PBXVariantGroup;
245 | children = (
246 | 97C147001CF9000F007C117D /* Base */,
247 | );
248 | name = LaunchScreen.storyboard;
249 | sourceTree = "";
250 | };
251 | /* End PBXVariantGroup section */
252 |
253 | /* Begin XCBuildConfiguration section */
254 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
255 | isa = XCBuildConfiguration;
256 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
257 | buildSettings = {
258 | ALWAYS_SEARCH_USER_PATHS = NO;
259 | CLANG_ANALYZER_NONNULL = YES;
260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
261 | CLANG_CXX_LIBRARY = "libc++";
262 | CLANG_ENABLE_MODULES = YES;
263 | CLANG_ENABLE_OBJC_ARC = YES;
264 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
265 | CLANG_WARN_BOOL_CONVERSION = YES;
266 | CLANG_WARN_COMMA = YES;
267 | CLANG_WARN_CONSTANT_CONVERSION = YES;
268 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
269 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
270 | CLANG_WARN_EMPTY_BODY = YES;
271 | CLANG_WARN_ENUM_CONVERSION = YES;
272 | CLANG_WARN_INFINITE_RECURSION = YES;
273 | CLANG_WARN_INT_CONVERSION = YES;
274 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
275 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
276 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
277 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
279 | CLANG_WARN_STRICT_PROTOTYPES = YES;
280 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
281 | CLANG_WARN_UNREACHABLE_CODE = YES;
282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
284 | COPY_PHASE_STRIP = NO;
285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
286 | ENABLE_NS_ASSERTIONS = NO;
287 | ENABLE_STRICT_OBJC_MSGSEND = YES;
288 | GCC_C_LANGUAGE_STANDARD = gnu99;
289 | GCC_NO_COMMON_BLOCKS = YES;
290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
292 | GCC_WARN_UNDECLARED_SELECTOR = YES;
293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
294 | GCC_WARN_UNUSED_FUNCTION = YES;
295 | GCC_WARN_UNUSED_VARIABLE = YES;
296 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
297 | MTL_ENABLE_DEBUG_INFO = NO;
298 | SDKROOT = iphoneos;
299 | SUPPORTED_PLATFORMS = iphoneos;
300 | TARGETED_DEVICE_FAMILY = "1,2";
301 | VALIDATE_PRODUCT = YES;
302 | };
303 | name = Profile;
304 | };
305 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
306 | isa = XCBuildConfiguration;
307 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
308 | buildSettings = {
309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
310 | CLANG_ENABLE_MODULES = YES;
311 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
312 | ENABLE_BITCODE = NO;
313 | FRAMEWORK_SEARCH_PATHS = (
314 | "$(inherited)",
315 | "$(PROJECT_DIR)/Flutter",
316 | );
317 | INFOPLIST_FILE = Runner/Info.plist;
318 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
319 | LIBRARY_SEARCH_PATHS = (
320 | "$(inherited)",
321 | "$(PROJECT_DIR)/Flutter",
322 | );
323 | PRODUCT_BUNDLE_IDENTIFIER = marchdev.tk.flutter_google_maps_example;
324 | PRODUCT_NAME = "$(TARGET_NAME)";
325 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
326 | SWIFT_VERSION = 5.0;
327 | VERSIONING_SYSTEM = "apple-generic";
328 | };
329 | name = Profile;
330 | };
331 | 97C147031CF9000F007C117D /* Debug */ = {
332 | isa = XCBuildConfiguration;
333 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
334 | buildSettings = {
335 | ALWAYS_SEARCH_USER_PATHS = NO;
336 | CLANG_ANALYZER_NONNULL = YES;
337 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
338 | CLANG_CXX_LIBRARY = "libc++";
339 | CLANG_ENABLE_MODULES = YES;
340 | CLANG_ENABLE_OBJC_ARC = YES;
341 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
342 | CLANG_WARN_BOOL_CONVERSION = YES;
343 | CLANG_WARN_COMMA = YES;
344 | CLANG_WARN_CONSTANT_CONVERSION = YES;
345 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
347 | CLANG_WARN_EMPTY_BODY = YES;
348 | CLANG_WARN_ENUM_CONVERSION = YES;
349 | CLANG_WARN_INFINITE_RECURSION = YES;
350 | CLANG_WARN_INT_CONVERSION = YES;
351 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
356 | CLANG_WARN_STRICT_PROTOTYPES = YES;
357 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
358 | CLANG_WARN_UNREACHABLE_CODE = YES;
359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
361 | COPY_PHASE_STRIP = NO;
362 | DEBUG_INFORMATION_FORMAT = dwarf;
363 | ENABLE_STRICT_OBJC_MSGSEND = YES;
364 | ENABLE_TESTABILITY = YES;
365 | GCC_C_LANGUAGE_STANDARD = gnu99;
366 | GCC_DYNAMIC_NO_PIC = NO;
367 | GCC_NO_COMMON_BLOCKS = YES;
368 | GCC_OPTIMIZATION_LEVEL = 0;
369 | GCC_PREPROCESSOR_DEFINITIONS = (
370 | "DEBUG=1",
371 | "$(inherited)",
372 | );
373 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
374 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
375 | GCC_WARN_UNDECLARED_SELECTOR = YES;
376 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
377 | GCC_WARN_UNUSED_FUNCTION = YES;
378 | GCC_WARN_UNUSED_VARIABLE = YES;
379 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
380 | MTL_ENABLE_DEBUG_INFO = YES;
381 | ONLY_ACTIVE_ARCH = YES;
382 | SDKROOT = iphoneos;
383 | TARGETED_DEVICE_FAMILY = "1,2";
384 | };
385 | name = Debug;
386 | };
387 | 97C147041CF9000F007C117D /* Release */ = {
388 | isa = XCBuildConfiguration;
389 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
390 | buildSettings = {
391 | ALWAYS_SEARCH_USER_PATHS = NO;
392 | CLANG_ANALYZER_NONNULL = YES;
393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
394 | CLANG_CXX_LIBRARY = "libc++";
395 | CLANG_ENABLE_MODULES = YES;
396 | CLANG_ENABLE_OBJC_ARC = YES;
397 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
398 | CLANG_WARN_BOOL_CONVERSION = YES;
399 | CLANG_WARN_COMMA = YES;
400 | CLANG_WARN_CONSTANT_CONVERSION = YES;
401 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
403 | CLANG_WARN_EMPTY_BODY = YES;
404 | CLANG_WARN_ENUM_CONVERSION = YES;
405 | CLANG_WARN_INFINITE_RECURSION = YES;
406 | CLANG_WARN_INT_CONVERSION = YES;
407 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
408 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
411 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
412 | CLANG_WARN_STRICT_PROTOTYPES = YES;
413 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
414 | CLANG_WARN_UNREACHABLE_CODE = YES;
415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
416 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
417 | COPY_PHASE_STRIP = NO;
418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
419 | ENABLE_NS_ASSERTIONS = NO;
420 | ENABLE_STRICT_OBJC_MSGSEND = YES;
421 | GCC_C_LANGUAGE_STANDARD = gnu99;
422 | GCC_NO_COMMON_BLOCKS = YES;
423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
425 | GCC_WARN_UNDECLARED_SELECTOR = YES;
426 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
427 | GCC_WARN_UNUSED_FUNCTION = YES;
428 | GCC_WARN_UNUSED_VARIABLE = YES;
429 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
430 | MTL_ENABLE_DEBUG_INFO = NO;
431 | SDKROOT = iphoneos;
432 | SUPPORTED_PLATFORMS = iphoneos;
433 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
434 | TARGETED_DEVICE_FAMILY = "1,2";
435 | VALIDATE_PRODUCT = YES;
436 | };
437 | name = Release;
438 | };
439 | 97C147061CF9000F007C117D /* Debug */ = {
440 | isa = XCBuildConfiguration;
441 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
442 | buildSettings = {
443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
444 | CLANG_ENABLE_MODULES = YES;
445 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
446 | ENABLE_BITCODE = NO;
447 | FRAMEWORK_SEARCH_PATHS = (
448 | "$(inherited)",
449 | "$(PROJECT_DIR)/Flutter",
450 | );
451 | INFOPLIST_FILE = Runner/Info.plist;
452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
453 | LIBRARY_SEARCH_PATHS = (
454 | "$(inherited)",
455 | "$(PROJECT_DIR)/Flutter",
456 | );
457 | PRODUCT_BUNDLE_IDENTIFIER = marchdev.tk.flutter_google_maps_example;
458 | PRODUCT_NAME = "$(TARGET_NAME)";
459 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
460 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
461 | SWIFT_VERSION = 5.0;
462 | VERSIONING_SYSTEM = "apple-generic";
463 | };
464 | name = Debug;
465 | };
466 | 97C147071CF9000F007C117D /* Release */ = {
467 | isa = XCBuildConfiguration;
468 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
469 | buildSettings = {
470 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
471 | CLANG_ENABLE_MODULES = YES;
472 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
473 | ENABLE_BITCODE = NO;
474 | FRAMEWORK_SEARCH_PATHS = (
475 | "$(inherited)",
476 | "$(PROJECT_DIR)/Flutter",
477 | );
478 | INFOPLIST_FILE = Runner/Info.plist;
479 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
480 | LIBRARY_SEARCH_PATHS = (
481 | "$(inherited)",
482 | "$(PROJECT_DIR)/Flutter",
483 | );
484 | PRODUCT_BUNDLE_IDENTIFIER = marchdev.tk.flutter_google_maps_example;
485 | PRODUCT_NAME = "$(TARGET_NAME)";
486 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
487 | SWIFT_VERSION = 5.0;
488 | VERSIONING_SYSTEM = "apple-generic";
489 | };
490 | name = Release;
491 | };
492 | /* End XCBuildConfiguration section */
493 |
494 | /* Begin XCConfigurationList section */
495 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
496 | isa = XCConfigurationList;
497 | buildConfigurations = (
498 | 97C147031CF9000F007C117D /* Debug */,
499 | 97C147041CF9000F007C117D /* Release */,
500 | 249021D3217E4FDB00AE95B9 /* Profile */,
501 | );
502 | defaultConfigurationIsVisible = 0;
503 | defaultConfigurationName = Release;
504 | };
505 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
506 | isa = XCConfigurationList;
507 | buildConfigurations = (
508 | 97C147061CF9000F007C117D /* Debug */,
509 | 97C147071CF9000F007C117D /* Release */,
510 | 249021D4217E4FDB00AE95B9 /* Profile */,
511 | );
512 | defaultConfigurationIsVisible = 0;
513 | defaultConfigurationName = Release;
514 | };
515 | /* End XCConfigurationList section */
516 | };
517 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
518 | }
519 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 | import GoogleMaps
4 |
5 | @UIApplicationMain
6 | @objc class AppDelegate: FlutterAppDelegate {
7 | override func application(
8 | _ application: UIApplication,
9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
10 | ) -> Bool {
11 | GMSServices.provideAPIKey("API_KEY")
12 | GeneratedPluginRegistrant.register(with: self)
13 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marchdev-tk/flutter_google_maps/664c61cb0f64972228a8001397d5e635765d103e/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/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 | flutter_google_maps_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | io.flutter.embedded_views_preview
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:flutter/material.dart';
6 | import 'package:flutter/foundation.dart';
7 | import 'package:flutter_google_maps/flutter_google_maps.dart';
8 |
9 | void main() {
10 | GoogleMap.init('API_KEY');
11 | WidgetsFlutterBinding.ensureInitialized();
12 | runApp(MyApp());
13 | }
14 |
15 | class MyApp extends StatelessWidget {
16 | @override
17 | Widget build(BuildContext context) {
18 | return MaterialApp(
19 | title: 'Google Map Demo',
20 | theme: ThemeData(
21 | primarySwatch: Colors.blue,
22 | ),
23 | home: MyHomePage(),
24 | );
25 | }
26 | }
27 |
28 | class MyHomePage extends StatefulWidget {
29 | @override
30 | _MyHomePageState createState() => _MyHomePageState();
31 | }
32 |
33 | class _MyHomePageState extends State {
34 | final _scaffoldKey = GlobalKey();
35 | final _key = GlobalKey();
36 | bool _polygonAdded = false;
37 | bool _darkMapStyle = false;
38 | String _mapStyle;
39 |
40 | List _buildClearButtons() => [
41 | RaisedButton.icon(
42 | color: Colors.red,
43 | textColor: Colors.white,
44 | icon: Icon(Icons.bubble_chart),
45 | label: Text('CLEAR POLYGONS'),
46 | onPressed: () {
47 | GoogleMap.of(_key).clearPolygons();
48 | setState(() => _polygonAdded = false);
49 | },
50 | ),
51 | const SizedBox(width: 16),
52 | RaisedButton.icon(
53 | color: Colors.red,
54 | textColor: Colors.white,
55 | icon: Icon(Icons.pin_drop),
56 | label: Text('CLEAR MARKERS'),
57 | onPressed: () {
58 | GoogleMap.of(_key).clearMarkers();
59 | },
60 | ),
61 | const SizedBox(width: 16),
62 | RaisedButton.icon(
63 | color: Colors.red,
64 | textColor: Colors.white,
65 | icon: Icon(Icons.directions),
66 | label: Text('CLEAR DIRECTIONS'),
67 | onPressed: () {
68 | GoogleMap.of(_key).clearDirections();
69 | },
70 | ),
71 | ];
72 |
73 | List _buildAddButtons() => [
74 | FloatingActionButton(
75 | child: Icon(_polygonAdded ? Icons.edit : Icons.bubble_chart),
76 | backgroundColor: _polygonAdded ? Colors.orange : null,
77 | onPressed: () {
78 | if (!_polygonAdded) {
79 | GoogleMap.of(_key).addPolygon(
80 | '1',
81 | polygon,
82 | onTap: (polygonId) async {
83 | await showDialog(
84 | context: context,
85 | builder: (context) => AlertDialog(
86 | content: Text(
87 | 'This dialog was opened by tapping on the polygon!\n'
88 | 'Polygon ID is $polygonId',
89 | ),
90 | actions: [
91 | FlatButton(
92 | onPressed: Navigator.of(context).pop,
93 | child: Text('CLOSE'),
94 | ),
95 | ],
96 | ),
97 | );
98 | },
99 | );
100 | } else {
101 | GoogleMap.of(_key).editPolygon(
102 | '1',
103 | polygon,
104 | fillColor: Colors.purple,
105 | strokeColor: Colors.purple,
106 | );
107 | }
108 |
109 | setState(() => _polygonAdded = true);
110 | },
111 | ),
112 | const SizedBox(width: 16),
113 | FloatingActionButton(
114 | child: Icon(Icons.pin_drop),
115 | onPressed: () {
116 | GoogleMap.of(_key).addMarkerRaw(
117 | GeoCoord(33.875513, -117.550257),
118 | info: 'test info',
119 | onInfoWindowTap: () async {
120 | await showDialog(
121 | context: context,
122 | builder: (context) => AlertDialog(
123 | content: Text(
124 | 'This dialog was opened by tapping on the InfoWindow!'),
125 | actions: [
126 | FlatButton(
127 | onPressed: Navigator.of(context).pop,
128 | child: Text('CLOSE'),
129 | ),
130 | ],
131 | ),
132 | );
133 | },
134 | );
135 | GoogleMap.of(_key).addMarkerRaw(
136 | GeoCoord(33.775513, -117.450257),
137 | icon: 'assets/images/map-marker-warehouse.png',
138 | info: contentString,
139 | );
140 | },
141 | ),
142 | const SizedBox(width: 16),
143 | FloatingActionButton(
144 | child: Icon(Icons.directions),
145 | onPressed: () {
146 | GoogleMap.of(_key).addDirection(
147 | 'San Francisco, CA',
148 | 'San Jose, CA',
149 | startLabel: '1',
150 | startInfo: 'San Francisco, CA',
151 | endIcon: 'assets/images/map-marker-warehouse.png',
152 | endInfo: 'San Jose, CA',
153 | );
154 | },
155 | ),
156 | ];
157 |
158 | @override
159 | Widget build(BuildContext context) => Scaffold(
160 | key: _scaffoldKey,
161 | appBar: AppBar(
162 | title: Text('Google Map'),
163 | ),
164 | body: Stack(
165 | children: [
166 | Positioned.fill(
167 | child: GoogleMap(
168 | key: _key,
169 | markers: {
170 | Marker(
171 | GeoCoord(34.0469058, -118.3503948),
172 | ),
173 | },
174 | initialZoom: 12,
175 | initialPosition:
176 | GeoCoord(34.0469058, -118.3503948), // Los Angeles, CA
177 | mapType: MapType.roadmap,
178 | mapStyle: _mapStyle,
179 | interactive: true,
180 | onTap: (coord) =>
181 | _scaffoldKey.currentState.showSnackBar(SnackBar(
182 | content: Text(coord?.toString()),
183 | duration: const Duration(seconds: 2),
184 | )),
185 | mobilePreferences: const MobileMapPreferences(
186 | trafficEnabled: true,
187 | zoomControlsEnabled: false,
188 | ),
189 | webPreferences: WebMapPreferences(
190 | fullscreenControl: true,
191 | zoomControl: true,
192 | ),
193 | ),
194 | ),
195 | Positioned(
196 | top: 16,
197 | left: 16,
198 | child: FloatingActionButton(
199 | child: Icon(Icons.person_pin_circle),
200 | onPressed: () {
201 | final bounds = GeoCoordBounds(
202 | northeast: GeoCoord(34.021307, -117.432317),
203 | southwest: GeoCoord(33.835745, -117.712785),
204 | );
205 | GoogleMap.of(_key).moveCameraBounds(bounds);
206 | GoogleMap.of(_key).addMarkerRaw(
207 | GeoCoord(
208 | (bounds.northeast.latitude + bounds.southwest.latitude) /
209 | 2,
210 | (bounds.northeast.longitude +
211 | bounds.southwest.longitude) /
212 | 2,
213 | ),
214 | onTap: (markerId) async {
215 | await showDialog(
216 | context: context,
217 | builder: (context) => AlertDialog(
218 | content: Text(
219 | 'This dialog was opened by tapping on the marker!\n'
220 | 'Marker ID is $markerId',
221 | ),
222 | actions: [
223 | FlatButton(
224 | onPressed: Navigator.of(context).pop,
225 | child: Text('CLOSE'),
226 | ),
227 | ],
228 | ),
229 | );
230 | },
231 | );
232 | },
233 | ),
234 | ),
235 | Positioned(
236 | top: 16,
237 | right: kIsWeb ? 60 : 16,
238 | child: FloatingActionButton(
239 | onPressed: () {
240 | if (_darkMapStyle) {
241 | GoogleMap.of(_key).changeMapStyle(null);
242 | _mapStyle = null;
243 | } else {
244 | GoogleMap.of(_key).changeMapStyle(darkMapStyle);
245 | _mapStyle = darkMapStyle;
246 | }
247 |
248 | setState(() => _darkMapStyle = !_darkMapStyle);
249 | },
250 | backgroundColor: _darkMapStyle ? Colors.black : Colors.white,
251 | child: Icon(
252 | _darkMapStyle ? Icons.wb_sunny : Icons.brightness_3,
253 | color: _darkMapStyle ? Colors.white : Colors.black,
254 | ),
255 | ),
256 | ),
257 | Positioned(
258 | left: 16,
259 | right: kIsWeb ? 60 : 16,
260 | bottom: 16,
261 | child: Row(
262 | children: [
263 | LayoutBuilder(
264 | builder: (context, constraints) =>
265 | constraints.maxWidth < 1000
266 | ? Row(children: _buildClearButtons())
267 | : Column(
268 | crossAxisAlignment: CrossAxisAlignment.start,
269 | children: _buildClearButtons(),
270 | ),
271 | ),
272 | Spacer(),
273 | ..._buildAddButtons(),
274 | ],
275 | ),
276 | ),
277 | ],
278 | ),
279 | );
280 | }
281 |
282 | const darkMapStyle = r'''
283 | [
284 | {
285 | "elementType": "geometry",
286 | "stylers": [
287 | {
288 | "color": "#212121"
289 | }
290 | ]
291 | },
292 | {
293 | "elementType": "labels.icon",
294 | "stylers": [
295 | {
296 | "visibility": "off"
297 | }
298 | ]
299 | },
300 | {
301 | "elementType": "labels.text.fill",
302 | "stylers": [
303 | {
304 | "color": "#757575"
305 | }
306 | ]
307 | },
308 | {
309 | "elementType": "labels.text.stroke",
310 | "stylers": [
311 | {
312 | "color": "#212121"
313 | }
314 | ]
315 | },
316 | {
317 | "featureType": "administrative",
318 | "elementType": "geometry",
319 | "stylers": [
320 | {
321 | "color": "#757575"
322 | }
323 | ]
324 | },
325 | {
326 | "featureType": "administrative.country",
327 | "elementType": "labels.text.fill",
328 | "stylers": [
329 | {
330 | "color": "#9e9e9e"
331 | }
332 | ]
333 | },
334 | {
335 | "featureType": "administrative.land_parcel",
336 | "stylers": [
337 | {
338 | "visibility": "off"
339 | }
340 | ]
341 | },
342 | {
343 | "featureType": "administrative.locality",
344 | "elementType": "labels.text.fill",
345 | "stylers": [
346 | {
347 | "color": "#bdbdbd"
348 | }
349 | ]
350 | },
351 | {
352 | "featureType": "poi",
353 | "elementType": "labels.text.fill",
354 | "stylers": [
355 | {
356 | "color": "#757575"
357 | }
358 | ]
359 | },
360 | {
361 | "featureType": "poi.park",
362 | "elementType": "geometry",
363 | "stylers": [
364 | {
365 | "color": "#181818"
366 | }
367 | ]
368 | },
369 | {
370 | "featureType": "poi.park",
371 | "elementType": "labels.text.fill",
372 | "stylers": [
373 | {
374 | "color": "#616161"
375 | }
376 | ]
377 | },
378 | {
379 | "featureType": "poi.park",
380 | "elementType": "labels.text.stroke",
381 | "stylers": [
382 | {
383 | "color": "#1b1b1b"
384 | }
385 | ]
386 | },
387 | {
388 | "featureType": "road",
389 | "elementType": "geometry.fill",
390 | "stylers": [
391 | {
392 | "color": "#2c2c2c"
393 | }
394 | ]
395 | },
396 | {
397 | "featureType": "road",
398 | "elementType": "labels.text.fill",
399 | "stylers": [
400 | {
401 | "color": "#8a8a8a"
402 | }
403 | ]
404 | },
405 | {
406 | "featureType": "road.arterial",
407 | "elementType": "geometry",
408 | "stylers": [
409 | {
410 | "color": "#373737"
411 | }
412 | ]
413 | },
414 | {
415 | "featureType": "road.highway",
416 | "elementType": "geometry",
417 | "stylers": [
418 | {
419 | "color": "#3c3c3c"
420 | }
421 | ]
422 | },
423 | {
424 | "featureType": "road.highway.controlled_access",
425 | "elementType": "geometry",
426 | "stylers": [
427 | {
428 | "color": "#4e4e4e"
429 | }
430 | ]
431 | },
432 | {
433 | "featureType": "road.local",
434 | "elementType": "labels.text.fill",
435 | "stylers": [
436 | {
437 | "color": "#616161"
438 | }
439 | ]
440 | },
441 | {
442 | "featureType": "transit",
443 | "elementType": "labels.text.fill",
444 | "stylers": [
445 | {
446 | "color": "#757575"
447 | }
448 | ]
449 | },
450 | {
451 | "featureType": "water",
452 | "elementType": "geometry",
453 | "stylers": [
454 | {
455 | "color": "#000000"
456 | }
457 | ]
458 | },
459 | {
460 | "featureType": "water",
461 | "elementType": "labels.text.fill",
462 | "stylers": [
463 | {
464 | "color": "#3d3d3d"
465 | }
466 | ]
467 | }
468 | ]
469 | ''';
470 |
471 | const contentString = r'''
472 |
473 |
474 |
Uluru
475 |
476 |
477 | Uluru, also referred to as Ayers Rock, is a large
478 | sandstone rock formation in the southern part of the
479 | Northern Territory, central Australia. It lies 335 km (208 mi)
480 | south west of the nearest large town, Alice Springs; 450 km
481 | (280 mi) by road. Kata Tjuta and Uluru are the two major
482 | features of the Uluru - Kata Tjuta National Park. Uluru is
483 | sacred to the Pitjantjatjara and Yankunytjatjara, the
484 | Aboriginal people of the area. It has many springs, waterholes,
485 | rock caves and ancient paintings. Uluru is listed as a World
486 | Heritage Site.
487 |
488 |
489 | Attribution: Uluru,
490 |
491 | http://en.wikipedia.org/w/index.php?title=Uluru
492 |
493 | (last visited June 22, 2009).
494 |
495 |
496 |
497 | ''';
498 |
499 | const polygon = [
500 | GeoCoord(32.707868, -117.191018),
501 | GeoCoord(32.705645, -117.191096),
502 | GeoCoord(32.697756, -117.166664),
503 | GeoCoord(32.686486, -117.163206),
504 | GeoCoord(32.675876, -117.169452),
505 | GeoCoord(32.674726, -117.165233),
506 | GeoCoord(32.679833, -117.158487),
507 | GeoCoord(32.677571, -117.153893),
508 | GeoCoord(32.671987, -117.160079),
509 | GeoCoord(32.667547, -117.160477),
510 | GeoCoord(32.654748, -117.147579),
511 | GeoCoord(32.651933, -117.150312),
512 | GeoCoord(32.649676, -117.144334),
513 | GeoCoord(32.631665, -117.138201),
514 | GeoCoord(32.632033, -117.132249),
515 | GeoCoord(32.630156, -117.137234),
516 | GeoCoord(32.628072, -117.136479),
517 | GeoCoord(32.630315, -117.131443),
518 | GeoCoord(32.625930, -117.135312),
519 | GeoCoord(32.623754, -117.131664),
520 | GeoCoord(32.627465, -117.130883),
521 | GeoCoord(32.622598, -117.128791),
522 | GeoCoord(32.622622, -117.133183),
523 | GeoCoord(32.618690, -117.133634),
524 | GeoCoord(32.618980, -117.128403),
525 | GeoCoord(32.609847, -117.132502),
526 | GeoCoord(32.604198, -117.125333),
527 | GeoCoord(32.588260, -117.122032),
528 | GeoCoord(32.591164, -117.116851),
529 | GeoCoord(32.587601, -117.105968),
530 | GeoCoord(32.583792, -117.104434),
531 | GeoCoord(32.570566, -117.101382),
532 | GeoCoord(32.569256, -117.122378),
533 | GeoCoord(32.560825, -117.122903),
534 | GeoCoord(32.557753, -117.131040),
535 | GeoCoord(32.542737, -117.124883),
536 | GeoCoord(32.534156, -117.126062),
537 | GeoCoord(32.563255, -117.134963),
538 | GeoCoord(32.584055, -117.134263),
539 | GeoCoord(32.619405, -117.140001),
540 | GeoCoord(32.655293, -117.157349),
541 | GeoCoord(32.669944, -117.169624),
542 | GeoCoord(32.682710, -117.189445),
543 | GeoCoord(32.685297, -117.208773),
544 | GeoCoord(32.679814, -117.224882),
545 | GeoCoord(32.697212, -117.227058),
546 | GeoCoord(32.707701, -117.219816),
547 | GeoCoord(32.711931, -117.214107),
548 | GeoCoord(32.715026, -117.196521),
549 | GeoCoord(32.713053, -117.189703),
550 | GeoCoord(32.707868, -117.191018),
551 | ];
552 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_google_maps_example
2 | description: Demonstrates how to use the flutter_google_maps package.
3 |
4 | environment:
5 | sdk: ">=2.6.0 <3.0.0"
6 |
7 | dependencies:
8 | flutter:
9 | sdk: flutter
10 |
11 | flutter_google_maps:
12 | path: ../
13 |
14 | flutter:
15 | uses-material-design: true
16 | assets:
17 | - assets/images/map-marker-warehouse.png
18 |
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flutter_google_maps_example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/flutter_google_maps.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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 | library flutter_google_maps;
6 |
7 | export 'src/core/map_items.dart';
8 | export 'src/core/map_operations.dart';
9 | export 'src/core/map_preferences.dart';
10 |
11 | export 'src/core/utils.dart';
12 |
13 | export 'src/core/google_map.dart';
14 | export 'src/core/google_map.state.dart'
15 | if (dart.library.html) 'src/web/google_map.state.dart'
16 | if (dart.library.io) 'src/mobile/google_map.state.dart';
17 |
18 | export 'package:google_polyline_algorithm/google_polyline_algorithm.dart';
19 | export 'package:google_directions_api/google_directions_api.dart'
20 | show GeoCoord, GeoCoordBounds;
21 |
--------------------------------------------------------------------------------
/lib/src/core/google_map.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:flutter/widgets.dart';
6 | import 'package:flutter/foundation.dart';
7 |
8 | import 'package:google_directions_api/google_directions_api.dart'
9 | show GeoCoord, DirectionsService;
10 |
11 | import 'map_items.dart';
12 | import 'map_operations.dart';
13 | import 'map_preferences.dart';
14 |
15 | import 'google_map.state.dart'
16 | if (dart.library.html) '../web/google_map.state.dart'
17 | if (dart.library.io) '../mobile/google_map.state.dart';
18 |
19 | /// This widget will try to occupy all available space
20 | class GoogleMap extends StatefulWidget {
21 | /// Creates an instance of [GoogleMap].
22 | const GoogleMap({
23 | Key key,
24 | this.minZoom,
25 | this.maxZoom,
26 | this.mapStyle,
27 | this.markers = const {},
28 | this.onTap,
29 | this.onLongPress,
30 | this.interactive = true,
31 | this.initialZoom = _zoom,
32 | this.mapType = MapType.roadmap,
33 | this.initialPosition = const GeoCoord(_defaultLat, _defaultLng),
34 | this.mobilePreferences = const MobileMapPreferences(),
35 | this.webPreferences = const WebMapPreferences(),
36 | }) : assert(mapType != null),
37 | assert(interactive != null),
38 | assert(initialPosition != null),
39 | assert(initialZoom != null),
40 | assert(mobilePreferences != null),
41 | assert(webPreferences != null),
42 | super(key: key);
43 |
44 | /// The initial position of the map's camera.
45 | final GeoCoord initialPosition;
46 |
47 | /// The initial zoom of the map's camera.
48 | final double initialZoom;
49 |
50 | /// Type of map tiles to be rendered.
51 | final MapType mapType;
52 |
53 | /// The preferred minimum zoom level or null, if unbounded from below.
54 | final double minZoom;
55 |
56 | /// The preferred maximum zoom level or null, if unbounded from above.
57 | final double maxZoom;
58 |
59 | /// Sets the styling of the base map.
60 | ///
61 | /// Set to `null` to clear any previous custom styling.
62 | ///
63 | /// If problems were detected with the [mapStyle], including un-parsable
64 | /// styling JSON, unrecognized feature type, unrecognized element type, or
65 | /// invalid styler keys: [MapStyleException] is thrown and the current
66 | /// style is left unchanged.
67 | ///
68 | /// The style string can be generated using [map style tool](https://mapstyle.withgoogle.com/).
69 | /// Also, refer [iOS](https://developers.google.com/maps/documentation/ios-sdk/style-reference)
70 | /// and [Android](https://developers.google.com/maps/documentation/android-sdk/style-reference)
71 | /// style reference for more information regarding the supported styles.
72 | final String mapStyle;
73 |
74 | /// Defines whether map is interactive or not.
75 | final bool interactive;
76 |
77 | /// Called every time a [GoogleMap] is tapped.
78 | final ValueChanged onTap;
79 |
80 | /// Markers to be placed on the map.
81 | final Set markers;
82 |
83 | /// Called every time a [GoogleMap] is long pressed.
84 | ///
85 | /// For `web` this will be called when `right mouse clicked`.
86 | final ValueChanged onLongPress;
87 |
88 | /// Set of mobile map preferences.
89 | final MobileMapPreferences mobilePreferences;
90 |
91 | /// Set of web map preferences.
92 | final WebMapPreferences webPreferences;
93 |
94 | static const _zoom = 12.0;
95 | static const _defaultLat = 34.0469058;
96 | static const _defaultLng = -118.3503948;
97 |
98 | /// Gets [MapOperations] interface via provided `key` of
99 | /// [GoogleMapStateBase] state.
100 | static MapOperations of(GlobalKey key) =>
101 | key.currentState;
102 |
103 | /// Initializer of [GoogleMap].
104 | ///
105 | /// `Required` if `Directions API` will be needed.
106 | /// For other cases, could be ignored.
107 | static void init(String apiKey) => DirectionsService.init(apiKey);
108 |
109 | @override
110 | GoogleMapState createState() => GoogleMapState();
111 | }
112 |
113 | abstract class GoogleMapStateBase extends State
114 | implements MapOperations {
115 | @protected
116 | String fixAssetPath(String icon) =>
117 | icon.endsWith('/marker_a.png') || icon.endsWith('/marker_b.png')
118 | ? 'packages/flutter_google_maps/'
119 | : '';
120 | }
121 |
--------------------------------------------------------------------------------
/lib/src/core/google_map.state.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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' show FutureOr;
6 |
7 | import 'package:flutter/widgets.dart';
8 |
9 | import 'package:google_directions_api/google_directions_api.dart'
10 | show GeoCoord, GeoCoordBounds;
11 |
12 | import 'map_items.dart';
13 | import 'google_map.dart';
14 |
15 | class GoogleMapState extends GoogleMapStateBase {
16 | @override
17 | void moveCameraBounds(
18 | GeoCoordBounds newBounds, {
19 | double padding = 0,
20 | bool animated = true,
21 | bool waitUntilReady = true,
22 | }) =>
23 | throw UnimplementedError();
24 |
25 | @override
26 | void moveCamera(
27 | GeoCoord latLng, {
28 | bool animated = true,
29 | bool waitUntilReady = true,
30 | double zoom,
31 | }) =>
32 | throw UnimplementedError();
33 |
34 | @override
35 | void zoomCamera(
36 | double zoom, {
37 | bool animated = true,
38 | bool waitUntilReady = true,
39 | }) =>
40 | throw UnimplementedError();
41 |
42 | @override
43 | FutureOr get center => throw UnimplementedError();
44 |
45 | @override
46 | void changeMapStyle(
47 | String mapStyle, {
48 | bool waitUntilReady = true,
49 | }) =>
50 | throw UnimplementedError();
51 |
52 | @override
53 | void addDirection(
54 | origin,
55 | destination, {
56 | String startLabel,
57 | String startIcon,
58 | String startInfo,
59 | String endLabel,
60 | String endIcon,
61 | String endInfo,
62 | }) =>
63 | throw UnimplementedError();
64 |
65 | @override
66 | void addMarkerRaw(
67 | GeoCoord position, {
68 | String label,
69 | String icon,
70 | String info,
71 | String infoSnippet,
72 | ValueChanged onTap,
73 | VoidCallback onInfoWindowTap,
74 | }) =>
75 | throw UnimplementedError();
76 |
77 | @override
78 | void addMarker(Marker marker) => throw UnimplementedError();
79 |
80 | @override
81 | void addPolygon(
82 | String id,
83 | Iterable points, {
84 | ValueChanged onTap,
85 | Color strokeColor = const Color(0x000000),
86 | double strokeOpacity = 0.8,
87 | double strokeWidth = 1,
88 | Color fillColor = const Color(0x000000),
89 | double fillOpacity = 0.35,
90 | }) =>
91 | throw UnimplementedError();
92 |
93 | @override
94 | void clearDirections() => throw UnimplementedError();
95 |
96 | @override
97 | void clearMarkers() => throw UnimplementedError();
98 |
99 | @override
100 | void clearPolygons() => throw UnimplementedError();
101 |
102 | @override
103 | void editPolygon(
104 | String id,
105 | Iterable points, {
106 | ValueChanged onTap,
107 | Color strokeColor = const Color(0x000000),
108 | double strokeOpacity = 0.8,
109 | double strokeWeight = 1,
110 | Color fillColor = const Color(0x000000),
111 | double fillOpacity = 0.35,
112 | }) =>
113 | throw UnimplementedError();
114 |
115 | @override
116 | void removeDirection(origin, destination) => throw UnimplementedError();
117 |
118 | @override
119 | void removeMarker(GeoCoord position) => throw UnimplementedError();
120 |
121 | @override
122 | void removePolygon(String id) => throw UnimplementedError();
123 |
124 | @override
125 | void addCircle(
126 | String id,
127 | GeoCoord center,
128 | double radius, {
129 | ValueChanged onTap,
130 | Color strokeColor = const Color(0x000000),
131 | double strokeOpacity = 0.8,
132 | double strokeWidth = 1,
133 | Color fillColor = const Color(0x000000),
134 | double fillOpacity = 0.35,
135 | }) =>
136 | throw UnimplementedError();
137 |
138 | @override
139 | void clearCircles() => throw UnimplementedError();
140 |
141 | @override
142 | void editCircle(
143 | String id,
144 | GeoCoord center,
145 | double radius, {
146 | ValueChanged onTap,
147 | Color strokeColor = const Color(0x000000),
148 | double strokeOpacity = 0.8,
149 | double strokeWidth = 1,
150 | Color fillColor = const Color(0x000000),
151 | double fillOpacity = 0.35,
152 | }) =>
153 | throw UnimplementedError();
154 |
155 | @override
156 | void removeCircle(String id) => throw UnimplementedError();
157 |
158 | @override
159 | Widget build(BuildContext context) => throw UnimplementedError();
160 | }
161 |
--------------------------------------------------------------------------------
/lib/src/core/map_items.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:flutter/foundation.dart' show ValueChanged, VoidCallback;
6 |
7 | import 'package:google_directions_api/google_directions_api.dart' show GeoCoord;
8 |
9 | /// Marks a geographical location on the map.
10 | ///
11 | /// A marker icon is drawn oriented against the device's screen rather than
12 | /// the map's surface; that is, it will not necessarily change orientation
13 | /// due to map rotations, tilting, or zooming.
14 | class Marker {
15 | /// Creates an instance of [Marker].
16 | const Marker(
17 | this.position, {
18 | this.label,
19 | this.icon,
20 | this.info,
21 | this.infoSnippet,
22 | this.onTap,
23 | this.onInfoWindowTap,
24 | });
25 |
26 | /// Geographical location on the map.
27 | final GeoCoord position;
28 |
29 | /// [label] can be set only for `web`.
30 | final String label;
31 |
32 | /// If [icon] is set, must be a path to an image from project root
33 | /// as follows: `assets/images/image.png`. Or it must be an instance
34 | /// of [ByteString].
35 | final String icon;
36 |
37 | /// If [info] is set and click event will be fired, will be shown popup with [info] within.
38 | /// * For `web` [info] could be a [String] or `HTML String`
39 | /// * For `mobile` [info] could be only a [String]
40 | final String info;
41 |
42 | /// [infoSnippet] sets snippet text for `InfoWindow`.
43 | final String infoSnippet;
44 |
45 | /// If [onTap] is not null, [info] popup will not be shown.
46 | final ValueChanged onTap;
47 |
48 | /// if [onInfoWindowTap] is set, it will be called once InfoWindow will be tapped.
49 | final VoidCallback onInfoWindowTap;
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/core/map_operations.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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' show FutureOr;
6 | import 'dart:ui' show Color, VoidCallback;
7 |
8 | import 'package:flutter/foundation.dart' show ValueChanged;
9 | import 'package:google_directions_api/google_directions_api.dart'
10 | show GeoCoord, GeoCoordBounds;
11 |
12 | import 'map_items.dart';
13 |
14 | /// Interface of setting up map operations including:
15 | ///
16 | /// * Markers
17 | /// * Directions
18 | /// * Polygons
19 | /// * Camera position
20 | /// * Map Style
21 | abstract class MapOperations
22 | implements MapMarkers, MapDirections, MapPolygons, MapCircles {
23 | /// Moves camera to the new bounds.
24 | ///
25 | /// If `padding` not set, it defaults to `0`.
26 | ///
27 | /// if `animated` not set, it defaults to `true`.
28 | ///
29 | /// For safe execution of [moveCameraBounds] some actions must be performed, and if
30 | /// `waitUntilReady` is set to `true` (by default it's true), so this method
31 | /// will await of completion of all actions, and executes [moveCameraBounds] as soon
32 | /// as it possible. This argument only affects on **mobile** devices.
33 | void moveCameraBounds(
34 | GeoCoordBounds newBounds, {
35 | double padding = 0,
36 | bool animated = true,
37 | bool waitUntilReady = true,
38 | });
39 |
40 | /// Moves camera to the new coordinates.
41 | ///
42 | /// if `animated` not set, it defaults to `true`.
43 | ///
44 | /// For safe execution of [moveCamera] some actions must be performed, and if
45 | /// `waitUntilReady` is set to `true` (by default it's true), so this method
46 | /// will await of completion of all actions, and executes [moveCamera] as soon
47 | /// as it possible. This argument only affects on **mobile** devices.
48 | void moveCamera(
49 | GeoCoord latLng, {
50 | bool animated = true,
51 | bool waitUntilReady = true,
52 | double zoom,
53 | });
54 |
55 | /// Sets new camera zoom.
56 | ///
57 | /// if `animated` not set, it defaults to `true`.
58 | /// This argument only affects on **mobile** devices.
59 | ///
60 | /// For safe execution of [zoomCamera] some actions must be performed, and if
61 | /// `waitUntilReady` is set to `true` (by default it's true), so this method
62 | /// will await of completion of all actions, and executes [zoomCamera] as soon
63 | /// as it possible. This argument only affects on **mobile** devices.
64 | void zoomCamera(
65 | double zoom, {
66 | bool animated = true,
67 | bool waitUntilReady = true,
68 | });
69 |
70 | /// Gets center coordinates of the map.
71 | FutureOr get center;
72 |
73 | /// Sets the styling of the base map.
74 | ///
75 | /// Set to `null` to clear any previous custom styling.
76 | ///
77 | /// For safe execution of [changeMapStyle] some actions must be performed, and if
78 | /// `waitUntilReady` is set to `true` (by default it's true), so this method
79 | /// will await of completion of all actions, and executes [changeMapStyle] as soon
80 | /// as it possible. This argument only affects on **mobile** devices.
81 | ///
82 | /// If problems were detected with the [mapStyle], including un-parsable
83 | /// styling JSON, unrecognized feature type, unrecognized element type, or
84 | /// invalid styler keys: [MapStyleException] is thrown and the current
85 | /// style is left unchanged.
86 | ///
87 | /// The style string can be generated using [map style tool](https://mapstyle.withgoogle.com/).
88 | /// Also, refer [iOS](https://developers.google.com/maps/documentation/ios-sdk/style-reference)
89 | /// and [Android](https://developers.google.com/maps/documentation/android-sdk/style-reference)
90 | /// style reference for more information regarding the supported styles.
91 | ///
92 | /// Please note, if widget rebuilds new map style will be ommited due to map style
93 | /// provided from the `widget`. So, if map will be scrolled out, make sure that
94 | /// new map style will be set to widgets [GoogleMap.mapStyle].
95 | void changeMapStyle(
96 | String mapStyle, {
97 | bool waitUntilReady = true,
98 | });
99 | }
100 |
101 | /// Interface of setting up markers
102 | abstract class MapMarkers {
103 | /// Adds a marker to the map by given [position].
104 | ///
105 | /// [label] can be set only for `web`.
106 | ///
107 | /// If [icon] is set, must be a path to an image from project root
108 | /// as follows: `assets/images/image.png`. Or it must be an instance
109 | /// of [ByteString].
110 | ///
111 | /// If [info] is set and click event will be fired, will be shown popup with [info] within.
112 | /// * For `web` [info] could be a [String] or `HTML String`
113 | /// * For `mobile` [info] could be only a [String]
114 | ///
115 | /// [infoSnippet] sets snippet text for `InfoWindow`.
116 | ///
117 | /// If [onTap] is not null, [info] popup will not be shown.
118 | ///
119 | /// if [onInfoWindowTap] is set, it will be called once InfoWindow will be tapped.
120 | ///
121 | /// If marker with same [position] have been already added, addition of a new marker will be ignored.
122 | void addMarkerRaw(
123 | GeoCoord position, {
124 | String label,
125 | String icon,
126 | String info,
127 | String infoSnippet,
128 | ValueChanged onTap,
129 | VoidCallback onInfoWindowTap,
130 | });
131 |
132 | /// Adds a marker to the map by given [position].
133 | ///
134 | /// If marker with same [position] have been already added, addition of a new marker will be ignored.
135 | void addMarker(Marker marker);
136 |
137 | /// Removes a marker from the map by given [position].
138 | void removeMarker(GeoCoord position);
139 |
140 | /// Removes all markers from the map.
141 | void clearMarkers();
142 | }
143 |
144 | /// Interface of setting up directions
145 | abstract class MapDirections {
146 | /// Adds a direction to the map by given [origin] and [destination] coordinates.
147 | ///
148 | /// [origin] and [destination] are `dynamic` due to following variations:
149 | /// * [LatLng], better use [Point], it will be converted into [LatLng]
150 | /// * [Place]
151 | /// * [String]
152 | ///
153 | /// If direction with same [origin] and [destination] have been already added,
154 | /// addition of a new polygon will be ignored.
155 | void addDirection(
156 | dynamic origin,
157 | dynamic destination, {
158 | String startLabel,
159 | String startIcon,
160 | String startInfo,
161 | String endLabel,
162 | String endIcon,
163 | String endInfo,
164 | });
165 |
166 | /// Removes a direction from the map by given [origin] and [destination] coordinates.
167 | ///
168 | /// [origin] and [destination] are `dynamic` due to following variations:
169 | /// * [LatLng], better use [GeoCoord], it will be converted into [LatLng]
170 | /// * [Place]
171 | /// * [String]
172 | void removeDirection(dynamic origin, dynamic destination);
173 |
174 | /// Removes all directions from the map.
175 | void clearDirections();
176 | }
177 |
178 | /// Interface of setting up polygons
179 | abstract class MapPolygons {
180 | /// Adds a polygon to the map by given [id] and [points].
181 | ///
182 | /// Where [id] must be **unique**.
183 | ///
184 | /// If [id] have been already added, addition of a new polygon will be ignored.
185 | void addPolygon(
186 | String id,
187 | Iterable points, {
188 | ValueChanged onTap,
189 | Color strokeColor = const Color(0x000000),
190 | double strokeOpacity = 0.8,
191 | double strokeWidth = 1,
192 | Color fillColor = const Color(0x000000),
193 | double fillOpacity = 0.35,
194 | });
195 |
196 | /// Removes and then adds a polygon to the map by given [id] and [points].
197 | ///
198 | /// Where [id] must be **unique**.
199 | ///
200 | /// If [id] have been already added, addition of a new polygon will be ignored.
201 | void editPolygon(
202 | String id,
203 | Iterable points, {
204 | ValueChanged onTap,
205 | Color strokeColor = const Color(0x000000),
206 | double strokeOpacity = 0.8,
207 | double strokeWeight = 1,
208 | Color fillColor = const Color(0x000000),
209 | double fillOpacity = 0.35,
210 | });
211 |
212 | /// Removes a polygon from the map by given [id].
213 | void removePolygon(String id);
214 |
215 | /// Removes all polygones from the map.
216 | void clearPolygons();
217 | }
218 |
219 | /// Interface of setting up circles
220 | abstract class MapCircles {
221 | /// Adds a circle to the map by given [id], [center] and [radius].
222 | ///
223 | /// Where [id] must be **unique**.
224 | ///
225 | /// If [id] have been already added, addition of a new circle will be ignored.
226 | void addCircle(
227 | String id,
228 | GeoCoord center,
229 | double radius, {
230 | ValueChanged onTap,
231 | Color strokeColor = const Color(0x000000),
232 | double strokeOpacity = 0.8,
233 | double strokeWidth = 1,
234 | Color fillColor = const Color(0x000000),
235 | double fillOpacity = 0.35,
236 | });
237 |
238 | /// Removes and then adds a circles to the map by given [id], [center] and [radius].
239 | ///
240 | /// Where [id] must be **unique**.
241 | ///
242 | /// If [id] have been already added, addition of a new circle will be ignored.
243 | void editCircle(
244 | String id,
245 | GeoCoord center,
246 | double radius, {
247 | ValueChanged onTap,
248 | Color strokeColor = const Color(0x000000),
249 | double strokeOpacity = 0.8,
250 | double strokeWidth = 1,
251 | Color fillColor = const Color(0x000000),
252 | double fillOpacity = 0.35,
253 | });
254 |
255 | /// Removes a circle from the map by given [id].
256 | void removeCircle(String id);
257 |
258 | /// Removes all circles from the map.
259 | void clearCircles();
260 | }
261 |
--------------------------------------------------------------------------------
/lib/src/core/map_preferences.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:flutter/widgets.dart';
6 |
7 | /// Type of map tiles to display.
8 | enum MapType {
9 | /// Do not display map tiles.
10 | none,
11 |
12 | /// Normal tiles (traffic and labels, subtle terrain information).
13 | roadmap,
14 |
15 | /// Satellite imaging tiles (aerial photos)
16 | satellite,
17 |
18 | /// Terrain tiles (indicates type and height of terrain)
19 | terrain,
20 |
21 | /// Hybrid tiles (satellite images with some labels/overlays)
22 | hybrid,
23 | }
24 |
25 | /// Set of mobile map preferences
26 | class MobileMapPreferences {
27 | /// Creates an instance of [MobileMapPreferences].
28 | const MobileMapPreferences({
29 | this.rotateGesturesEnabled = true,
30 | this.scrollGesturesEnabled = true,
31 | this.zoomGesturesEnabled = true,
32 | this.tiltGesturesEnabled = true,
33 | this.compassEnabled = true,
34 | this.mapToolbarEnabled = true,
35 | this.myLocationEnabled = false,
36 | this.myLocationButtonEnabled = true,
37 | this.zoomControlsEnabled = true,
38 | this.indoorViewEnabled = false,
39 | this.trafficEnabled = false,
40 | this.buildingsEnabled = true,
41 | this.padding = const EdgeInsets.all(0),
42 | });
43 |
44 | /// True if the map should show a compass when rotated.
45 | final bool compassEnabled;
46 |
47 | /// True if the map should show a toolbar when you interact with the map. Android only.
48 | final bool mapToolbarEnabled;
49 |
50 | /// True if a "My Location" layer should be shown on the map.
51 | ///
52 | /// This layer includes a location indicator at the current device location,
53 | /// as well as a My Location button.
54 | /// * The indicator is a small blue dot if the device is stationary, or a
55 | /// chevron if the device is moving.
56 | /// * The My Location button animates to focus on the user's current location
57 | /// if the user's location is currently known.
58 | ///
59 | /// Enabling this feature requires adding location permissions to both native
60 | /// platforms of your app.
61 | /// * On Android add either
62 | /// ``
63 | /// or ``
64 | /// to your `AndroidManifest.xml` file. `ACCESS_COARSE_LOCATION` returns a
65 | /// location with an accuracy approximately equivalent to a city block, while
66 | /// `ACCESS_FINE_LOCATION` returns as precise a location as possible, although
67 | /// it consumes more battery power. You will also need to request these
68 | /// permissions during run-time. If they are not granted, the My Location
69 | /// feature will fail silently.
70 | /// * On iOS add a `NSLocationWhenInUseUsageDescription` key to your
71 | /// `Info.plist` file. This will automatically prompt the user for permissions
72 | /// when the map tries to turn on the My Location layer.
73 | final bool myLocationEnabled;
74 |
75 | /// Enables or disables the my-location button.
76 | ///
77 | /// The my-location button causes the camera to move such that the user's
78 | /// location is in the center of the map. If the button is enabled, it is
79 | /// only shown when the my-location layer is enabled.
80 | ///
81 | /// By default, the my-location button is enabled (and hence shown when the
82 | /// my-location layer is enabled).
83 | ///
84 | /// See also:
85 | /// * [myLocationEnabled] parameter.
86 | final bool myLocationButtonEnabled;
87 |
88 | /// Enables or disables the zoom in / zoom out (+/-) buttons.
89 | /// By default, they are enabled.
90 | final bool zoomControlsEnabled;
91 |
92 | /// Enables or disables the indoor view from the map.
93 | final bool indoorViewEnabled;
94 |
95 | /// Enables or disables the traffic layer of the map.
96 | final bool trafficEnabled;
97 |
98 | /// Enables or disables showing 3D buildings where available.
99 | final bool buildingsEnabled;
100 |
101 | /// Padding to be set on map. See https://developers.google.com/maps/documentation/android-sdk/map#map_padding for more details.
102 | final EdgeInsets padding;
103 |
104 | /// True if the map view should respond to rotate gestures.
105 | final bool rotateGesturesEnabled;
106 |
107 | /// True if the map view should respond to scroll gestures.
108 | final bool scrollGesturesEnabled;
109 |
110 | /// True if the map view should respond to zoom gestures.
111 | final bool zoomGesturesEnabled;
112 |
113 | /// True if the map view should respond to tilt gestures.
114 | final bool tiltGesturesEnabled;
115 | }
116 |
117 | /// Set of web map preferences
118 | class WebMapPreferences {
119 | /// Creates an instance of [WebMapPreferences].
120 | const WebMapPreferences({
121 | this.streetViewControl = false,
122 | this.fullscreenControl = false,
123 | this.mapTypeControl = false,
124 | this.panControl = false,
125 | this.overviewMapControl = false,
126 | this.rotateControl = false,
127 | this.scaleControl = false,
128 | this.zoomControl = false,
129 | this.dragGestures = true,
130 | this.scrollwheel = true,
131 | });
132 |
133 | /// Predefined support for fullscreen map.
134 | ///
135 | /// Scrollwheel zomming and dragging gestures are enabled.
136 | const WebMapPreferences.fullscreen({
137 | this.streetViewControl = false,
138 | this.fullscreenControl = false,
139 | this.mapTypeControl = false,
140 | this.panControl = false,
141 | this.overviewMapControl = false,
142 | this.rotateControl = false,
143 | this.scaleControl = false,
144 | this.zoomControl = false,
145 | }) : dragGestures = true,
146 | scrollwheel = true;
147 |
148 | /// Predefined support for map that will be scrolled.
149 | ///
150 | /// Scrollwheel zomming and dragging gestures are disabled.
151 | const WebMapPreferences.scrollable({
152 | this.streetViewControl = false,
153 | this.fullscreenControl = false,
154 | this.mapTypeControl = false,
155 | this.panControl = false,
156 | this.overviewMapControl = false,
157 | this.rotateControl = false,
158 | this.scaleControl = false,
159 | this.zoomControl = false,
160 | }) : dragGestures = false,
161 | scrollwheel = false;
162 |
163 | /// Enables or disables streetViewControl.
164 | final bool streetViewControl;
165 |
166 | /// Enables or disables fullscreenControl.
167 | final bool fullscreenControl;
168 |
169 | /// Enables or disables mapTypeControl.
170 | final bool mapTypeControl;
171 |
172 | /// Enables or disables scrollwheel.
173 | final bool scrollwheel;
174 |
175 | /// Enables or disables panControl.
176 | final bool panControl;
177 |
178 | /// Enables or disables overviewMapControl.
179 | final bool overviewMapControl;
180 |
181 | /// Enables or disables rotateControl.
182 | final bool rotateControl;
183 |
184 | /// Enables or disables scaleControl.
185 | final bool scaleControl;
186 |
187 | /// Enables or disables zoomControl.
188 | final bool zoomControl;
189 |
190 | /// Enables or disables flutter drag gestures.
191 | final bool dragGestures;
192 | }
193 |
--------------------------------------------------------------------------------
/lib/src/core/utils.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:convert' show base64;
6 | import 'dart:typed_data' show Uint8List;
7 |
8 | /// Exception when a map style is invalid or was unable to be set.
9 | ///
10 | /// See also: `mapStyle` on [GoogleMap] and `changeMapStyle` on
11 | /// [MapOperations] for why this exception might be thrown.
12 | class MapStyleException implements Exception {
13 | /// Default constructor for [MapStyleException].
14 | const MapStyleException(this.cause);
15 |
16 | /// The reason `GoogleMap.mapStyle` or `MapOperations.changeMapStyle`
17 | /// would throw this exception.
18 | final String cause;
19 | }
20 |
21 | /// Wrapper for byte array image representation.
22 | class ByteString {
23 | /// Constructor an instance of [ByteString].
24 | const ByteString(this.byteData);
25 |
26 | /// Byte representation of an image.
27 | final Uint8List byteData;
28 |
29 | static const String _prefix = 'bytes://';
30 |
31 | /// Checks whether provided [String] is a string representation of byte array.
32 | static bool isByteString(String byteString) => byteString.startsWith(_prefix);
33 |
34 | /// Converts [String] to byte array.
35 | static Uint8List fromString(String byteString) =>
36 | base64.decode(byteString.replaceFirst(_prefix, ''));
37 |
38 | @override
39 | String toString() => _prefix + base64.encode(byteData);
40 | }
41 |
--------------------------------------------------------------------------------
/lib/src/mobile/google_map.state.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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 | import 'package:flinq/flinq.dart';
10 | import 'package:google_maps_flutter/google_maps_flutter.dart';
11 | import 'package:google_directions_api/google_directions_api.dart';
12 |
13 | import 'utils.dart';
14 | import '../core/utils.dart' as utils;
15 | import '../core/google_map.dart' as gmap;
16 | import '../core/map_items.dart' as items;
17 |
18 | class GoogleMapState extends gmap.GoogleMapStateBase {
19 | final directionsService = DirectionsService();
20 |
21 | final _markers = {};
22 | final _polygons = {};
23 | final _circles = {};
24 | final _polylines = {};
25 | final _directionMarkerCoords = {};
26 |
27 | final _waitUntilReadyCompleter = Completer();
28 |
29 | GoogleMapController _controller;
30 |
31 | void _setState(VoidCallback fn) {
32 | if (mounted) {
33 | setState(fn);
34 | } else {
35 | fn();
36 | }
37 | }
38 |
39 | FutureOr _getBmpDesc(String image) async {
40 | if (image == null) return BitmapDescriptor.defaultMarker;
41 |
42 | if (utils.ByteString.isByteString(image)) {
43 | return BitmapDescriptor.fromBytes(utils.ByteString.fromString(image));
44 | }
45 |
46 | return await BitmapDescriptor.fromAssetImage(
47 | createLocalImageConfiguration(context),
48 | image,
49 | );
50 | }
51 |
52 | @override
53 | void moveCameraBounds(
54 | GeoCoordBounds newBounds, {
55 | double padding = 0,
56 | bool animated = true,
57 | bool waitUntilReady = true,
58 | }) async {
59 | assert(() {
60 | if (newBounds == null) {
61 | throw ArgumentError.notNull('newBounds');
62 | }
63 |
64 | return true;
65 | }());
66 |
67 | if (waitUntilReady == true) {
68 | await _waitUntilReadyCompleter.future;
69 | }
70 |
71 | if (animated == true) {
72 | await _controller?.animateCamera(CameraUpdate.newLatLngBounds(
73 | newBounds.toLatLngBounds(),
74 | padding ?? 0,
75 | ));
76 | } else {
77 | await _controller?.moveCamera(CameraUpdate.newLatLngBounds(
78 | newBounds.toLatLngBounds(),
79 | padding ?? 0,
80 | ));
81 | }
82 | }
83 |
84 | @override
85 | void moveCamera(
86 | GeoCoord latLng, {
87 | bool animated = true,
88 | bool waitUntilReady = true,
89 | double zoom,
90 | }) async {
91 | assert(() {
92 | if (latLng == null) {
93 | throw ArgumentError.notNull('latLng');
94 | }
95 |
96 | return true;
97 | }());
98 |
99 | if (waitUntilReady == true) {
100 | await _waitUntilReadyCompleter.future;
101 | }
102 |
103 | if (animated == true) {
104 | await _controller?.animateCamera(CameraUpdate.newLatLngZoom(
105 | latLng.toLatLng(),
106 | zoom ?? await _controller?.getZoomLevel(),
107 | ));
108 | } else {
109 | await _controller?.moveCamera(CameraUpdate.newLatLngZoom(
110 | latLng.toLatLng(),
111 | zoom ?? await _controller?.getZoomLevel(),
112 | ));
113 | }
114 | }
115 |
116 | @override
117 | void zoomCamera(
118 | double zoom, {
119 | bool animated = true,
120 | bool waitUntilReady = true,
121 | }) async {
122 | assert(() {
123 | if (zoom == null) {
124 | throw ArgumentError.notNull('zoom');
125 | }
126 |
127 | return true;
128 | }());
129 |
130 | if (waitUntilReady == true) {
131 | await _waitUntilReadyCompleter.future;
132 | }
133 |
134 | if (animated == true) {
135 | await _controller?.animateCamera(CameraUpdate.zoomTo(zoom));
136 | } else {
137 | await _controller?.moveCamera(CameraUpdate.zoomTo(zoom));
138 | }
139 | }
140 |
141 | FutureOr get center async =>
142 | (await _controller?.getVisibleRegion())?.toGeoCoordBounds()?.center;
143 |
144 | @override
145 | void changeMapStyle(
146 | String mapStyle, {
147 | bool waitUntilReady = true,
148 | }) async {
149 | if (waitUntilReady == true) {
150 | await _waitUntilReadyCompleter.future;
151 | }
152 | try {
153 | await _controller?.setMapStyle(mapStyle);
154 | } on MapStyleException catch (e) {
155 | throw utils.MapStyleException(e.cause);
156 | }
157 | }
158 |
159 | @override
160 | void addMarkerRaw(
161 | GeoCoord position, {
162 | String label,
163 | String icon,
164 | String info,
165 | String infoSnippet,
166 | ValueChanged onTap,
167 | VoidCallback onInfoWindowTap,
168 | }) async {
169 | assert(() {
170 | if (position == null) {
171 | throw ArgumentError.notNull('position');
172 | }
173 |
174 | if (position.latitude == null || position.longitude == null) {
175 | throw ArgumentError.notNull('position.latitude && position.longitude');
176 | }
177 |
178 | return true;
179 | }());
180 |
181 | final key = position.toString();
182 |
183 | if (_markers.containsKey(key)) return;
184 |
185 | final markerId = MarkerId(key);
186 | final marker = Marker(
187 | markerId: markerId,
188 | onTap: onTap != null ? () => onTap(key) : null,
189 | consumeTapEvents: onTap != null,
190 | position: position.toLatLng(),
191 | icon: icon == null
192 | ? BitmapDescriptor.defaultMarker
193 | : await _getBmpDesc('${fixAssetPath(icon)}$icon'),
194 | infoWindow: info != null
195 | ? InfoWindow(
196 | title: info,
197 | snippet: infoSnippet,
198 | onTap: onInfoWindowTap,
199 | )
200 | : null,
201 | );
202 |
203 | _setState(() => _markers[key] = marker);
204 | }
205 |
206 | @override
207 | void addMarker(items.Marker marker) => addMarkerRaw(
208 | marker.position,
209 | label: marker.label,
210 | icon: marker.icon,
211 | info: marker.info,
212 | infoSnippet: marker.infoSnippet,
213 | onTap: marker.onTap,
214 | onInfoWindowTap: marker.onInfoWindowTap,
215 | );
216 |
217 | @override
218 | void removeMarker(GeoCoord position) {
219 | assert(() {
220 | if (position == null) {
221 | throw ArgumentError.notNull('position');
222 | }
223 |
224 | if (position.latitude == null || position.longitude == null) {
225 | throw ArgumentError.notNull('position.latitude && position.longitude');
226 | }
227 |
228 | return true;
229 | }());
230 |
231 | final key = position.toString();
232 |
233 | if (!_markers.containsKey(key)) return;
234 |
235 | _setState(() => _markers.remove(key));
236 | }
237 |
238 | @override
239 | void clearMarkers() => _setState(() => _markers.clear());
240 |
241 | @override
242 | void addDirection(
243 | dynamic origin,
244 | dynamic destination, {
245 | String startLabel,
246 | String startIcon,
247 | String startInfo,
248 | String endLabel,
249 | String endIcon,
250 | String endInfo,
251 | }) {
252 | assert(() {
253 | if (origin == null) {
254 | throw ArgumentError.notNull('origin');
255 | }
256 |
257 | if (destination == null) {
258 | throw ArgumentError.notNull('destination');
259 | }
260 |
261 | return true;
262 | }());
263 |
264 | final request = DirectionsRequest(
265 | origin: origin is GeoCoord
266 | ? LatLng(origin.latitude, origin.longitude)
267 | : origin,
268 | destination:
269 | destination is GeoCoord ? destination.toLatLng() : destination,
270 | travelMode: TravelMode.driving,
271 | );
272 | directionsService.route(
273 | request,
274 | (response, status) {
275 | if (status == DirectionsStatus.ok) {
276 | final key = '${origin}_$destination';
277 |
278 | if (_polylines.containsKey(key)) return;
279 |
280 | moveCameraBounds(
281 | response?.routes?.firstOrNull?.bounds,
282 | padding: 80,
283 | );
284 |
285 | final leg = response?.routes?.firstOrNull?.legs?.firstOrNull;
286 |
287 | final startLatLng = leg?.startLocation;
288 | if (startLatLng != null) {
289 | _directionMarkerCoords[startLatLng] = origin;
290 | if (startIcon != null || startInfo != null || startLabel != null) {
291 | addMarkerRaw(
292 | startLatLng,
293 | icon: startIcon ?? 'assets/images/marker_a.png',
294 | info: startInfo ?? leg.startAddress,
295 | label: startLabel,
296 | );
297 | } else {
298 | addMarkerRaw(
299 | startLatLng,
300 | icon: 'assets/images/marker_a.png',
301 | info: leg.startAddress,
302 | );
303 | }
304 | }
305 |
306 | final endLatLng = leg?.endLocation;
307 | if (endLatLng != null) {
308 | _directionMarkerCoords[endLatLng] = destination;
309 | if (endIcon != null || endInfo != null || endLabel != null) {
310 | addMarkerRaw(
311 | endLatLng,
312 | icon: endIcon ?? 'assets/images/marker_b.png',
313 | info: endInfo ?? leg.endAddress,
314 | label: endLabel,
315 | );
316 | } else {
317 | addMarkerRaw(
318 | endLatLng,
319 | icon: 'assets/images/marker_b.png',
320 | info: leg.endAddress,
321 | );
322 | }
323 | }
324 |
325 | final polylineId = PolylineId(key);
326 | final polyline = Polyline(
327 | polylineId: polylineId,
328 | points: response?.routes?.firstOrNull?.overviewPath
329 | ?.mapList((_) => _.toLatLng()) ??
330 | [startLatLng?.toLatLng(), endLatLng?.toLatLng()],
331 | color: const Color(0xcc2196F3),
332 | startCap: Cap.roundCap,
333 | endCap: Cap.roundCap,
334 | width: 8,
335 | );
336 |
337 | _setState(() => _polylines[key] = polyline);
338 | }
339 | },
340 | );
341 | }
342 |
343 | @override
344 | void removeDirection(dynamic origin, dynamic destination) {
345 | assert(() {
346 | if (origin == null) {
347 | throw ArgumentError.notNull('origin');
348 | }
349 |
350 | if (destination == null) {
351 | throw ArgumentError.notNull('destination');
352 | }
353 |
354 | return true;
355 | }());
356 |
357 | var value = _polylines.remove('${origin}_$destination');
358 | final start = value?.points?.firstOrNull?.toGeoCoord();
359 | if (start != null) {
360 | removeMarker(start);
361 | _directionMarkerCoords.remove(start);
362 | }
363 | final end = value?.points?.lastOrNull?.toGeoCoord();
364 | if (end != null) {
365 | removeMarker(end);
366 | _directionMarkerCoords.remove(end);
367 | }
368 | value = null;
369 | }
370 |
371 | @override
372 | void clearDirections() {
373 | for (var polyline in _polylines.values) {
374 | final start = polyline?.points?.firstOrNull?.toGeoCoord();
375 | if (start != null) {
376 | removeMarker(start);
377 | _directionMarkerCoords.remove(start);
378 | }
379 | final end = polyline?.points?.lastOrNull?.toGeoCoord();
380 | if (end != null) {
381 | removeMarker(end);
382 | _directionMarkerCoords.remove(end);
383 | }
384 | polyline = null;
385 | }
386 | _polylines.clear();
387 | }
388 |
389 | @override
390 | void addPolygon(
391 | String id,
392 | Iterable points, {
393 | ValueChanged onTap,
394 | Color strokeColor = const Color(0x000000),
395 | double strokeOpacity = 0.8,
396 | double strokeWidth = 1,
397 | Color fillColor = const Color(0x000000),
398 | double fillOpacity = 0.35,
399 | }) {
400 | assert(() {
401 | if (id == null) {
402 | throw ArgumentError.notNull('id');
403 | }
404 |
405 | if (points == null) {
406 | throw ArgumentError.notNull('position');
407 | }
408 |
409 | if (points.isEmpty) {
410 | throw ArgumentError.value([], 'points');
411 | }
412 |
413 | if (points.length < 3) {
414 | throw ArgumentError('Polygon must have at least 3 coordinates');
415 | }
416 |
417 | return true;
418 | }());
419 |
420 | _polygons.putIfAbsent(
421 | id,
422 | () => Polygon(
423 | polygonId: PolygonId(id),
424 | points: points.mapList((_) => _.toLatLng()),
425 | consumeTapEvents: onTap != null,
426 | onTap: onTap != null ? () => onTap(id) : null,
427 | strokeWidth: strokeWidth?.toInt() ?? 1,
428 | strokeColor: (strokeColor ?? const Color(0x000000))
429 | .withOpacity(strokeOpacity ?? 0.8),
430 | fillColor: (fillColor ?? const Color(0x000000))
431 | .withOpacity(fillOpacity ?? 0.35),
432 | ),
433 | );
434 | }
435 |
436 | @override
437 | void editPolygon(
438 | String id,
439 | Iterable points, {
440 | ValueChanged onTap,
441 | Color strokeColor = const Color(0x000000),
442 | double strokeOpacity = 0.8,
443 | double strokeWeight = 1,
444 | Color fillColor = const Color(0x000000),
445 | double fillOpacity = 0.35,
446 | }) {
447 | removePolygon(id);
448 | addPolygon(
449 | id,
450 | points,
451 | onTap: onTap,
452 | strokeColor: strokeColor,
453 | strokeOpacity: strokeOpacity,
454 | strokeWidth: strokeWeight,
455 | fillColor: fillColor,
456 | fillOpacity: fillOpacity,
457 | );
458 | }
459 |
460 | @override
461 | void removePolygon(String id) {
462 | assert(() {
463 | if (id == null) {
464 | throw ArgumentError.notNull('id');
465 | }
466 |
467 | return true;
468 | }());
469 |
470 | if (!_polygons.containsKey(id)) return;
471 |
472 | _setState(() => _polygons.remove(id));
473 | }
474 |
475 | @override
476 | void clearPolygons() => _setState(() => _polygons.clear());
477 |
478 | @override
479 | void addCircle(
480 | String id,
481 | GeoCoord center,
482 | double radius, {
483 | ValueChanged onTap,
484 | Color strokeColor = const Color(0x000000),
485 | double strokeOpacity = 0.8,
486 | double strokeWidth = 1,
487 | Color fillColor = const Color(0x000000),
488 | double fillOpacity = 0.35,
489 | }) {
490 | assert(() {
491 | if (id == null) {
492 | throw ArgumentError.notNull('id');
493 | }
494 |
495 | if (center == null) {
496 | throw ArgumentError.notNull('center');
497 | }
498 |
499 | if (radius == null) {
500 | throw ArgumentError.notNull('radius');
501 | }
502 |
503 | return true;
504 | }());
505 |
506 | setState(() {
507 | _circles.putIfAbsent(
508 | id,
509 | () => Circle(
510 | circleId: CircleId(id),
511 | center: center.toLatLng(),
512 | radius: radius,
513 | onTap: () => onTap(id),
514 | strokeColor: strokeColor.withOpacity(strokeOpacity),
515 | strokeWidth: strokeWidth.toInt(),
516 | fillColor: fillColor.withOpacity(fillOpacity),
517 | ),
518 | );
519 | });
520 | }
521 |
522 | @override
523 | void clearCircles() => setState(() => _circles.clear());
524 |
525 | @override
526 | void editCircle(
527 | String id,
528 | GeoCoord center,
529 | double radius, {
530 | ValueChanged onTap,
531 | Color strokeColor = const Color(0x000000),
532 | double strokeOpacity = 0.8,
533 | double strokeWidth = 1,
534 | Color fillColor = const Color(0x000000),
535 | double fillOpacity = 0.35,
536 | }) {
537 | removeCircle(id);
538 | addCircle(
539 | id,
540 | center,
541 | radius,
542 | onTap: onTap,
543 | strokeColor: strokeColor,
544 | strokeOpacity: strokeOpacity,
545 | strokeWidth: strokeWidth,
546 | fillColor: fillColor,
547 | fillOpacity: fillOpacity,
548 | );
549 | }
550 |
551 | @override
552 | void removeCircle(String id) {
553 | assert(() {
554 | if (id == null) {
555 | throw ArgumentError.notNull('id');
556 | }
557 |
558 | return true;
559 | }());
560 |
561 | if (!_circles.containsKey(id)) return;
562 |
563 | _setState(() => _circles.remove(id));
564 | }
565 |
566 | @override
567 | void initState() {
568 | super.initState();
569 | if (widget.markers != null) {
570 | for (var marker in widget.markers) {
571 | addMarker(marker);
572 | }
573 | }
574 | }
575 |
576 | @override
577 | Widget build(BuildContext context) => LayoutBuilder(
578 | builder: (context, constraints) => IgnorePointer(
579 | ignoring: !widget.interactive,
580 | child: Container(
581 | constraints: BoxConstraints(maxHeight: constraints.maxHeight),
582 | child: GoogleMap(
583 | markers: Set.of(_markers.values),
584 | polygons: Set.of(_polygons.values),
585 | polylines: Set.of(_polylines.values),
586 | circles: Set.of(_circles.values),
587 | mapType: MapType.values[widget.mapType.index],
588 | minMaxZoomPreference:
589 | MinMaxZoomPreference(widget.minZoom, widget.minZoom),
590 | initialCameraPosition: CameraPosition(
591 | target: widget.initialPosition.toLatLng(),
592 | zoom: widget.initialZoom,
593 | ),
594 | onTap: (coords) => widget.onTap?.call(coords?.toGeoCoord()),
595 | onLongPress: (coords) =>
596 | widget.onLongPress?.call(coords?.toGeoCoord()),
597 | onMapCreated: (GoogleMapController controller) {
598 | _controller = controller;
599 | _controller.setMapStyle(widget.mapStyle);
600 |
601 | _waitUntilReadyCompleter.complete();
602 | },
603 | padding: widget.mobilePreferences.padding,
604 | compassEnabled: widget.mobilePreferences.compassEnabled,
605 | trafficEnabled: widget.mobilePreferences.trafficEnabled,
606 | buildingsEnabled: widget.mobilePreferences.buildingsEnabled,
607 | indoorViewEnabled: widget.mobilePreferences.indoorViewEnabled,
608 | mapToolbarEnabled: widget.mobilePreferences.mapToolbarEnabled,
609 | myLocationEnabled: widget.mobilePreferences.myLocationEnabled,
610 | myLocationButtonEnabled:
611 | widget.mobilePreferences.myLocationButtonEnabled,
612 | tiltGesturesEnabled: widget.mobilePreferences.tiltGesturesEnabled,
613 | zoomGesturesEnabled: widget.mobilePreferences.zoomGesturesEnabled,
614 | rotateGesturesEnabled:
615 | widget.mobilePreferences.rotateGesturesEnabled,
616 | zoomControlsEnabled: widget.mobilePreferences.zoomControlsEnabled,
617 | scrollGesturesEnabled:
618 | widget.mobilePreferences.scrollGesturesEnabled,
619 | ),
620 | ),
621 | ),
622 | );
623 |
624 | @override
625 | void dispose() {
626 | super.dispose();
627 |
628 | _markers.clear();
629 | _polygons.clear();
630 | _polylines.clear();
631 | _directionMarkerCoords.clear();
632 |
633 | _controller = null;
634 | }
635 | }
636 |
--------------------------------------------------------------------------------
/lib/src/mobile/utils.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:google_maps_flutter/google_maps_flutter.dart';
6 | import 'package:google_directions_api/google_directions_api.dart'
7 | show GeoCoord, GeoCoordBounds;
8 |
9 | extension MobileLatLngExtensions on LatLng {
10 | GeoCoord toGeoCoord() => GeoCoord(this.latitude, this.longitude);
11 | }
12 |
13 | extension MobileGeoCoordExtensions on GeoCoord {
14 | LatLng toLatLng() => LatLng(this.latitude, this.longitude);
15 | }
16 |
17 | extension MobileGeoCoordBoundsExtensions on GeoCoordBounds {
18 | LatLngBounds toLatLngBounds() => LatLngBounds(
19 | northeast: this.northeast.toLatLng(),
20 | southwest: this.southwest.toLatLng(),
21 | );
22 |
23 | GeoCoord get center => GeoCoord(
24 | (this.northeast.latitude + this.southwest.latitude) / 2,
25 | (this.northeast.longitude + this.southwest.longitude) / 2,
26 | );
27 | }
28 |
29 | extension MobileLatLngBoundsExtensions on LatLngBounds {
30 | GeoCoordBounds toGeoCoordBounds() => GeoCoordBounds(
31 | northeast: this.northeast.toGeoCoord(),
32 | southwest: this.southwest.toGeoCoord(),
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/lib/src/web/google_map.state.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:html';
6 | import 'dart:async';
7 | import 'dart:ui' as ui;
8 |
9 | import 'package:flutter/widgets.dart';
10 | import 'package:flutter/scheduler.dart' show SchedulerBinding;
11 |
12 | import 'package:uuid/uuid.dart';
13 | import 'package:flinq/flinq.dart';
14 | import 'package:google_maps/google_maps.dart';
15 | import 'package:google_directions_api/google_directions_api.dart'
16 | show GeoCoord, GeoCoordBounds;
17 |
18 | import 'utils.dart';
19 | import '../core/google_map.dart';
20 | import '../core/utils.dart' as utils;
21 | import '../core/map_items.dart' as items;
22 |
23 | class GoogleMapState extends GoogleMapStateBase {
24 | final htmlId = Uuid().v1();
25 | final directionsService = DirectionsService();
26 |
27 | final _markers = {};
28 | final _infoState = {};
29 | final _infos = {};
30 | final _polygons = {};
31 | final _circles = {};
32 | final _subscriptions = [];
33 | final _directions = {};
34 |
35 | GMap _map;
36 | MapOptions _mapOptions;
37 |
38 | String _getImage(String image) {
39 | if (image == null) return null;
40 |
41 | if (utils.ByteString.isByteString(image)) {
42 | final blob = Blob([utils.ByteString.fromString(image)], 'image/png');
43 | return Url.createObjectUrlFromBlob(blob);
44 | }
45 |
46 | return '${fixAssetPath(image)}assets/$image';
47 | }
48 |
49 | @override
50 | void moveCameraBounds(
51 | GeoCoordBounds newBounds, {
52 | double padding = 0,
53 | bool animated = true,
54 | bool waitUntilReady = true,
55 | }) {
56 | assert(() {
57 | if (newBounds == null) {
58 | throw ArgumentError.notNull('newBounds');
59 | }
60 |
61 | return true;
62 | }());
63 |
64 | _map.center = newBounds.center.toLatLng();
65 |
66 | final zoom = _map.zoom;
67 | if (animated == true) {
68 | _map.panToBounds(newBounds.toLatLngBounds());
69 | } else {
70 | _map.fitBounds(newBounds.toLatLngBounds());
71 | }
72 | _map.zoom = zoom;
73 | }
74 |
75 | @override
76 | void moveCamera(
77 | GeoCoord latLng, {
78 | bool animated = true,
79 | bool waitUntilReady = true,
80 | double zoom,
81 | }) {
82 | assert(() {
83 | if (latLng == null) {
84 | throw ArgumentError.notNull('latLng');
85 | }
86 |
87 | return true;
88 | }());
89 |
90 | if (animated == true) {
91 | _map.panTo(latLng.toLatLng());
92 | _map.zoom = zoom ?? _map.zoom;
93 | } else {
94 | _map.center = latLng.toLatLng();
95 | _map.zoom = zoom ?? _map.zoom;
96 | }
97 | }
98 |
99 | @override
100 | void zoomCamera(
101 | double zoom, {
102 | bool animated = true,
103 | bool waitUntilReady = true,
104 | }) {
105 | assert(() {
106 | if (zoom == null) {
107 | throw ArgumentError.notNull('zoom');
108 | }
109 |
110 | return true;
111 | }());
112 |
113 | _map.zoom = zoom;
114 | }
115 |
116 | @override
117 | FutureOr get center => _map.center?.toGeoCoord();
118 |
119 | @override
120 | void changeMapStyle(
121 | String mapStyle, {
122 | bool waitUntilReady = true,
123 | }) {
124 | try {
125 | _mapOptions.styles = mapStyle?.parseMapStyle();
126 | _map.options = _mapOptions;
127 | } catch (e) {
128 | throw utils.MapStyleException(e.toString());
129 | }
130 | }
131 |
132 | @override
133 | void addMarkerRaw(
134 | GeoCoord position, {
135 | String label,
136 | String icon,
137 | String info,
138 | String infoSnippet,
139 | ValueChanged onTap,
140 | ui.VoidCallback onInfoWindowTap,
141 | }) {
142 | assert(() {
143 | if (position == null) {
144 | throw ArgumentError.notNull('position');
145 | }
146 |
147 | if (position.latitude == null || position.longitude == null) {
148 | throw ArgumentError.notNull('position.latitude && position.longitude');
149 | }
150 |
151 | return true;
152 | }());
153 |
154 | final key = position.toString();
155 |
156 | if (_markers.containsKey(key)) return;
157 |
158 | final marker = Marker()
159 | ..map = _map
160 | ..label = label
161 | ..icon = _getImage(icon)
162 | ..position = position.toLatLng();
163 |
164 | if (info != null || onTap != null) {
165 | _subscriptions.add(marker.onClick.listen((_) async {
166 | final key = position.toString();
167 |
168 | if (onTap != null) {
169 | onTap(key);
170 | return;
171 | }
172 |
173 | int doubleToInt(double value) => (value * 100000).truncate();
174 | final id =
175 | 'position${doubleToInt(position.latitude)}${doubleToInt(position.longitude)}';
176 |
177 | if (_infos[key] == null) {
178 | print(id);
179 | final _info = onInfoWindowTap == null
180 | ? '$info${infoSnippet.isNotEmpty == true ? '\n$infoSnippet' : ''}'
181 | : '$info${infoSnippet.isNotEmpty == true ? '
$infoSnippet
' : ''}
';
182 |
183 | _infos[key] = InfoWindow(InfoWindowOptions()..content = _info);
184 | _subscriptions.add(
185 | _infos[key].onCloseclick.listen((_) => _infoState[key] = false));
186 | }
187 |
188 | if (!(_infoState[key] ?? false)) {
189 | _infos[key].open(_map, marker);
190 | if (_infoState[key] == null) {
191 | await Future.delayed(const Duration(milliseconds: 100));
192 |
193 | final infoElem = querySelector('flt-platform-view')
194 | .shadowRoot
195 | .getElementById('$htmlId')
196 | .querySelector('#$id');
197 |
198 | infoElem.addEventListener('click', (event) => onInfoWindowTap());
199 | }
200 | _infoState[key] = true;
201 | } else {
202 | _infos[key].close();
203 |
204 | _infoState[key] = false;
205 | }
206 | }));
207 | }
208 |
209 | _markers[key] = marker;
210 | }
211 |
212 | @override
213 | void addMarker(items.Marker marker) => addMarkerRaw(
214 | marker.position,
215 | label: marker.label,
216 | icon: marker.icon,
217 | info: marker.info,
218 | infoSnippet: marker.infoSnippet,
219 | onTap: marker.onTap,
220 | onInfoWindowTap: marker.onInfoWindowTap,
221 | );
222 |
223 | @override
224 | void removeMarker(GeoCoord position) {
225 | assert(() {
226 | if (position == null) {
227 | throw ArgumentError.notNull('position');
228 | }
229 |
230 | if (position.latitude == null || position.longitude == null) {
231 | throw ArgumentError.notNull('position.latitude && position.longitude');
232 | }
233 |
234 | return true;
235 | }());
236 |
237 | final key = position.toString();
238 |
239 | var marker = _markers.remove(key);
240 | marker?.map = null;
241 | marker = null;
242 |
243 | var info = _infos.remove(key);
244 | info?.close();
245 | info = null;
246 |
247 | _infoState.remove(key);
248 | }
249 |
250 | @override
251 | void clearMarkers() {
252 | for (var marker in _markers.values) {
253 | marker?.map = null;
254 | marker = null;
255 | }
256 | _markers.clear();
257 |
258 | for (var info in _infos.values) {
259 | info?.close();
260 | info = null;
261 | }
262 | _infos.clear();
263 |
264 | _infoState.clear();
265 | }
266 |
267 | @override
268 | void addDirection(
269 | dynamic origin,
270 | dynamic destination, {
271 | String startLabel,
272 | String startIcon,
273 | String startInfo,
274 | String endLabel,
275 | String endIcon,
276 | String endInfo,
277 | }) {
278 | assert(() {
279 | if (origin == null) {
280 | throw ArgumentError.notNull('origin');
281 | }
282 |
283 | if (destination == null) {
284 | throw ArgumentError.notNull('destination');
285 | }
286 |
287 | return true;
288 | }());
289 |
290 | _directions.putIfAbsent(
291 | '${origin}_$destination',
292 | () {
293 | DirectionsRenderer direction = DirectionsRenderer(
294 | DirectionsRendererOptions()..suppressMarkers = true);
295 | direction.map = _map;
296 |
297 | final request = DirectionsRequest()
298 | ..origin = origin is GeoCoord
299 | ? LatLng(origin.latitude, origin.longitude)
300 | : origin
301 | ..destination =
302 | destination is GeoCoord ? destination.toLatLng() : destination
303 | ..travelMode = TravelMode.DRIVING;
304 | directionsService.route(
305 | request,
306 | (response, status) {
307 | if (status == DirectionsStatus.OK) {
308 | direction.directions = response;
309 |
310 | final leg = response?.routes?.firstOrNull?.legs?.firstOrNull;
311 |
312 | final startLatLng = leg?.startLocation;
313 | if (startLatLng != null) {
314 | if (startIcon != null ||
315 | startInfo != null ||
316 | startLabel != null) {
317 | addMarkerRaw(
318 | startLatLng.toGeoCoord(),
319 | icon: startIcon,
320 | info: startInfo ?? leg.startAddress,
321 | label: startLabel,
322 | );
323 | } else {
324 | addMarkerRaw(
325 | startLatLng.toGeoCoord(),
326 | icon: 'assets/images/marker_a.png',
327 | info: leg.startAddress,
328 | );
329 | }
330 | }
331 |
332 | final endLatLng = leg?.endLocation;
333 | if (endLatLng != null) {
334 | if (endIcon != null || endInfo != null || endLabel != null) {
335 | addMarkerRaw(
336 | endLatLng.toGeoCoord(),
337 | icon: endIcon,
338 | info: endInfo ?? leg.endAddress,
339 | label: endLabel,
340 | );
341 | } else {
342 | addMarkerRaw(
343 | endLatLng.toGeoCoord(),
344 | icon: 'assets/images/marker_b.png',
345 | info: leg.endAddress,
346 | );
347 | }
348 | }
349 | }
350 | },
351 | );
352 |
353 | return direction;
354 | },
355 | );
356 | }
357 |
358 | @override
359 | void removeDirection(dynamic origin, dynamic destination) {
360 | assert(() {
361 | if (origin == null) {
362 | throw ArgumentError.notNull('origin');
363 | }
364 |
365 | if (destination == null) {
366 | throw ArgumentError.notNull('destination');
367 | }
368 |
369 | return true;
370 | }());
371 |
372 | var value = _directions.remove('${origin}_$destination');
373 | value?.map = null;
374 | final start = value
375 | ?.directions?.routes?.firstOrNull?.legs?.firstOrNull?.startLocation
376 | ?.toGeoCoord();
377 | if (start != null) {
378 | removeMarker(start);
379 | }
380 | final end = value
381 | ?.directions?.routes?.firstOrNull?.legs?.lastOrNull?.endLocation
382 | ?.toGeoCoord();
383 | if (end != null) {
384 | removeMarker(end);
385 | }
386 | value = null;
387 | }
388 |
389 | @override
390 | void clearDirections() {
391 | for (var direction in _directions.values) {
392 | direction?.map = null;
393 | final start = direction
394 | ?.directions?.routes?.firstOrNull?.legs?.firstOrNull?.startLocation
395 | ?.toGeoCoord();
396 | if (start != null) {
397 | removeMarker(start);
398 | }
399 | final end = direction
400 | ?.directions?.routes?.firstOrNull?.legs?.lastOrNull?.endLocation
401 | ?.toGeoCoord();
402 | if (end != null) {
403 | removeMarker(end);
404 | }
405 | direction = null;
406 | }
407 | _directions.clear();
408 | }
409 |
410 | @override
411 | void addPolygon(
412 | String id,
413 | Iterable points, {
414 | ValueChanged onTap,
415 | Color strokeColor = const Color(0x000000),
416 | double strokeOpacity = 0.8,
417 | double strokeWidth = 1,
418 | Color fillColor = const Color(0x000000),
419 | double fillOpacity = 0.35,
420 | }) {
421 | assert(() {
422 | if (id == null) {
423 | throw ArgumentError.notNull('id');
424 | }
425 |
426 | if (points == null) {
427 | throw ArgumentError.notNull('position');
428 | }
429 |
430 | if (points.isEmpty) {
431 | throw ArgumentError.value([], 'points');
432 | }
433 |
434 | if (points.length < 3) {
435 | throw ArgumentError('Polygon must have at least 3 coordinates');
436 | }
437 |
438 | return true;
439 | }());
440 |
441 | _polygons.putIfAbsent(
442 | id,
443 | () {
444 | final options = PolygonOptions()
445 | ..clickable = onTap != null
446 | ..paths = points.mapList((_) => _.toLatLng())
447 | ..strokeColor = strokeColor?.toHashString() ?? '#000000'
448 | ..strokeOpacity = strokeOpacity ?? 0.8
449 | ..strokeWeight = strokeWidth ?? 1
450 | ..fillColor = strokeColor?.toHashString() ?? '#000000'
451 | ..fillOpacity = fillOpacity ?? 0.35;
452 |
453 | final polygon = Polygon(options)..map = _map;
454 |
455 | if (onTap != null) {
456 | _subscriptions.add(polygon.onClick.listen((_) => onTap(id)));
457 | }
458 |
459 | return polygon;
460 | },
461 | );
462 | }
463 |
464 | @override
465 | void editPolygon(
466 | String id,
467 | Iterable points, {
468 | ValueChanged onTap,
469 | Color strokeColor = const Color(0x000000),
470 | double strokeOpacity = 0.8,
471 | double strokeWeight = 1,
472 | Color fillColor = const Color(0x000000),
473 | double fillOpacity = 0.35,
474 | }) {
475 | removePolygon(id);
476 | addPolygon(
477 | id,
478 | points,
479 | onTap: onTap,
480 | strokeColor: strokeColor,
481 | strokeOpacity: strokeOpacity,
482 | strokeWidth: strokeWeight,
483 | fillColor: fillColor,
484 | fillOpacity: fillOpacity,
485 | );
486 | }
487 |
488 | @override
489 | void removePolygon(String id) {
490 | assert(() {
491 | if (id == null) {
492 | throw ArgumentError.notNull('id');
493 | }
494 |
495 | return true;
496 | }());
497 |
498 | var value = _polygons.remove(id);
499 | value?.map = null;
500 | value = null;
501 | }
502 |
503 | @override
504 | void clearPolygons() {
505 | for (var polygon in _polygons.values) {
506 | polygon?.map = null;
507 | polygon = null;
508 | }
509 | _polygons.clear();
510 | }
511 |
512 | void _createMapOptions() {
513 | _mapOptions = MapOptions()
514 | ..zoom = widget.initialZoom
515 | ..center = widget.initialPosition.toLatLng()
516 | ..streetViewControl = widget.webPreferences.streetViewControl
517 | ..fullscreenControl = widget.webPreferences.fullscreenControl
518 | ..mapTypeControl = widget.webPreferences.mapTypeControl
519 | ..scrollwheel = widget.webPreferences.scrollwheel
520 | ..panControl = widget.webPreferences.panControl
521 | ..overviewMapControl = widget.webPreferences.overviewMapControl
522 | ..rotateControl = widget.webPreferences.rotateControl
523 | ..scaleControl = widget.webPreferences.scaleControl
524 | ..zoomControl = widget.webPreferences.zoomControl
525 | ..minZoom = widget.minZoom
526 | ..maxZoom = widget.maxZoom
527 | ..styles = widget.mapStyle?.parseMapStyle()
528 | ..mapTypeId = widget.mapType.toString().split('.')[1]
529 | ..gestureHandling = widget.interactive ? 'auto' : 'none';
530 | }
531 |
532 | @override
533 | void addCircle(
534 | String id,
535 | GeoCoord center,
536 | double radius, {
537 | ValueChanged onTap,
538 | ui.Color strokeColor = const Color(0x000000),
539 | double strokeOpacity = 0.8,
540 | double strokeWidth = 1,
541 | ui.Color fillColor = const Color(0x000000),
542 | double fillOpacity = 0.35,
543 | }) {
544 | assert(() {
545 | if (id == null) {
546 | throw ArgumentError.notNull('id');
547 | }
548 |
549 | if (center == null) {
550 | throw ArgumentError.notNull('center');
551 | }
552 |
553 | if (radius == null) {
554 | throw ArgumentError.notNull('radius');
555 | }
556 |
557 | return true;
558 | }());
559 |
560 | _circles.putIfAbsent(
561 | id,
562 | () {
563 | final options = CircleOptions()
564 | ..center = center.toLatLng()
565 | ..radius = radius
566 | ..clickable = onTap != null
567 | ..strokeColor = strokeColor?.toHashString() ?? '#000000'
568 | ..strokeOpacity = strokeOpacity ?? 0.8
569 | ..strokeWeight = strokeWidth ?? 1
570 | ..fillColor = strokeColor?.toHashString() ?? '#000000'
571 | ..fillOpacity = fillOpacity ?? 0.35;
572 |
573 | final circle = Circle(options)..map = _map;
574 |
575 | if (onTap != null) {
576 | _subscriptions.add(circle.onClick.listen((_) => onTap(id)));
577 | }
578 |
579 | return circle;
580 | },
581 | );
582 | }
583 |
584 | @override
585 | void clearCircles() {
586 | for (var circle in _circles.values) {
587 | circle?.map = null;
588 | circle = null;
589 | }
590 | _circles.clear();
591 | }
592 |
593 | @override
594 | void editCircle(
595 | String id,
596 | GeoCoord center,
597 | double radius, {
598 | ValueChanged onTap,
599 | ui.Color strokeColor = const Color(0x000000),
600 | double strokeOpacity = 0.8,
601 | double strokeWidth = 1,
602 | ui.Color fillColor = const Color(0x000000),
603 | double fillOpacity = 0.35,
604 | }) {
605 | removeCircle(id);
606 | addCircle(
607 | id,
608 | center,
609 | radius,
610 | onTap: onTap,
611 | strokeColor: strokeColor,
612 | strokeOpacity: strokeOpacity,
613 | strokeWidth: strokeWidth,
614 | fillColor: fillColor,
615 | fillOpacity: fillOpacity,
616 | );
617 | }
618 |
619 | @override
620 | void removeCircle(String id) {
621 | assert(() {
622 | if (id == null) {
623 | throw ArgumentError.notNull('id');
624 | }
625 |
626 | return true;
627 | }());
628 |
629 | var value = _circles.remove(id);
630 | value?.map = null;
631 | value = null;
632 | }
633 |
634 | @override
635 | void initState() {
636 | super.initState();
637 | SchedulerBinding.instance.addPostFrameCallback((_) {
638 | for (var marker in widget.markers) {
639 | addMarker(marker);
640 | }
641 | });
642 | }
643 |
644 | @override
645 | Widget build(BuildContext context) {
646 | _createMapOptions();
647 |
648 | if (_map == null) {
649 | ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
650 | final elem = DivElement()
651 | ..id = htmlId
652 | ..style.width = '100%'
653 | ..style.height = '100%'
654 | ..style.border = 'none';
655 |
656 | _map = GMap(elem, _mapOptions);
657 |
658 | _subscriptions.add(_map.onClick.listen(
659 | (event) => widget.onTap?.call(event?.latLng?.toGeoCoord())));
660 | _subscriptions.add(_map.onRightclick.listen(
661 | (event) => widget.onLongPress?.call(event?.latLng?.toGeoCoord())));
662 |
663 | return elem;
664 | });
665 | }
666 |
667 | return LayoutBuilder(
668 | builder: (context, constraints) => GestureDetector(
669 | onVerticalDragUpdate:
670 | widget.webPreferences.dragGestures ? null : (_) {},
671 | onHorizontalDragUpdate:
672 | widget.webPreferences.dragGestures ? null : (_) {},
673 | child: Container(
674 | constraints: BoxConstraints(maxHeight: constraints.maxHeight),
675 | child: HtmlElementView(viewType: htmlId),
676 | ),
677 | ),
678 | );
679 | }
680 |
681 | @override
682 | void dispose() {
683 | super.dispose();
684 | _subscriptions.forEach((_) => _.cancel());
685 |
686 | _infos.clear();
687 | _markers.clear();
688 | _polygons.clear();
689 | _circles.clear();
690 | _infoState.clear();
691 | _directions.clear();
692 | _subscriptions.clear();
693 |
694 | _map = null;
695 | _mapOptions = null;
696 | }
697 | }
698 |
--------------------------------------------------------------------------------
/lib/src/web/utils.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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:convert';
6 | import 'dart:ui' as ui show Color;
7 |
8 | import 'package:flinq/flinq.dart';
9 | import 'package:google_maps/google_maps.dart';
10 | import 'package:google_directions_api/google_directions_api.dart'
11 | show GeoCoord, GeoCoordBounds;
12 |
13 | extension WebLatLngExtensions on LatLng {
14 | GeoCoord toGeoCoord() => GeoCoord(this.lat, this.lng);
15 | }
16 |
17 | extension WebGeoCoordExtensions on GeoCoord {
18 | LatLng toLatLng() => LatLng(this.latitude, this.longitude);
19 | }
20 |
21 | extension WebGeoCoordBoundsExtensions on GeoCoordBounds {
22 | LatLngBounds toLatLngBounds() => LatLngBounds(
23 | this.southwest.toLatLng(),
24 | this.northeast.toLatLng(),
25 | );
26 |
27 | GeoCoord get center => GeoCoord(
28 | (this.northeast.latitude + this.southwest.latitude) / 2,
29 | (this.northeast.longitude + this.southwest.longitude) / 2,
30 | );
31 | }
32 |
33 | extension WebLatLngBoundsExtensions on LatLngBounds {
34 | GeoCoordBounds toGeoCoordBounds() => GeoCoordBounds(
35 | northeast: this.northEast.toGeoCoord(),
36 | southwest: this.southWest.toGeoCoord(),
37 | );
38 | }
39 |
40 | extension WebColorExtensions on ui.Color {
41 | String toHashString() =>
42 | '#${this.red.toRadixString(16)}${this.green.toRadixString(16)}${this.blue.toRadixString(16)}';
43 | }
44 |
45 | extension WebMapStyleExtension on String {
46 | MapTypeStyleElementType _elementTypeFromString(String value) {
47 | switch (value) {
48 | case 'all':
49 | return MapTypeStyleElementType.ALL;
50 | case 'geometry':
51 | return MapTypeStyleElementType.GEOMETRY;
52 | case 'geometry.fill':
53 | return MapTypeStyleElementType.GEOMETRY_FILL;
54 | case 'geometry.stroke':
55 | return MapTypeStyleElementType.GEOMETRY_STROKE;
56 | case 'labels':
57 | return MapTypeStyleElementType.LABELS;
58 | case 'labels.icon':
59 | return MapTypeStyleElementType.LABELS_ICON;
60 | case 'labels.text':
61 | return MapTypeStyleElementType.LABELS_TEXT;
62 | case 'labels.text.fill':
63 | return MapTypeStyleElementType.LABELS_TEXT_FILL;
64 | case 'labels.text.stroke':
65 | return MapTypeStyleElementType.LABELS_TEXT_STROKE;
66 |
67 | default:
68 | return null;
69 | }
70 | }
71 |
72 | MapTypeStyleFeatureType _featureTypeFromString(String value) {
73 | switch (value) {
74 | case 'administrative':
75 | return MapTypeStyleFeatureType.ADMINISTRATIVE;
76 | case 'administrative.country':
77 | return MapTypeStyleFeatureType.ADMINISTRATIVE_COUNTRY;
78 | case 'administrative.land_parcel':
79 | return MapTypeStyleFeatureType.ADMINISTRATIVE_LAND_PARCEL;
80 | case 'administrative.locality':
81 | return MapTypeStyleFeatureType.ADMINISTRATIVE_LOCALITY;
82 | case 'administrative.neighborhood':
83 | return MapTypeStyleFeatureType.ADMINISTRATIVE_NEIGHBORHOOD;
84 | case 'administrative.province':
85 | return MapTypeStyleFeatureType.ADMINISTRATIVE_PROVINCE;
86 | case 'all':
87 | return MapTypeStyleFeatureType.ALL;
88 | case 'landscape':
89 | return MapTypeStyleFeatureType.LANDSCAPE;
90 | case 'landscape.man_made':
91 | return MapTypeStyleFeatureType.LANDSCAPE_MAN_MADE;
92 | case 'landscape.natural':
93 | return MapTypeStyleFeatureType.LANDSCAPE_NATURAL;
94 | case 'landscape.natural.landcover':
95 | return MapTypeStyleFeatureType.LANDSCAPE_NATURAL_LANDCOVER;
96 | case 'landscape.natural.terrain':
97 | return MapTypeStyleFeatureType.LANDSCAPE_NATURAL_TERRAIN;
98 | case 'poi':
99 | return MapTypeStyleFeatureType.POI;
100 | case 'poi.attraction':
101 | return MapTypeStyleFeatureType.POI_ATTRACTION;
102 | case 'poi.business':
103 | return MapTypeStyleFeatureType.POI_BUSINESS;
104 | case 'poi.government':
105 | return MapTypeStyleFeatureType.POI_GOVERNMENT;
106 | case 'poi.medical':
107 | return MapTypeStyleFeatureType.POI_MEDICAL;
108 | case 'poi.park':
109 | return MapTypeStyleFeatureType.POI_PARK;
110 | case 'poi.place_of_worship':
111 | return MapTypeStyleFeatureType.POI_PLACE_OF_WORSHIP;
112 | case 'poi.school':
113 | return MapTypeStyleFeatureType.POI_SCHOOL;
114 | case 'poi.sports_complex':
115 | return MapTypeStyleFeatureType.POI_SPORTS_COMPLEX;
116 | case 'road':
117 | return MapTypeStyleFeatureType.ROAD;
118 | case 'road.arterial':
119 | return MapTypeStyleFeatureType.ROAD_ARTERIAL;
120 | case 'road.highway':
121 | return MapTypeStyleFeatureType.ROAD_HIGHWAY;
122 | case 'road.highway.controlled_access':
123 | return MapTypeStyleFeatureType.ROAD_HIGHWAY_CONTROLLED_ACCESS;
124 | case 'road.local':
125 | return MapTypeStyleFeatureType.ROAD_LOCAL;
126 | case 'transit':
127 | return MapTypeStyleFeatureType.TRANSIT;
128 | case 'transit.line':
129 | return MapTypeStyleFeatureType.TRANSIT_LINE;
130 | case 'transit.station':
131 | return MapTypeStyleFeatureType.TRANSIT_STATION;
132 | case 'transit.station.airport':
133 | return MapTypeStyleFeatureType.TRANSIT_STATION_AIRPORT;
134 | case 'transit.station.bus':
135 | return MapTypeStyleFeatureType.TRANSIT_STATION_BUS;
136 | case 'transit.station.rail':
137 | return MapTypeStyleFeatureType.TRANSIT_STATION_RAIL;
138 | case 'water':
139 | return MapTypeStyleFeatureType.WATER;
140 |
141 | default:
142 | return null;
143 | }
144 | }
145 |
146 | MapTypeStyler _stylerFromMap(Map map) => MapTypeStyler()
147 | ..color = map['color']
148 | ..gamma = map['gamma']
149 | ..hue = map['hue']
150 | ..invertLightness = map['invertLightness']
151 | ..lightness = map['lightness']
152 | ..saturation = map['saturation']
153 | ..visibility = map['visibility']
154 | ..weight = map['weight'];
155 |
156 | List parseMapStyle() {
157 | final List map = json.decode(this);
158 | return map.mapList(
159 | (style) => MapTypeStyle()
160 | ..elementType = _elementTypeFromString(style['elementType'])
161 | ..featureType = _featureTypeFromString(style['featureType'])
162 | ..stylers = (style['stylers'] as List)?.mapList(
163 | (styler) => _stylerFromMap(styler),
164 | ),
165 | );
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/publish_commands.txt:
--------------------------------------------------------------------------------
1 | flutter packages pub publish --dry-run
2 | flutter packages pub publish
3 |
4 | pub uploader --package= add
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_google_maps
2 | version: 4.0.0
3 | homepage: https://github.com/marchdev-tk/flutter_google_maps
4 | description: A Flutter plugin for integrating Google Maps in iOS, Android and Web applications. It is a wrapper of google_maps_flutter for Mobile and google_maps for Web.
5 |
6 | environment:
7 | sdk: ">=2.6.0 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | google_polyline_algorithm: ^2.0.0
14 | google_maps_flutter: ^0.5.28+1
15 | google_directions_api: ^0.5.0
16 | google_maps: ^3.4.3
17 | http: ^0.12.1
18 | flinq: ^1.1.0
19 | uuid: ^2.1.0
20 |
21 | dev_dependencies:
22 | flutter_test:
23 | sdk: flutter
24 |
25 | pedantic: ^1.8.0
26 |
27 | flutter:
28 | uses-material-design: true
29 | assets:
30 | - assets/images/marker_a.png
31 | - assets/images/marker_b.png
--------------------------------------------------------------------------------
/test/flutter_google_maps_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, the MarchDev Toolkit 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 | void main() {}
6 |
--------------------------------------------------------------------------------