| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| part of google_maps_flutter; |
| |
| typedef void MapCreatedCallback(GoogleMapController controller); |
| |
| class GoogleMap extends StatefulWidget { |
| const GoogleMap({ |
| @required this.initialCameraPosition, |
| this.onMapCreated, |
| this.gestureRecognizers, |
| this.compassEnabled = true, |
| this.cameraTargetBounds = CameraTargetBounds.unbounded, |
| this.mapType = MapType.normal, |
| this.minMaxZoomPreference = MinMaxZoomPreference.unbounded, |
| this.rotateGesturesEnabled = true, |
| this.scrollGesturesEnabled = true, |
| this.zoomGesturesEnabled = true, |
| this.tiltGesturesEnabled = true, |
| this.trackCameraPosition = false, |
| this.myLocationEnabled = false, |
| }) : assert(initialCameraPosition != null); |
| |
| final MapCreatedCallback onMapCreated; |
| |
| /// The initial position of the map's camera. |
| final CameraPosition initialCameraPosition; |
| |
| /// True if the map should show a compass when rotated. |
| final bool compassEnabled; |
| |
| /// Geographical bounding box for the camera target. |
| final CameraTargetBounds cameraTargetBounds; |
| |
| /// Type of map tiles to be rendered. |
| final MapType mapType; |
| |
| /// Preferred bounds for the camera zoom level. |
| /// |
| /// Actual bounds depend on map data and device. |
| final MinMaxZoomPreference minMaxZoomPreference; |
| |
| /// True if the map view should respond to rotate gestures. |
| final bool rotateGesturesEnabled; |
| |
| /// True if the map view should respond to scroll gestures. |
| final bool scrollGesturesEnabled; |
| |
| /// True if the map view should respond to zoom gestures. |
| final bool zoomGesturesEnabled; |
| |
| /// True if the map view should respond to tilt gestures. |
| final bool tiltGesturesEnabled; |
| |
| /// True if the map view should relay camera move events to Flutter. |
| final bool trackCameraPosition; |
| |
| /// True if a "My Location" layer should be shown on the map. |
| /// |
| /// This layer includes a location indicator at the current device location, |
| /// as well as a My Location button. |
| /// * The indicator is a small blue dot if the device is stationary, or a |
| /// chevron if the device is moving. |
| /// * The My Location button animates to focus on the user's current location |
| /// if the user's location is currently known. |
| /// |
| /// Enabling this feature requires adding location permissions to both native |
| /// platforms of your app. |
| /// * On Android add either |
| /// `<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />` |
| /// or `<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />` |
| /// to your `AndroidManifest.xml` file. `ACCESS_COARSE_LOCATION` returns a |
| /// location with an accuracy approximately equivalent to a city block, while |
| /// `ACCESS_FINE_LOCATION` returns as precise a location as possible, although |
| /// it consumes more battery power. You will also need to request these |
| /// permissions during run-time. If they are not granted, the My Location |
| /// feature will fail silently. |
| /// * On iOS add a `NSLocationWhenInUseUsageDescription` key to your |
| /// `Info.plist` file. This will automatically prompt the user for permissions |
| /// when the map tries to turn on the My Location layer. |
| final bool myLocationEnabled; |
| |
| /// Which gestures should be consumed by the map. |
| /// |
| /// It is possible for other gesture recognizers to be competing with the map on pointer |
| /// events, e.g if the map is inside a [ListView] the [ListView] will want to handle |
| /// vertical drags. The map will claim gestures that are recognized by any of the |
| /// recognizers on this list. |
| /// |
| /// When this set is empty or null, the map will only handle pointer events for gestures that |
| /// were not claimed by any other gesture recognizer. |
| final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers; |
| |
| @override |
| State createState() => _GoogleMapState(); |
| } |
| |
| class _GoogleMapState extends State<GoogleMap> { |
| final Completer<GoogleMapController> _controller = |
| Completer<GoogleMapController>(); |
| |
| _GoogleMapOptions _googleMapOptions; |
| |
| @override |
| Widget build(BuildContext context) { |
| final Map<String, dynamic> creationParams = <String, dynamic>{ |
| 'initialCameraPosition': widget.initialCameraPosition?._toMap(), |
| 'options': _GoogleMapOptions.fromWidget(widget).toMap(), |
| }; |
| if (defaultTargetPlatform == TargetPlatform.android) { |
| return AndroidView( |
| viewType: 'plugins.flutter.io/google_maps', |
| onPlatformViewCreated: onPlatformViewCreated, |
| gestureRecognizers: widget.gestureRecognizers, |
| creationParams: creationParams, |
| creationParamsCodec: const StandardMessageCodec(), |
| ); |
| } else if (defaultTargetPlatform == TargetPlatform.iOS) { |
| return UiKitView( |
| viewType: 'plugins.flutter.io/google_maps', |
| onPlatformViewCreated: onPlatformViewCreated, |
| gestureRecognizers: widget.gestureRecognizers, |
| creationParams: creationParams, |
| creationParamsCodec: const StandardMessageCodec(), |
| ); |
| } |
| |
| return Text( |
| '$defaultTargetPlatform is not yet supported by the maps plugin'); |
| } |
| |
| @override |
| void initState() { |
| super.initState(); |
| _googleMapOptions = _GoogleMapOptions.fromWidget(widget); |
| } |
| |
| @override |
| void didUpdateWidget(GoogleMap oldWidget) { |
| super.didUpdateWidget(oldWidget); |
| final _GoogleMapOptions newOptions = _GoogleMapOptions.fromWidget(widget); |
| final Map<String, dynamic> updates = |
| _googleMapOptions.updatesMap(newOptions); |
| _updateOptions(updates); |
| _googleMapOptions = newOptions; |
| } |
| |
| void _updateOptions(Map<String, dynamic> updates) async { |
| if (updates.isEmpty) { |
| return; |
| } |
| final GoogleMapController controller = await _controller.future; |
| controller._updateMapOptions(updates); |
| } |
| |
| Future<void> onPlatformViewCreated(int id) async { |
| final GoogleMapController controller = |
| await GoogleMapController.init(id, widget.initialCameraPosition); |
| _controller.complete(controller); |
| if (widget.onMapCreated != null) { |
| widget.onMapCreated(controller); |
| } |
| } |
| } |
| |
| /// Configuration options for the GoogleMaps user interface. |
| /// |
| /// When used to change configuration, null values will be interpreted as |
| /// "do not change this configuration option". |
| class _GoogleMapOptions { |
| _GoogleMapOptions({ |
| this.compassEnabled, |
| this.cameraTargetBounds, |
| this.mapType, |
| this.minMaxZoomPreference, |
| this.rotateGesturesEnabled, |
| this.scrollGesturesEnabled, |
| this.tiltGesturesEnabled, |
| this.trackCameraPosition, |
| this.zoomGesturesEnabled, |
| this.myLocationEnabled, |
| }); |
| |
| static _GoogleMapOptions fromWidget(GoogleMap map) { |
| return _GoogleMapOptions( |
| compassEnabled: map.compassEnabled, |
| cameraTargetBounds: map.cameraTargetBounds, |
| mapType: map.mapType, |
| minMaxZoomPreference: map.minMaxZoomPreference, |
| rotateGesturesEnabled: map.rotateGesturesEnabled, |
| scrollGesturesEnabled: map.scrollGesturesEnabled, |
| tiltGesturesEnabled: map.tiltGesturesEnabled, |
| trackCameraPosition: map.trackCameraPosition, |
| zoomGesturesEnabled: map.zoomGesturesEnabled, |
| myLocationEnabled: map.myLocationEnabled, |
| ); |
| } |
| |
| final bool compassEnabled; |
| |
| final CameraTargetBounds cameraTargetBounds; |
| |
| final MapType mapType; |
| |
| final MinMaxZoomPreference minMaxZoomPreference; |
| |
| final bool rotateGesturesEnabled; |
| |
| final bool scrollGesturesEnabled; |
| |
| final bool tiltGesturesEnabled; |
| |
| final bool trackCameraPosition; |
| |
| final bool zoomGesturesEnabled; |
| |
| final bool myLocationEnabled; |
| |
| Map<String, dynamic> toMap() { |
| final Map<String, dynamic> optionsMap = <String, dynamic>{}; |
| |
| void addIfNonNull(String fieldName, dynamic value) { |
| if (value != null) { |
| optionsMap[fieldName] = value; |
| } |
| } |
| |
| addIfNonNull('compassEnabled', compassEnabled); |
| addIfNonNull('cameraTargetBounds', cameraTargetBounds?._toJson()); |
| addIfNonNull('mapType', mapType?.index); |
| addIfNonNull('minMaxZoomPreference', minMaxZoomPreference?._toJson()); |
| addIfNonNull('rotateGesturesEnabled', rotateGesturesEnabled); |
| addIfNonNull('scrollGesturesEnabled', scrollGesturesEnabled); |
| addIfNonNull('tiltGesturesEnabled', tiltGesturesEnabled); |
| addIfNonNull('zoomGesturesEnabled', zoomGesturesEnabled); |
| addIfNonNull('trackCameraPosition', trackCameraPosition); |
| addIfNonNull('myLocationEnabled', myLocationEnabled); |
| return optionsMap; |
| } |
| |
| Map<String, dynamic> updatesMap(_GoogleMapOptions newOptions) { |
| final Map<String, dynamic> prevOptionsMap = toMap(); |
| return newOptions.toMap() |
| ..removeWhere( |
| (String key, dynamic value) => prevOptionsMap[key] == value); |
| } |
| } |