From 1f969376beb87989c083e190576cb8bc73c136cf Mon Sep 17 00:00:00 2001
From: morvagergely <51244648+morvagergely@users.noreply.github.com>
Date: Thu, 12 May 2022 13:16:20 +0200
Subject: [PATCH 01/22] Document layer zoom limits (#1028)
---
lib/src/controller.dart | 32 ++++++++++++++++++++++++++++++--
1 file changed, 30 insertions(+), 2 deletions(-)
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index 1d4583f13..70df6971a 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -340,6 +340,10 @@ class MapboxMapController extends ChangeNotifier {
/// If [enableInteraction] is set the layer is considered for touch or drag
/// events. [sourceLayer] is used to selected a specific source layer from
/// Vector source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
/// [filter] determines which features should be rendered in the layer.
/// Filters are written as [expressions].
///
@@ -376,6 +380,10 @@ class MapboxMapController extends ChangeNotifier {
/// If [enableInteraction] is set the layer is considered for touch or drag
/// events. [sourceLayer] is used to selected a specific source layer from
/// Vector source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
/// [filter] determines which features should be rendered in the layer.
/// Filters are written as [expressions].
///
@@ -412,6 +420,10 @@ class MapboxMapController extends ChangeNotifier {
/// If [enableInteraction] is set the layer is considered for touch or drag
/// events. [sourceLayer] is used to selected a specific source layer from
/// Vector source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
/// [filter] determines which features should be rendered in the layer.
/// Filters are written as [expressions].
///
@@ -448,6 +460,10 @@ class MapboxMapController extends ChangeNotifier {
/// If [enableInteraction] is set the layer is considered for touch or drag
/// events. [sourceLayer] is used to selected a specific source layer from
/// Vector source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
/// [filter] determines which features should be rendered in the layer.
/// Filters are written as [expressions].
///
@@ -482,7 +498,11 @@ class MapboxMapController extends ChangeNotifier {
///
/// Setting [belowLayerId] adds the new layer below the given id.
/// [sourceLayer] is used to selected a specific source layer from
- /// Raster source
+ /// Raster source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
Future addRasterLayer(
String sourceId, String layerId, RasterLayerProperties properties,
{String? belowLayerId,
@@ -509,7 +529,11 @@ class MapboxMapController extends ChangeNotifier {
///
/// Setting [belowLayerId] adds the new layer below the given id.
/// [sourceLayer] is used to selected a specific source layer from
- /// Raster source
+ /// Raster source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
Future addHillshadeLayer(
String sourceId, String layerId, HillshadeLayerProperties properties,
{String? belowLayerId,
@@ -1129,6 +1153,10 @@ class MapboxMapController extends ChangeNotifier {
/// [HillshadeLayerProperties].
/// [sourceLayer] is used to selected a specific source layer from Vector
/// source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
/// [filter] determines which features should be rendered in the layer.
/// Filters are written as [expressions].
/// [filter] is not supported by RasterLayer and HillshadeLayer.
From 47b77e74cf261cf1ca75938481a0736eb5ca0492 Mon Sep 17 00:00:00 2001
From: Tobrun
Date: Thu, 19 May 2022 12:08:11 +0200
Subject: [PATCH 02/22] [release] update changelog for v0.16.0 (#1052)
---
CHANGELOG.md | 37 +++++++++++++++++++++--
mapbox_gl_platform_interface/CHANGELOG.md | 13 ++++++--
mapbox_gl_web/CHANGELOG.md | 13 +++++++-
3 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1163ad7b7..dcc46455e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,38 @@
+## 0.16.0, May 19, 2022
+* Fix type issues in query rendered features in rect [#862](https://github.com/flutter-mapbox-gl/maps/pull/862)
+* Annotation manager moved to dart [#779](https://github.com/flutter-mapbox-gl/maps/pull/779)
+* Add java formatting [#863](https://github.com/flutter-mapbox-gl/maps/pull/863)
+* Fix urls parsing [#868](https://github.com/flutter-mapbox-gl/maps/pull/868)
+* Fix for MissingPluginException when using downloadOfflineRegion [#864](https://github.com/flutter-mapbox-gl/maps/pull/864)
+* Fix issue with map disposal on web [#895](https://github.com/flutter-mapbox-gl/maps/pull/895)
+* Fix for rescale issues on web [#896](https://github.com/flutter-mapbox-gl/maps/pull/896)
+* Fix android build issues [#904](https://github.com/flutter-mapbox-gl/maps/pull/904)
+* Upgraded mapbox gl js to 2.7.0 [#889](https://github.com/flutter-mapbox-gl/maps/pull/889)
+* Add updateContentInsets on Android [#903](https://github.com/flutter-mapbox-gl/maps/pull/903)
+* Fix MapController's queryRendered* methods not considering layerIds [#870](https://github.com/flutter-mapbox-gl/maps/pull/870)
+* Adding function in annotation manager to remove multiple annotations [#931](https://github.com/flutter-mapbox-gl/maps/pull/931)
+* Update "Setting up" section of README [#918](https://github.com/flutter-mapbox-gl/maps/pull/918)
+* Add support for layer zoom limits [#934](https://github.com/flutter-mapbox-gl/maps/pull/934)
+* Add locationComponent#getLastLocation [#922](https://github.com/flutter-mapbox-gl/maps/pull/922)
+* Add and default to Hybrid composition on Android [#916](https://github.com/flutter-mapbox-gl/maps/pull/916)
+* Fix location puck getting hidden [#956](https://github.com/flutter-mapbox-gl/maps/pull/956)
+* Don't call locationComponent if it's null [#966](https://github.com/flutter-mapbox-gl/maps/pull/966)
+* Do nothing and correctly return if layer and source to remove are not there [](https://github.com/flutter-mapbox-gl/maps/pull/961)
+* Fixed LocationTracking issue delayed permissions [#958](https://github.com/flutter-mapbox-gl/maps/pull/958)
+* Fix for LocationComponent update issue [#969](https://github.com/flutter-mapbox-gl/maps/pull/969)
+* Features with non string ids will not update properly [#970](https://github.com/flutter-mapbox-gl/maps/pull/970)
+* Update added shapes by layer when setting a feature [#972](https://github.com/flutter-mapbox-gl/maps/pull/972)
+* Allows modifying http headers [#977](https://github.com/flutter-mapbox-gl/maps/pull/977)
+* Additional documentation [#986](https://github.com/flutter-mapbox-gl/maps/pull/986)
+* Disabled hybrid composition do to various issues [#992](https://github.com/flutter-mapbox-gl/maps/pull/992)
+* Implement layer filtering [#997](https://github.com/flutter-mapbox-gl/maps/pull/997)
+* Drag event types support added [#987](https://github.com/flutter-mapbox-gl/maps/pull/987)
+* Remove duplicated code [#1026](https://github.com/flutter-mapbox-gl/maps/pull/1026)
+* Support filtering on addLayer [#1024](https://github.com/flutter-mapbox-gl/maps/pull/1024)
+* Document layer zoom limits [#1028](https://github.com/flutter-mapbox-gl/maps/pull/1028)
+
## 0.15.0, January 13, 2022
-* Callbacks added to onFeatureTapped will now also get the position (`Point`) and the location (`LatLng`) of the click passed when called [#798](https://github.com/flutter-mapbox-gl/maps/pull/798)
+* Callbacks added to onFeatureTapped will now also get the position (`Point`) and the location (`LatLng`) of the click passed when called [#798](https://github.com/flutter-mapbox-gl/maps/pull/798)
* Fixed layer based feature selection [#765](https://github.com/flutter-mapbox-gl/maps/pull/765)
* Implement the changePosition function for place_fill example [#778](https://github.com/flutter-mapbox-gl/maps/pull/778)
* Invoke onPause method of MapView in onPause lifecycle [#782](https://github.com/flutter-mapbox-gl/maps/pull/782)
@@ -24,7 +57,7 @@
* Fixed issue with return type of remove source on web [#854](https://github.com/flutter-mapbox-gl/maps/pull/854)
## 0.14.0, November 13, 2021
-* Remove memory leaks by disposing internal components [#706](https://github.com/tobrun/flutter-mapbox-gl/pull/706)
+* Remove memory leaks by disposing internal components [#706](https://github.com/tobrun/flutter-mapbox-gl/pull/706)
* Improved annotation click order [#748](https://github.com/tobrun/flutter-mapbox-gl/pull/748)
* Add support for Layers, properties and expressions backed by GeoJsonSource [#723](https://github.com/tobrun/flutter-mapbox-gl/pull/723)
* Add attribution button gravity, position normally [#731](https://github.com/tobrun/flutter-mapbox-gl/pull/731)
diff --git a/mapbox_gl_platform_interface/CHANGELOG.md b/mapbox_gl_platform_interface/CHANGELOG.md
index 9c4cda24a..c187c4f50 100644
--- a/mapbox_gl_platform_interface/CHANGELOG.md
+++ b/mapbox_gl_platform_interface/CHANGELOG.md
@@ -1,10 +1,19 @@
+## 0.16.0, May 19, 2022
+* Annotation manager moved to dart [#779](https://github.com/flutter-mapbox-gl/maps/pull/779)
+* Fix issue with map disposal on web [#895](https://github.com/flutter-mapbox-gl/maps/pull/895)
+* Add support for layer zoom limits [#934](https://github.com/flutter-mapbox-gl/maps/pull/934)
+* Add and default to Hybrid composition on Android [#916](https://github.com/flutter-mapbox-gl/maps/pull/916)
+* Implement layer filtering [#997](https://github.com/flutter-mapbox-gl/maps/pull/997)
+* Drag event types support added [#987](https://github.com/flutter-mapbox-gl/maps/pull/987)
+* Support filtering on addLayer [#1024](https://github.com/flutter-mapbox-gl/maps/pull/1024)
+
## 0.15.0, January 13, 2022
-* Callbacks added to onFeatureTapped will now also get the position (`Point`) and the location (`LatLng`) of the click passed when called [#798](https://github.com/flutter-mapbox-gl/maps/pull/798)
+* Callbacks added to onFeatureTapped will now also get the position (`Point`) and the location (`LatLng`) of the click passed when called [#798](https://github.com/flutter-mapbox-gl/maps/pull/798)
* Fix web issues with style loaded, feature tap, add promoteId, pointer change issue [#785](https://github.com/flutter-mapbox-gl/maps/pull/785)
* Full style source support [#797](https://github.com/flutter-mapbox-gl/maps/pull/797)
## 0.14.0, November 13, 2021
-* Remove memory leaks by disposing internal components [#706](https://github.com/tobrun/flutter-mapbox-gl/pull/706)
+* Remove memory leaks by disposing internal components [#706](https://github.com/tobrun/flutter-mapbox-gl/pull/706)
* Add support for Layers, properties and expressions backed by GeoJsonSource [#723](https://github.com/tobrun/flutter-mapbox-gl/pull/723)
* Add attribution button gravity, position normally [#731](https://github.com/tobrun/flutter-mapbox-gl/pull/731)
* Remove MapboxGlPlatform.getInstance [#710](https://github.com/tobrun/flutter-mapbox-gl/pull/710)
diff --git a/mapbox_gl_web/CHANGELOG.md b/mapbox_gl_web/CHANGELOG.md
index a8330bde4..854f294c6 100644
--- a/mapbox_gl_web/CHANGELOG.md
+++ b/mapbox_gl_web/CHANGELOG.md
@@ -1,5 +1,16 @@
+## 0.16.0, May 19, 2022
+* Fix type issues in query rendered features in rect [#862](https://github.com/flutter-mapbox-gl/maps/pull/862)
+* Annotation manager moved to dart [#779](https://github.com/flutter-mapbox-gl/maps/pull/779)
+* Fix issue with map disposal on web [#895](https://github.com/flutter-mapbox-gl/maps/pull/895)
+* Fix for rescale issues on web [#896](https://github.com/flutter-mapbox-gl/maps/pull/896)
+* Upgraded mapbox gl js to 2.7.0 [#889](https://github.com/flutter-mapbox-gl/maps/pull/889)
+* Add support for layer zoom limits [#934](https://github.com/flutter-mapbox-gl/maps/pull/934)
+* Implement layer filtering [#997](https://github.com/flutter-mapbox-gl/maps/pull/997)
+* Drag event types support added [#987](https://github.com/flutter-mapbox-gl/maps/pull/987)
+* Support filtering on addLayer [#1024](https://github.com/flutter-mapbox-gl/maps/pull/1024)
+
## 0.15.0, January 13, 2022
-* Callbacks added to onFeatureTapped will now also get the position (`Point`) and the location (`LatLng`) of the click passed when called [#798](https://github.com/flutter-mapbox-gl/maps/pull/798)
+* Callbacks added to onFeatureTapped will now also get the position (`Point`) and the location (`LatLng`) of the click passed when called [#798](https://github.com/flutter-mapbox-gl/maps/pull/798)
* Fix web issues with style loaded, feature tap, add promoteId, pointer change issue [#785](https://github.com/flutter-mapbox-gl/maps/pull/785)
* Add check for Dart formatting [#803](https://github.com/flutter-mapbox-gl/maps/pull/803)
* Remove unnecessary print of style height and width [#847](https://github.com/flutter-mapbox-gl/maps/pull/847)
From 734b70701dbbfb967154e0547d3474e2a1478e95 Mon Sep 17 00:00:00 2001
From: Tobrun
Date: Mon, 23 May 2022 16:42:55 +0200
Subject: [PATCH 03/22] [release] release v0.16.0 (#1053)
---
mapbox_gl_platform_interface/pubspec.yaml | 2 +-
mapbox_gl_web/pubspec.yaml | 2 +-
pubspec.yaml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/mapbox_gl_platform_interface/pubspec.yaml b/mapbox_gl_platform_interface/pubspec.yaml
index 6cbdc932b..c3eae26be 100644
--- a/mapbox_gl_platform_interface/pubspec.yaml
+++ b/mapbox_gl_platform_interface/pubspec.yaml
@@ -1,6 +1,6 @@
name: mapbox_gl_platform_interface
description: A common platform interface for the mapbox_gl plugin.
-version: 0.15.0
+version: 0.16.0
homepage: https://github.com/tobrun/flutter-mapbox-gl
dependencies:
diff --git a/mapbox_gl_web/pubspec.yaml b/mapbox_gl_web/pubspec.yaml
index e6e736718..769890992 100644
--- a/mapbox_gl_web/pubspec.yaml
+++ b/mapbox_gl_web/pubspec.yaml
@@ -1,6 +1,6 @@
name: mapbox_gl_web
description: Web platform implementation of mapbox_gl
-version: 0.15.0
+version: 0.16.0
homepage: https://github.com/tobrun/flutter-mapbox-gl
flutter:
diff --git a/pubspec.yaml b/pubspec.yaml
index d99a102e5..ea086f30b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: mapbox_gl
description: A Flutter plugin for integrating Mapbox Maps inside a Flutter application on Android, iOS and web platfroms.
-version: 0.15.0
+version: 0.16.0
homepage: https://github.com/tobrun/flutter-mapbox-gl
dependencies:
From b1479f405596a3571c76b4628fabbf9b1196fe90 Mon Sep 17 00:00:00 2001
From: Felix Mittermeier <100692288+felix-mittermeier@users.noreply.github.com>
Date: Mon, 30 May 2022 17:57:31 +0200
Subject: [PATCH 04/22] Improved map resizing (#1061)
* Improved map resizing
* Added functionality to trigger the map resize event from outside the package
* Fixed pipeline
---
.github/workflows/flutter_ci.yml | 8 +++-
README.md | 12 +++++-
example/web/index.html | 2 +-
lib/src/controller.dart | 15 +++++++
.../lib/mapbox_gl_platform_interface.dart | 1 -
.../lib/src/mapbox_gl_platform_interface.dart | 3 ++
.../lib/src/method_channel_mapbox_gl.dart | 6 +++
mapbox_gl_web/lib/mapbox_gl_web.dart | 2 +-
.../lib/src/mapbox_web_gl_platform.dart | 41 +++++++++++++++----
pubspec.lock | 15 +++++--
10 files changed, 87 insertions(+), 18 deletions(-)
diff --git a/.github/workflows/flutter_ci.yml b/.github/workflows/flutter_ci.yml
index b6707163e..2df64cee2 100644
--- a/.github/workflows/flutter_ci.yml
+++ b/.github/workflows/flutter_ci.yml
@@ -63,7 +63,9 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: '12.x'
- - uses: subosito/flutter-action@v1
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '2.10.5'
- run: flutter pub get
- name: Build example APK
run: cd example && flutter build apk
@@ -80,7 +82,9 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: '12.x'
- - uses: subosito/flutter-action@v1
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '2.10.5'
- run: flutter pub get
- name: build iOS package
run: |
diff --git a/README.md b/README.md
index ad6887d49..ebbf3b077 100644
--- a/README.md
+++ b/README.md
@@ -70,8 +70,16 @@ curl: (22) The requested URL returned error: 401 Unauthorized
Include the JavaScript and CSS files in the `` of your `index.html` file:
```
-
-
+
+
+
+
```
*Note: Look for latest version in [Mapbox GL JS documentation](https://docs.mapbox.com/mapbox-gl-js/guides/).*
diff --git a/example/web/index.html b/example/web/index.html
index 4d19c8101..f6098b72b 100644
--- a/example/web/index.html
+++ b/example/web/index.html
@@ -15,7 +15,7 @@
-
+
example
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index 70df6971a..ba49d28ef 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -257,6 +257,21 @@ class MapboxMapController extends ChangeNotifier {
notifyListeners();
}
+ /// Triggers a resize event for the map on web (ignored on Android or iOS).
+ ///
+ /// Checks first if a resize is required or if it looks like it is already correctly resized.
+ /// If it looks good, the resize call will be skipped.
+ ///
+ /// To force resize map (without any checks) have a look at forceResizeWebMap()
+ void resizeWebMap() {
+ _mapboxGlPlatform.resizeWebMap();
+ }
+
+ /// Triggers a hard map resize event on web and does not check if it is required or not.
+ void forceResizeWebMap() {
+ _mapboxGlPlatform.forceResizeWebMap();
+ }
+
/// Starts an animated change of the map camera position.
///
/// The returned [Future] completes after the change has been started on the
diff --git a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
index bf59ae3bb..62fa0b607 100644
--- a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
@@ -9,7 +9,6 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
-import 'package:meta/meta.dart' show visibleForTesting;
part 'src/annotation.dart';
part 'src/callbacks.dart';
diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
index 49cacae91..f2469e6fc 100644
--- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
@@ -58,6 +58,9 @@ abstract class MapboxGlPlatform {
Future matchMapLanguageWithDeviceDefault();
+ void resizeWebMap();
+ void forceResizeWebMap();
+
Future updateContentInsets(EdgeInsets insets, bool animated);
Future setMapLanguage(String language);
Future setTelemetryEnabled(bool enabled);
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index 38da956cf..dbaf0482d 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -678,4 +678,10 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
'geojsonFeature': jsonEncode(geojsonFeature)
});
}
+
+ @override
+ void forceResizeWebMap() {}
+
+ @override
+ void resizeWebMap() {}
}
diff --git a/mapbox_gl_web/lib/mapbox_gl_web.dart b/mapbox_gl_web/lib/mapbox_gl_web.dart
index a6296a824..bec769c43 100644
--- a/mapbox_gl_web/lib/mapbox_gl_web.dart
+++ b/mapbox_gl_web/lib/mapbox_gl_web.dart
@@ -13,7 +13,7 @@ import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
+import 'package:flutter/material.dart' hide Element;
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart';
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index 8a506b8a5..12358d8f9 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -24,6 +24,7 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
String? _navigationControlPosition;
NavigationControl? _navigationControl;
+ Timer? lastResizeObserverTimer;
@override
Widget buildView(
@@ -47,8 +48,10 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
ui.platformViewRegistry.registerViewFactory(
'plugins.flutter.io/mapbox_gl_$identifier', (int viewId) {
_mapElement = DivElement()
- ..style.width = '100%'
- ..style.height = '100%';
+ ..style.position = 'absolute'
+ ..style.top = '0'
+ ..style.bottom = '0'
+ ..style.width = '100%';
callback(viewId);
return _mapElement;
});
@@ -59,7 +62,6 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
await _addStylesheetToShadowRoot(_mapElement);
if (_creationParams.containsKey('initialCameraPosition')) {
var camera = _creationParams['initialCameraPosition'];
-
_dragEnabled = _creationParams['dragEnabled'] ?? true;
if (_creationParams.containsKey('accessToken')) {
@@ -82,16 +84,31 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
_map.on('movestart', _onCameraMoveStarted);
_map.on('move', _onCameraMove);
_map.on('moveend', _onCameraIdle);
- _map.on('resize', _onMapResize);
+ _map.on('resize', (_) => _onMapResize());
_map.on('styleimagemissing', _loadFromAssets);
if (_dragEnabled) {
_map.on('mouseup', _onMouseUp);
_map.on('mousemove', _onMouseMove);
}
+
+ _initResizeObserver();
}
Convert.interpretMapboxMapOptions(_creationParams['options'], this);
}
+ void _initResizeObserver() {
+ final resizeObserver = ResizeObserver((entries, observer) {
+ // The resize observer might be called a lot of times when the user resizes the browser window with the mouse for example.
+ // Due to the fact that the resize call is quite expensive it should not be called for every triggered event but only the last one, like "onMoveEnd".
+ // But because there is no event type for the end, there is only the option to spawn timers and cancel the previous ones if they get overwritten by a new event.
+ lastResizeObserverTimer?.cancel();
+ lastResizeObserverTimer = Timer(Duration(milliseconds: 50), () {
+ _onMapResize();
+ });
+ });
+ resizeObserver.observe(document.body as Element);
+ }
+
void _loadFromAssets(Event event) async {
final imagePath = event.id;
final ByteData bytes = await rootBundle.load(imagePath);
@@ -340,12 +357,12 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
void _onStyleLoaded(_) {
_mapReady = true;
- _map.resize();
+ _onMapResize();
onMapStyleLoadedPlatform(null);
}
- void _onMapResize(Event e) {
- Timer(Duration(microseconds: 10), () {
+ void _onMapResize() {
+ Timer(Duration(), () {
var container = _map.getContainer();
var canvas = _map.getCanvas();
var widthMismatch = canvas.clientWidth != container.clientWidth;
@@ -949,4 +966,14 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
}
}
}
+
+ @override
+ void resizeWebMap() {
+ _onMapResize();
+ }
+
+ @override
+ void forceResizeWebMap() {
+ _map.resize();
+ }
}
diff --git a/pubspec.lock b/pubspec.lock
index 4aab14388..5b21d9f78 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -16,7 +16,7 @@ packages:
source: hosted
version: "1.2.0"
collection:
- dependency: transitive
+ dependency: "direct main"
description:
name: collection
url: "https://pub.dartlang.org"
@@ -59,21 +59,28 @@ packages:
name: mapbox_gl_dart
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.0-nullsafety.0"
+ version: "0.2.1"
mapbox_gl_platform_interface:
dependency: "direct main"
description:
path: mapbox_gl_platform_interface
relative: true
source: path
- version: "0.14.0"
+ version: "0.16.1"
mapbox_gl_web:
dependency: "direct main"
description:
path: mapbox_gl_web
relative: true
source: path
- version: "0.14.0"
+ version: "0.16.1"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.3"
meta:
dependency: transitive
description:
From 3496907955cd4b442e4eb905d67e8d46692174f1 Mon Sep 17 00:00:00 2001
From: morvagergely <51244648+morvagergely@users.noreply.github.com>
Date: Tue, 31 May 2022 15:03:18 +0200
Subject: [PATCH 05/22] Add duration for animateCamera (#1066)
---
example/lib/animate_camera.dart | 1 +
ios/Classes/MapboxMapController.swift | 3 ++-
lib/src/controller.dart | 7 +++++--
.../lib/src/mapbox_gl_platform_interface.dart | 2 +-
.../lib/src/method_channel_mapbox_gl.dart | 3 ++-
mapbox_gl_web/lib/mapbox_gl_web.dart | 1 +
.../lib/src/mapbox_web_gl_platform.dart | 20 +++++++++++++++++--
7 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/example/lib/animate_camera.dart b/example/lib/animate_camera.dart
index 36f6a9c41..ec9cd55c9 100644
--- a/example/lib/animate_camera.dart
+++ b/example/lib/animate_camera.dart
@@ -79,6 +79,7 @@ class AnimateCameraState extends State {
CameraUpdate.newLatLng(
const LatLng(56.1725505, 10.1850512),
),
+ duration: Duration(seconds: 5),
)
.then((result) => print(
"mapController.animateCamera() returned $result"));
diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift
index e1d55b8ab..703b68c31 100644
--- a/ios/Classes/MapboxMapController.swift
+++ b/ios/Classes/MapboxMapController.swift
@@ -336,8 +336,9 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
animationTimingFunction: CAMediaTimingFunction(name: CAMediaTimingFunctionName
.easeInEaseOut))
result(nil)
+ } else {
+ mapView.setCamera(camera, animated: true)
}
- mapView.setCamera(camera, animated: true)
}
result(nil)
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index ba49d28ef..bb3d3e7b3 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -274,12 +274,15 @@ class MapboxMapController extends ChangeNotifier {
/// Starts an animated change of the map camera position.
///
+ /// [duration] is the amount of time, that the transition animation should take.
+ ///
/// The returned [Future] completes after the change has been started on the
/// platform side.
/// It returns true if the camera was successfully moved and false if the movement was canceled.
/// Note: this currently always returns immediately with a value of null on iOS
- Future animateCamera(CameraUpdate cameraUpdate) async {
- return _mapboxGlPlatform.animateCamera(cameraUpdate);
+ Future animateCamera(CameraUpdate cameraUpdate,
+ {Duration? duration}) async {
+ return _mapboxGlPlatform.animateCamera(cameraUpdate, duration: duration);
}
/// Instantaneously re-position the camera.
diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
index f2469e6fc..ca1f9252d 100644
--- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
@@ -51,7 +51,7 @@ abstract class MapboxGlPlatform {
OnPlatformViewCreatedCallback onPlatformViewCreated,
Set>? gestureRecognizers);
Future updateMapOptions(Map optionsUpdate);
- Future animateCamera(CameraUpdate cameraUpdate);
+ Future animateCamera(CameraUpdate cameraUpdate, {Duration? duration});
Future moveCamera(CameraUpdate cameraUpdate);
Future updateMyLocationTrackingMode(
MyLocationTrackingMode myLocationTrackingMode);
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index dbaf0482d..ea83b082a 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -211,9 +211,10 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
}
@override
- Future animateCamera(cameraUpdate) async {
+ Future animateCamera(cameraUpdate, {Duration? duration}) async {
return await _channel.invokeMethod('camera#animate', {
'cameraUpdate': cameraUpdate.toJson(),
+ 'duration': duration?.inMilliseconds,
});
}
diff --git a/mapbox_gl_web/lib/mapbox_gl_web.dart b/mapbox_gl_web/lib/mapbox_gl_web.dart
index bec769c43..70756d3be 100644
--- a/mapbox_gl_web/lib/mapbox_gl_web.dart
+++ b/mapbox_gl_web/lib/mapbox_gl_web.dart
@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:html';
// ignore: unused_import
import 'dart:js';
+import 'dart:js_util';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index 12358d8f9..ed2918556 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -194,9 +194,25 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
}
@override
- Future animateCamera(CameraUpdate cameraUpdate) async {
+ Future animateCamera(CameraUpdate cameraUpdate,
+ {Duration? duration}) async {
final cameraOptions = Convert.toCameraOptions(cameraUpdate, _map);
- _map.flyTo(cameraOptions);
+
+ final around = getProperty(cameraOptions, 'around');
+ final bearing = getProperty(cameraOptions, 'bearing');
+ final center = getProperty(cameraOptions, 'center');
+ final pitch = getProperty(cameraOptions, 'pitch');
+ final zoom = getProperty(cameraOptions, 'zoom');
+
+ _map.flyTo({
+ if (around.jsObject != null) 'around': around,
+ if (bearing != null) 'bearing': bearing,
+ if (center.jsObject != null) 'center': center,
+ if (pitch != null) 'pitch': pitch,
+ if (zoom != null) 'zoom': zoom,
+ if (duration != null) 'duration': duration.inMilliseconds,
+ });
+
return true;
}
From 019ed848e5516f414745fa8c1bb9ca567600e59c Mon Sep 17 00:00:00 2001
From: Felix Horvat
Date: Wed, 28 Sep 2022 17:30:39 +0200
Subject: [PATCH 06/22] Add makefile for formatting (#1181)
* refactor and fix ci the file
* add makefile for formatting and installing formatting tools
---
.github/workflows/flutter_ci.yml | 112 ++++++++++++++++---------------
.gitignore | 6 ++
Makefile | 13 ++++
install_formatting_tools.sh | 21 ++++++
pubspec.lock | 4 +-
5 files changed, 100 insertions(+), 56 deletions(-)
create mode 100644 Makefile
create mode 100755 install_formatting_tools.sh
diff --git a/.github/workflows/flutter_ci.yml b/.github/workflows/flutter_ci.yml
index 2df64cee2..4f16f71fd 100644
--- a/.github/workflows/flutter_ci.yml
+++ b/.github/workflows/flutter_ci.yml
@@ -8,69 +8,73 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
- - uses: actions/setup-java@v1
- with:
- java-version: '12.x'
- - uses: subosito/flutter-action@v1
- - run: flutter pub get
- - name: Lint analysis
- run: cd example && flutter analyze
-
+ - uses: actions/checkout@v1
+ - uses: actions/setup-java@v1
+ with:
+ java-version: "12.x"
+ - uses: subosito/flutter-action@v1
+ with:
+ flutter-version: "2.10.5"
+ - run: flutter pub get
+ - name: Lint analysis
+ run: cd example && flutter analyze
+
check-dart-formatting:
name: "Check Dart formatting"
runs-on: ubuntu-latest
-
+
steps:
- - uses: actions/checkout@v1
- - uses: subosito/flutter-action@v1
- - name: Check Dart formatting
- run: flutter format --set-exit-if-changed .
+ - uses: actions/checkout@v1
+ - uses: subosito/flutter-action@v1
+ - name: Check Dart formatting
+ run: flutter format --set-exit-if-changed .
check-swift-formatting:
name: "Check Swift formatting"
runs-on: ubuntu-latest
-
+
steps:
- - uses: actions/checkout@v1
- - name: get SwiftFormat
- run: wget https://github.com/nicklockwood/SwiftFormat/releases/download/0.48.18/swiftformat_linux.zip
- - run: unzip swiftformat_linux.zip
- - run: chmod +x swiftformat_linux
- - name: Check Swift formatting
- run: ./swiftformat_linux --swiftversion 4.2 --maxwidth 100 --lint ios
+ - uses: actions/checkout@v1
+ - name: get SwiftFormat
+ run: ./install_formatting_tools.sh
+ - name: Check Swift formatting
+ run: ./swiftformat --swiftversion 4.2 --maxwidth 100 --lint ios
check-java-formatting:
name: "Check Java formatting"
runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v1
- - name: get google-java-format
- run: wget https://github.com/google/google-java-format/releases/download/v1.13.0/google-java-format-1.13.0-all-deps.jar
- - run: java --version
- - name: Check Java formatting
- run: java -jar google-java-format-1.13.0-all-deps.jar --set-exit-if-changed -n $(find . -type f -name "*.java")
+ steps:
+ - uses: actions/checkout@v1
+ - name: get google-java-format
+ run: ./install_formatting_tools.sh
+ - name: Check Java formatting
+ run: >
+ java
+ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+ --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ -jar google-java-format-1.13.0-all-deps.jar --set-exit-if-changed -n $(find . -type f -name "*.java")
build-android:
environment: ANDROID_CI_DOWNLOADS_TOKEN
name: "Build Android apk"
runs-on: ubuntu-latest
-
steps:
- - uses: actions/checkout@v1
- - uses: actions/setup-java@v1
- with:
- java-version: '12.x'
- - uses: subosito/flutter-action@v2
- with:
- flutter-version: '2.10.5'
- - run: flutter pub get
- - name: Build example APK
- run: cd example && flutter build apk
- env:
- SDK_REGISTRY_TOKEN: ${{ secrets.SDK_REGISTRY_ANDROID}}
+ - uses: actions/checkout@v1
+ - uses: actions/setup-java@v1
+ with:
+ java-version: "12.x"
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: "2.10.5"
+ - run: flutter pub get
+ - name: Build example APK
+ run: cd example && flutter build apk
+ env:
+ SDK_REGISTRY_TOKEN: ${{ secrets.SDK_REGISTRY_ANDROID}}
build-iOS:
environment: ANDROID_CI_DOWNLOADS_TOKEN
@@ -81,10 +85,10 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-java@v1
with:
- java-version: '12.x'
+ java-version: "12.x"
- uses: subosito/flutter-action@v2
with:
- flutter-version: '2.10.5'
+ flutter-version: "2.10.5"
- run: flutter pub get
- name: build iOS package
run: |
@@ -102,12 +106,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
- - uses: actions/setup-java@v1
- with:
- java-version: '12.x'
- - uses: subosito/flutter-action@v1
- - run: flutter config --enable-web
- - run: flutter pub get
- - name: Build web
- run: cd example && flutter build web
+ - uses: actions/checkout@v1
+ - uses: actions/setup-java@v1
+ with:
+ java-version: "12.x"
+ - uses: subosito/flutter-action@v1
+ - run: flutter config --enable-web
+ - run: flutter pub get
+ - name: Build web
+ run: cd example && flutter build web
diff --git a/.gitignore b/.gitignore
index b7d74c826..64487b8f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -98,10 +98,15 @@ unlinked_spec.ds
**/macos/Flutter/Flutter-Debug.xcconfig
**/macos/Flutter/Flutter-Release.xcconfig
**/macos/Flutter/Flutter-Profile.xcconfig
+__MACOSX/
# Coverage
coverage/
+# Binaries
+google-java-format-1.13.0-all-deps.jar
+swiftformat
+
# Symbols
app.*.symbols
@@ -112,3 +117,4 @@ app.*.symbols
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..93faecdf7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+format:
+ java \
+ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \
+ -jar google-java-format-1.13.0-all-deps.jar -r $(shell find . -type f -name "*.java")
+ flutter format .
+ ./swiftformat --swiftversion 4.2 --maxwidth 100 ios
+
+install_formatting:
+ ./install_formatting_tools.sh
diff --git a/install_formatting_tools.sh b/install_formatting_tools.sh
new file mode 100755
index 000000000..e9aad1960
--- /dev/null
+++ b/install_formatting_tools.sh
@@ -0,0 +1,21 @@
+echo "Cleanup"
+rm swiftformat
+rm -r __MACOSX
+rm google-java-format-1.13.0-all-deps.jar
+
+if [ $(uname) = "Linux" ]
+then
+ echo "Install for Linux"
+ wget https://github.com/nicklockwood/SwiftFormat/releases/download/0.48.18/swiftformat_linux.zip
+ unzip swiftformat_linux.zip
+ rm swiftformat_linux.zip
+ mv swiftformat_linux swiftformat
+else
+ echo "Install for MAC"
+ wget https://github.com/nicklockwood/SwiftFormat/releases/download/0.48.18/swiftformat.zip
+ unzip swiftformat.zip
+ rm swiftformat.zip
+fi
+chmod +x swiftformat
+
+wget https://github.com/google/google-java-format/releases/download/v1.13.0/google-java-format-1.13.0-all-deps.jar
diff --git a/pubspec.lock b/pubspec.lock
index 5b21d9f78..23ff03728 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -66,14 +66,14 @@ packages:
path: mapbox_gl_platform_interface
relative: true
source: path
- version: "0.16.1"
+ version: "0.16.0"
mapbox_gl_web:
dependency: "direct main"
description:
path: mapbox_gl_web
relative: true
source: path
- version: "0.16.1"
+ version: "0.16.0"
material_color_utilities:
dependency: transitive
description:
From 3e490a8fc42b6b47374b3932fa49061293b709ee Mon Sep 17 00:00:00 2001
From: Hung Tran <32390731+hungtrn75@users.noreply.github.com>
Date: Wed, 28 Sep 2022 22:54:17 +0700
Subject: [PATCH 07/22] Create a static map snapshot (#1076)
* android impl
* take snapshot ios
* config for each platform interface
* take snapshot in ios
* take snap in android && example
* android hybrid composition
* ios test
* test with android hybrid composition
* Update README.md
* use JPG instead of PNG with writeToDisk option & remove unused funtions
* render example result with base64 option
* remove team ID in example
* iOS: use JPG instead of PNG
* rename funtion
* test flutter ci
* test ci
* ci: check swift formatting
* ci: test check java formatting
* ci: check java formatting
* ci: test check java formatting
* document for take snapshot feature
* revert ci config
* migration: jpeg with base64 option
* docs: web support
* feat: web support with base64 option
* ci: test github ci
* lint: ignore unnecessary_import
* ci: reverse config
---
README.md | 19 ++
android/build.gradle | 1 +
.../java/com/mapbox/mapboxgl/BitmapUtils.java | 57 ++++++
.../com/mapbox/mapboxgl/GeoJSONUtils.java | 38 ++++
.../mapbox/mapboxgl/MapboxMapController.java | 89 +++++++++-
example/.gitignore | 1 +
.../android/app/src/main/AndroidManifest.xml | 1 +
.../app/src/main/res/values/styles.xml | 3 +
example/ios/Runner.xcodeproj/project.pbxproj | 15 +-
example/ios/Runner/AppDelegate.swift | 16 +-
example/ios/Runner/Info.plist | 2 +
example/lib/generated_plugin_registrant.dart | 1 +
example/lib/line.dart | 1 +
example/lib/main.dart | 21 +--
example/lib/place_fill.dart | 1 +
example/lib/place_source.dart | 1 +
example/lib/place_symbol.dart | 1 +
example/lib/take_snapshot.dart | 165 ++++++++++++++++++
example/macos/Runner/AppDelegate.swift | 6 +-
example/macos/Runner/MainFlutterWindow.swift | 16 +-
ios/Classes/MapboxMapController.swift | 114 ++++++++++++
ios/Classes/RNMBImageUtils.swift | 26 +++
lib/mapbox_gl.dart | 1 +
lib/src/controller.dart | 10 ++
.../lib/mapbox_gl_platform_interface.dart | 2 +
.../lib/src/mapbox_gl_platform_interface.dart | 2 +
.../lib/src/method_channel_mapbox_gl.dart | 12 ++
.../lib/src/snapshot.dart | 155 ++++++++++++++++
.../lib/src/mapbox_web_gl_platform.dart | 19 ++
pubspec.lock | 10 +-
30 files changed, 754 insertions(+), 52 deletions(-)
create mode 100644 android/src/main/java/com/mapbox/mapboxgl/BitmapUtils.java
create mode 100644 android/src/main/java/com/mapbox/mapboxgl/GeoJSONUtils.java
create mode 100644 example/lib/take_snapshot.dart
create mode 100644 ios/Classes/RNMBImageUtils.swift
create mode 100644 mapbox_gl_platform_interface/lib/src/snapshot.dart
diff --git a/README.md b/README.md
index ebbf3b077..6ca9d0fc7 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@
- [Map Styles](#map-styles)
- [Offline Sideloading](#offline-sideloading)
- [Downloading Offline Regions](#downloading-offline-regions)
+ - [Create a static map snapshot](#create-a-static-map-snapshot)
- [Location features](#location-features)
- [Android](#android)
- [iOS](#ios)
@@ -206,7 +207,25 @@ An offline region is a defined region of a map that is available for use in cond
downloadOfflineRegionStream(offlineRegion, onEvent);
```
+## Create a static map snapshot
+The snapshotManager generates static raster images of the map.
+Each snapshot image depicts a portion of a map defined by an SnapshotOptions object you provide.
+
+* Call `takeSnapshot` with predefined `SnapshotOptions`
+
+```
+ final renderBox = mapKey.currentContext?.findRenderObject() as RenderBox;
+
+ final snapshotOptions = SnapshotOptions(
+ width: renderBox.size.width,
+ height: renderBox.size.height,
+ writeToDisk: true,
+ withLogo: false,
+ );
+
+ final uri = await mapController?.takeSnapshot(snapshotOptions);
+```
## Location features
### Android
diff --git a/android/build.gradle b/android/build.gradle
index 1430207bc..88f4d5bd1 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -57,6 +57,7 @@ android {
implementation "com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.12.0"
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-offline-v9:0.7.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ implementation 'com.mapbox.mapboxsdk:mapbox-sdk-turf:5.1.0'
}
compileOptions {
sourceCompatibility 1.8
diff --git a/android/src/main/java/com/mapbox/mapboxgl/BitmapUtils.java b/android/src/main/java/com/mapbox/mapboxgl/BitmapUtils.java
new file mode 100644
index 000000000..c91044e51
--- /dev/null
+++ b/android/src/main/java/com/mapbox/mapboxgl/BitmapUtils.java
@@ -0,0 +1,57 @@
+package com.mapbox.mapboxgl;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.Base64;
+import android.util.Log;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Created by nickitaliano on 10/9/17. */
+public class BitmapUtils {
+ private static final String LOG_TAG = "BitmapUtils";
+
+ public static String createTempFile(Context context, Bitmap bitmap) {
+ File tempFile = null;
+ FileOutputStream outputStream = null;
+
+ try {
+ tempFile = File.createTempFile(LOG_TAG, ".jpeg", context.getCacheDir());
+ outputStream = new FileOutputStream(tempFile);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, e.getLocalizedMessage());
+ }
+
+ if (tempFile == null) {
+ return null;
+ }
+
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
+ closeSnapshotOutputStream(outputStream);
+ return Uri.fromFile(tempFile).toString();
+ }
+
+ public static String createBase64(Bitmap bitmap) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
+ byte[] bitmapBytes = outputStream.toByteArray();
+ closeSnapshotOutputStream(outputStream);
+ String base64Prefix = "data:image/jpeg;base64,";
+ return base64Prefix + Base64.encodeToString(bitmapBytes, Base64.NO_WRAP);
+ }
+
+ private static void closeSnapshotOutputStream(OutputStream outputStream) {
+ if (outputStream == null) {
+ return;
+ }
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ Log.w(LOG_TAG, e.getLocalizedMessage());
+ }
+ }
+}
diff --git a/android/src/main/java/com/mapbox/mapboxgl/GeoJSONUtils.java b/android/src/main/java/com/mapbox/mapboxgl/GeoJSONUtils.java
new file mode 100644
index 000000000..db3b3ed51
--- /dev/null
+++ b/android/src/main/java/com/mapbox/mapboxgl/GeoJSONUtils.java
@@ -0,0 +1,38 @@
+package com.mapbox.mapboxgl;
+
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Geometry;
+import com.mapbox.geojson.GeometryCollection;
+import com.mapbox.geojson.Point;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.turf.TurfMeasurement;
+import java.util.ArrayList;
+import java.util.List;
+
+public class GeoJSONUtils {
+ public static LatLng toLatLng(Point point) {
+ if (point == null) {
+ return null;
+ }
+ return new LatLng(point.latitude(), point.longitude());
+ }
+
+ private static GeometryCollection toGeometryCollection(List features) {
+ ArrayList geometries = new ArrayList<>();
+ geometries.ensureCapacity(features.size());
+ for (Feature feature : features) {
+ geometries.add(feature.geometry());
+ }
+ return GeometryCollection.fromGeometries(geometries);
+ }
+
+ public static LatLngBounds toLatLngBounds(FeatureCollection featureCollection) {
+ List features = featureCollection.features();
+
+ double[] bbox = TurfMeasurement.bbox(toGeometryCollection(features));
+
+ return LatLngBounds.from(bbox[3], bbox[2], bbox[1], bbox[0]);
+ }
+}
diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
index f45556c9f..fe2a9f4db 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
@@ -37,6 +37,7 @@
import com.mapbox.android.telemetry.TelemetryEnabler;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
@@ -58,6 +59,8 @@
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.plugins.localization.LocalizationPlugin;
+import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
+import com.mapbox.mapboxsdk.storage.FileSource;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
@@ -85,6 +88,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
/** Controller of a single MapboxMaps MapView instance. */
@SuppressLint("MissingPermission")
@@ -108,6 +112,9 @@ final class MapboxMapController
private final float density;
private final Context context;
private final String styleStringInitial;
+ private final Set interactiveFeatureLayerIds;
+ private final Map addedFeaturesByLayer;
+ private final Map mSnapshotterMap;
private MapView mapView;
private MapboxMap mapboxMap;
private boolean trackCameraPosition = false;
@@ -124,13 +131,8 @@ final class MapboxMapController
private Style style;
private Feature draggedFeature;
private AndroidGesturesManager androidGesturesManager;
-
private LatLng dragOrigin;
private LatLng dragPrevious;
-
- private Set interactiveFeatureLayerIds;
- private Map addedFeaturesByLayer;
-
private LatLngBounds bounds = null;
Style.OnStyleLoaded onStyleLoadedCallback =
new Style.OnStyleLoaded() {
@@ -174,7 +176,7 @@ public void onStyleLoaded(@NonNull Style style) {
if (dragEnabled) {
this.androidGesturesManager = new AndroidGesturesManager(this.mapView.getContext(), false);
}
-
+ this.mSnapshotterMap = new HashMap<>();
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/mapbox_maps_" + id);
methodChannel.setMethodCallHandler(this);
}
@@ -1185,6 +1187,81 @@ public void onFailure(@NonNull Exception exception) {
result.success(null);
break;
}
+ case "snapshot#takeSnapshot":
+ {
+ FileSource.getInstance(context).activate();
+ MapSnapshotter.Options snapShotOptions =
+ new MapSnapshotter.Options(
+ (int) call.argument("width"), (int) call.argument("height"));
+
+ snapShotOptions.withLogo((boolean) call.argument("withLogo"));
+ Style.Builder styleBuilder = new Style.Builder();
+ if (call.hasArgument("styleUri")) {
+ styleBuilder.fromUri((String) call.argument("styleUri"));
+ } else if (call.hasArgument("styleJson")) {
+ styleBuilder.fromJson((String) call.argument("styleJson"));
+ } else {
+ if (style == null) {
+ result.error(
+ "STYLE IS NULL",
+ "The style is null. Has onStyleLoaded() already been invoked?",
+ null);
+ }
+ styleBuilder.fromUri(style.getUri());
+ }
+ snapShotOptions.withStyleBuilder(styleBuilder);
+ if (call.hasArgument("bounds")) {
+ FeatureCollection bounds = FeatureCollection.fromJson((String) call.argument("bounds"));
+ snapShotOptions.withRegion(GeoJSONUtils.toLatLngBounds(bounds));
+ } else if (call.hasArgument("centerCoordinate")) {
+ Feature centerPoint = Feature.fromJson((String) call.argument("centerCoordinate"));
+ CameraPosition cameraPosition =
+ new CameraPosition.Builder()
+ .target(GeoJSONUtils.toLatLng((Point) centerPoint.geometry()))
+ .tilt((double) call.argument("pitch"))
+ .bearing((double) call.argument("heading"))
+ .zoom((double) call.argument("zoomLevel"))
+ .build();
+ snapShotOptions.withCameraPosition(cameraPosition);
+ } else {
+ snapShotOptions.withRegion(mapboxMap.getProjection().getVisibleRegion().latLngBounds);
+ }
+
+ final MapSnapshotter snapshotter = new MapSnapshotter(context, snapShotOptions);
+ final String snapshotterID = UUID.randomUUID().toString();
+ mSnapshotterMap.put(snapshotterID, snapshotter);
+
+ snapshotter.start(
+ snapshot -> {
+ Bitmap bitmap = snapshot.getBitmap();
+
+ String result1;
+ if ((boolean) call.argument("writeToDisk")) {
+ result1 = BitmapUtils.createTempFile(context, bitmap);
+ } else {
+ result1 = BitmapUtils.createBase64(bitmap);
+ }
+
+ if (result1 == null) {
+ result.error(
+ "NO_RESULT",
+ "Could not generate snapshot, please check Android logs for more info.",
+ null);
+ return;
+ }
+
+ result.success(result1);
+ mSnapshotterMap.remove(snapshotterID);
+ },
+ new MapSnapshotter.ErrorHandler() {
+ @Override
+ public void onError(String error) {
+ result.error("SNAPSHOT_ERROR", error, null);
+ mSnapshotterMap.remove(snapshotterID);
+ }
+ });
+ break;
+ }
default:
result.notImplemented();
}
diff --git a/example/.gitignore b/example/.gitignore
index 47e0b4d62..92b28e840 100644
--- a/example/.gitignore
+++ b/example/.gitignore
@@ -9,6 +9,7 @@
.buildlog/
.history
.svn/
+.fvm/
# IntelliJ related
*.iml
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index bba79f17c..519763b2e 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
+
- @drawable/launch_background
+
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index c40f8995b..bfc583f29 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -381,10 +381,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/Frameworks",
- );
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -512,10 +509,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/Frameworks",
- );
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -539,10 +533,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/Frameworks",
- );
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift
index 70693e4a8..175629709 100644
--- a/example/ios/Runner/AppDelegate.swift
+++ b/example/ios/Runner/AppDelegate.swift
@@ -1,13 +1,13 @@
-import UIKit
import Flutter
+import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
}
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index 8fbf2ef03..922b790c3 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -51,5 +51,7 @@
Shows your location on the map and helps improve the map
NSLocationAlwaysUsageDescription
Shows your location on the map and helps improve the map
+ CADisableMinimumFrameDurationOnPhone
+
diff --git a/example/lib/generated_plugin_registrant.dart b/example/lib/generated_plugin_registrant.dart
index ae1e051c7..91d1beadb 100644
--- a/example/lib/generated_plugin_registrant.dart
+++ b/example/lib/generated_plugin_registrant.dart
@@ -4,6 +4,7 @@
// ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars
+// ignore_for_file: depend_on_referenced_packages
import 'package:device_info_plus_web/device_info_plus_web.dart';
import 'package:location_web/location_web.dart';
diff --git a/example/lib/line.dart b/example/lib/line.dart
index cc8e541df..1dd281b90 100644
--- a/example/lib/line.dart
+++ b/example/lib/line.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 2b6e4c899..e28938b2c 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -4,32 +4,32 @@
import 'dart:io';
+import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
-import 'package:mapbox_gl_example/custom_marker.dart';
-import 'package:mapbox_gl_example/full_map.dart';
-import 'package:mapbox_gl_example/offline_regions.dart';
-import 'package:mapbox_gl_example/place_batch.dart';
-import 'package:mapbox_gl_example/layer.dart';
-import 'package:mapbox_gl_example/sources.dart';
-import 'package:device_info_plus/device_info_plus.dart';
+import 'package:mapbox_gl/mapbox_gl.dart';
import 'animate_camera.dart';
import 'annotation_order_maps.dart';
+import 'click_annotations.dart';
+import 'custom_marker.dart';
import 'full_map.dart';
+import 'layer.dart';
import 'line.dart';
import 'local_style.dart';
import 'map_ui.dart';
import 'move_camera.dart';
-import 'click_annotations.dart';
+import 'offline_regions.dart';
import 'page.dart';
+import 'place_batch.dart';
import 'place_circle.dart';
+import 'place_fill.dart';
import 'place_source.dart';
import 'place_symbol.dart';
-import 'place_fill.dart';
import 'scrolling_map.dart';
-import 'package:mapbox_gl/mapbox_gl.dart';
+import 'sources.dart';
+import 'take_snapshot.dart';
final List _allPages = [
MapUiPage(),
@@ -48,6 +48,7 @@ final List _allPages = [
AnnotationOrderPage(),
CustomMarkerPage(),
BatchAddPage(),
+ TakeSnapPage(),
ClickAnnotationPage(),
Sources()
];
diff --git a/example/lib/place_fill.dart b/example/lib/place_fill.dart
index 5b6420968..b323307d3 100644
--- a/example/lib/place_fill.dart
+++ b/example/lib/place_fill.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/place_source.dart b/example/lib/place_source.dart
index 426a7760d..10a1b45c2 100644
--- a/example/lib/place_source.dart
+++ b/example/lib/place_source.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/place_symbol.dart b/example/lib/place_symbol.dart
index 2edc59051..055d27c9e 100644
--- a/example/lib/place_symbol.dart
+++ b/example/lib/place_symbol.dart
@@ -5,6 +5,7 @@
import 'dart:async'; // ignore: unnecessary_import
import 'dart:core';
import 'dart:math';
+// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/take_snapshot.dart b/example/lib/take_snapshot.dart
new file mode 100644
index 000000000..49288a9b0
--- /dev/null
+++ b/example/lib/take_snapshot.dart
@@ -0,0 +1,165 @@
+import 'dart:convert';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:mapbox_gl/mapbox_gl.dart';
+
+import 'main.dart';
+import 'page.dart';
+
+class TakeSnapPage extends ExamplePage {
+ TakeSnapPage() : super(const Icon(Icons.camera_alt), 'Take snapshot');
+
+ @override
+ Widget build(BuildContext context) {
+ return const TakeSnapshot();
+ }
+}
+
+class TakeSnapshot extends StatefulWidget {
+ const TakeSnapshot();
+
+ @override
+ State createState() => FullMapState();
+}
+
+class FullMapState extends State {
+ FullMapState();
+
+ MapboxMapController? mapController;
+ final mapKey = GlobalKey();
+ String? snapshotResult;
+
+ void _onMapCreated(MapboxMapController controller) {
+ mapController = controller;
+ }
+
+ void _onTakeSnapshot([bool writeToDisk = true]) async {
+ final renderBox = mapKey.currentContext?.findRenderObject() as RenderBox;
+
+ final snapshotOptions = SnapshotOptions(
+ width: renderBox.size.width,
+ height: renderBox.size.height,
+ writeToDisk: writeToDisk,
+ withLogo: false,
+ );
+ final result = await mapController?.takeSnapshot(snapshotOptions);
+ debugPrint("result: $result");
+ _setResult(result);
+ }
+
+ void _onTakeSnapshotWithBounds() async {
+ final renderBox = mapKey.currentContext?.findRenderObject() as RenderBox;
+ final bounds = await mapController?.getVisibleRegion();
+
+ final snapshotOptions = SnapshotOptions(
+ width: renderBox.size.width,
+ height: renderBox.size.height,
+ writeToDisk: true,
+ withLogo: false,
+ bounds: bounds,
+ );
+ final uri = await mapController?.takeSnapshot(snapshotOptions);
+
+ _setResult(uri);
+ }
+
+ void _onTakeSnapshotWithCameraPosition() async {
+ final renderBox = mapKey.currentContext?.findRenderObject() as RenderBox;
+
+ final snapshotOptions = SnapshotOptions(
+ width: renderBox.size.width,
+ height: renderBox.size.height,
+ writeToDisk: true,
+ withLogo: false,
+ centerCoordinate: LatLng(40.79796, -74.126410),
+ zoomLevel: 12,
+ pitch: 30,
+ heading: 20,
+ );
+ final uri = await mapController?.takeSnapshot(snapshotOptions);
+ _setResult(uri);
+ }
+
+ void _setResult(String? result) {
+ if (result != null) {
+ setState(() {
+ snapshotResult = result.replaceAll("file:", "");
+ });
+ }
+ }
+
+ Uint8List convertBase64Image(String base64String) {
+ return Base64Decoder().convert(base64String.split(',').last);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final height = MediaQuery.of(context).size.height;
+ return Column(
+ children: [
+ Expanded(
+ child: MapboxMap(
+ key: mapKey,
+ accessToken: MapsDemo.ACCESS_TOKEN,
+ onMapCreated: _onMapCreated,
+ initialCameraPosition:
+ const CameraPosition(target: LatLng(0.0, 0.0)),
+ myLocationEnabled: true,
+ styleString: MapboxStyles.SATELLITE,
+ ),
+ ),
+ const SizedBox(
+ height: 5,
+ ),
+ Container(
+ height: height * 0.4,
+ child: Column(
+ children: [
+ Wrap(
+ spacing: 10,
+ alignment: WrapAlignment.center,
+ children: [
+ ElevatedButton(
+ onPressed: _onTakeSnapshot,
+ child: Text("Take Snap"),
+ ),
+ ElevatedButton(
+ onPressed: _onTakeSnapshotWithBounds,
+ child: Text("With Bounds"),
+ ),
+ ElevatedButton(
+ onPressed: _onTakeSnapshotWithCameraPosition,
+ child: Text("With Camera Position"),
+ ),
+ ElevatedButton(
+ onPressed: () => _onTakeSnapshot(false),
+ child: Text("With Base64"),
+ ),
+ ],
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ if (snapshotResult != null)
+ Container(
+ decoration: BoxDecoration(border: Border.all()),
+ child: snapshotResult!.contains("base64")
+ ? Image.memory(
+ convertBase64Image(snapshotResult!),
+ gaplessPlayback: true,
+ height: height * 0.20,
+ )
+ : Image.file(
+ File(snapshotResult!),
+ height: height * 0.20,
+ ),
+ ),
+ ],
+ ),
+ )
+ ],
+ );
+ }
+}
diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift
index d53ef6437..8f3dd47c6 100644
--- a/example/macos/Runner/AppDelegate.swift
+++ b/example/macos/Runner/AppDelegate.swift
@@ -3,7 +3,7 @@ import FlutterMacOS
@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
- override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
- return true
- }
+ override func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
+ return true
+ }
}
diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift
index 2722837ec..decbd0eb1 100644
--- a/example/macos/Runner/MainFlutterWindow.swift
+++ b/example/macos/Runner/MainFlutterWindow.swift
@@ -2,14 +2,14 @@ import Cocoa
import FlutterMacOS
class MainFlutterWindow: NSWindow {
- override func awakeFromNib() {
- let flutterViewController = FlutterViewController.init()
- let windowFrame = self.frame
- self.contentViewController = flutterViewController
- self.setFrame(windowFrame, display: true)
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController()
+ let windowFrame = frame
+ contentViewController = flutterViewController
+ setFrame(windowFrame, display: true)
- RegisterGeneratedPlugins(registry: flutterViewController)
+ RegisterGeneratedPlugins(registry: flutterViewController)
- super.awakeFromNib()
- }
+ super.awakeFromNib()
+ }
}
diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift
index 703b68c31..7fa523e0e 100644
--- a/ios/Classes/MapboxMapController.swift
+++ b/ios/Classes/MapboxMapController.swift
@@ -697,6 +697,120 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
guard let geojson = arguments["geojsonFeature"] as? String else { return }
setFeature(sourceId: sourceId, geojsonFeature: geojson)
result(nil)
+ case "snapshot#takeSnapshot":
+ guard let arguments = methodCall.arguments as? [String: Any] else { return }
+ let camera = MGLMapCamera()
+
+ guard let pitch = arguments["pitch"] as? NSNumber else {
+ result(FlutterError(code: "invalidArgument", message: "pitch is not a number",
+ details: nil))
+ return
+ }
+ camera.pitch = pitch.doubleValue
+
+ guard let heading = arguments["heading"] as? NSNumber else {
+ result(FlutterError(code: "invalidArgument", message: "heading is not a number",
+ details: nil))
+ return
+ }
+ camera.heading = heading.doubleValue
+
+ camera.centerCoordinate = mapView.centerCoordinate
+ if arguments["centerCoordinate"] != nil {
+ guard let centerCoordinate = arguments["centerCoordinate"] as? [NSNumber] else {
+ result(FlutterError(
+ code: "invalidArgument",
+ message: "centerCoordinate is not a number list",
+ details: nil
+ ))
+ return
+ }
+ camera.centerCoordinate = CLLocationCoordinate2D(
+ latitude: centerCoordinate[0].doubleValue,
+ longitude: centerCoordinate[1].doubleValue
+ )
+ }
+
+ guard let width = arguments["width"] as? NSNumber else {
+ result(FlutterError(code: "invalidArgument", message: "width is not a number",
+ details: nil))
+ return
+ }
+ guard let height = arguments["height"] as? NSNumber else {
+ result(FlutterError(code: "invalidArgument", message: "height is not a number",
+ details: nil))
+ return
+ }
+
+ let size = CGSize(width: width.doubleValue, height: height.doubleValue)
+
+ var styleURL: URL = mapView.styleURL
+ if arguments["styleUri"] != nil {
+ guard let styleUri = arguments["styleUri"] as? String else {
+ result(
+ FlutterError(code: "invalidArgument", message: "styleUri is not a string",
+ details: nil)
+ )
+ return
+ }
+ styleURL = URL(string: styleUri)!
+ }
+
+ let snapshotOptions: MGLMapSnapshotOptions = .init(
+ styleURL: styleURL,
+ camera: camera,
+ size: size
+ )
+
+ snapshotOptions.zoomLevel = mapView.zoomLevel
+ if arguments["zoomLevel"] != nil {
+ guard let zoomLevel = arguments["zoomLevel"] as? NSNumber else {
+ result(FlutterError(code: "invalidArgument",
+ message: "zoomLevel is not a number", details: nil))
+ return
+ }
+ snapshotOptions.zoomLevel = zoomLevel.doubleValue
+ }
+
+ if arguments["bounds"] != nil {
+ guard let bounds = arguments["bounds"] as? [[NSNumber]] else {
+ result(FlutterError(code: "invalidArgument",
+ message: "bounds is not a number list", details: nil))
+ return
+ }
+ let sw = bounds[0]
+ let ne = bounds[1]
+ snapshotOptions.coordinateBounds = MGLCoordinateBounds(
+ sw: CLLocationCoordinate2D(latitude: sw[0].doubleValue,
+ longitude: sw[1].doubleValue),
+ ne: CLLocationCoordinate2D(
+ latitude: ne[0].doubleValue,
+ longitude: ne[1].doubleValue
+ )
+ )
+ }
+
+ let snapshotter: MGLMapSnapshotter? = MGLMapSnapshotter(options: snapshotOptions)
+
+ snapshotter?.start { snapshot, error in
+ if error != nil {
+ result(FlutterError(
+ code: "canCreateSnapshot",
+ message: error?.localizedDescription,
+ details: error.debugDescription
+ ))
+ } else if let image = snapshot?.image {
+ guard let writeToDisk = arguments["writeToDisk"] as? NSNumber else {
+ result(FlutterError(code: "invalidArgument",
+ message: "writeToDisk is not a boolean", details: nil))
+ return
+ }
+
+ let value = writeToDisk.boolValue ? RNMBImageUtils
+ .createTempFile(image) : RNMBImageUtils.createBase64(image)
+ result(value.absoluteString)
+ }
+ }
default:
result(FlutterMethodNotImplemented)
}
diff --git a/ios/Classes/RNMBImageUtils.swift b/ios/Classes/RNMBImageUtils.swift
new file mode 100644
index 000000000..1ecd74176
--- /dev/null
+++ b/ios/Classes/RNMBImageUtils.swift
@@ -0,0 +1,26 @@
+//
+// RNMBImageUtils.swift
+// mapbox_gl
+//
+// Created by mac on 30/05/2022.
+//
+
+enum RNMBImageUtils {
+ static func createTempFile(_ image: UIImage) -> URL {
+ let fileID = UUID().uuidString
+ let pathComponent = "Documents/rctmgl-snapshot-\(fileID).jpeg"
+
+ let filePath = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent(pathComponent)
+
+ let data = image.jpegData(compressionQuality: 1.0)
+ try! data?.write(to: filePath, options: [.atomic])
+ return filePath
+ }
+
+ static func createBase64(_ image: UIImage) -> URL {
+ let data = image.jpegData(compressionQuality: 1.0)
+ let b64string: String = data!.base64EncodedString(options: [.endLineWithCarriageReturn])
+ let result = "data:image/jpeg;base64,\(b64string)"
+ return URL(string: result)!
+ }
+}
diff --git a/lib/mapbox_gl.dart b/lib/mapbox_gl.dart
index 0c836a3e5..ea45970d3 100644
--- a/lib/mapbox_gl.dart
+++ b/lib/mapbox_gl.dart
@@ -41,6 +41,7 @@ export 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart'
LineOptions,
Fill,
FillOptions,
+ SnapshotOptions,
SourceProperties,
RasterSourceProperties,
VectorSourceProperties,
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index bb3d3e7b3..3a5257cd3 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -1243,6 +1243,16 @@ class MapboxMapController extends ChangeNotifier {
}
}
+ /// Generates static raster images of the map. Each snapshot image depicts a portion of a map defined by an [SnapshotOptions] object you provide
+ /// Android/iOS: Return snapshot uri in app specific cache storage or base64 string
+ /// Web: Return base64 string with current camera posision of [MapboxMap]
+ ///
+ /// Default will return snapshot uri in Android and iOS
+ /// If you want base64 value, you must set writeToDisk option to False
+ Future takeSnapshot(SnapshotOptions snapshotOptions) async {
+ return _mapboxGlPlatform.takeSnapshot(snapshotOptions);
+ }
+
@override
void dispose() {
super.dispose();
diff --git a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
index 62fa0b607..5281f1f0d 100644
--- a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
@@ -3,6 +3,7 @@ library mapbox_gl_platform_interface;
import 'dart:async';
import 'dart:convert';
import 'dart:math';
+import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
@@ -20,5 +21,6 @@ part 'src/method_channel_mapbox_gl.dart';
part 'src/symbol.dart';
part 'src/fill.dart';
part 'src/ui.dart';
+part 'src/snapshot.dart';
part 'src/mapbox_gl_platform_interface.dart';
part 'src/source_properties.dart';
diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
index ca1f9252d..a86ae1c57 100644
--- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
@@ -161,6 +161,8 @@ abstract class MapboxGlPlatform {
Future addSource(String sourceId, SourceProperties properties);
+ Future takeSnapshot(SnapshotOptions snapshotOptions);
+
@mustCallSuper
void dispose() {
// clear all callbacks to avoid cyclic refs
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index ea83b082a..63a080e46 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -685,4 +685,16 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
@override
void resizeWebMap() {}
+
+ @override
+ Future takeSnapshot(SnapshotOptions snapshotOptions) async {
+ try {
+ debugPrint("${snapshotOptions.toJson()}");
+ var uri = await _channel.invokeMethod(
+ 'snapshot#takeSnapshot', snapshotOptions.toJson());
+ return uri;
+ } on PlatformException catch (e) {
+ return new Future.error(e);
+ }
+ }
}
diff --git a/mapbox_gl_platform_interface/lib/src/snapshot.dart b/mapbox_gl_platform_interface/lib/src/snapshot.dart
new file mode 100644
index 000000000..679770a57
--- /dev/null
+++ b/mapbox_gl_platform_interface/lib/src/snapshot.dart
@@ -0,0 +1,155 @@
+part of mapbox_gl_platform_interface;
+
+/// Set of options for taking map snapshot
+class SnapshotOptions {
+ /// Dimensions of the snapshot
+ /// The width of the image
+ final double width;
+
+ /// Dimensions of the snapshot
+ /// The height of the image
+ final double height;
+
+ /// If you want to take snapshot with camera position option
+ ///
+ /// Current center coordinate of camera position
+ final LatLng? centerCoordinate;
+
+ /// The coordinate rectangle that encompasses the bounds to capture. This is applied after the camera position
+ final LatLngBounds? bounds;
+
+ /// If you want to take snapshot with camera position option
+ ///
+ /// Zoom level of camera position
+ final double? zoomLevel;
+
+ /// If you want to take snapshot with camera position option
+ ///
+ /// Pitch toward the horizon measured in degrees, with 0 degrees resulting in a two-dimensional map
+ final double? pitch;
+
+ /// If you want to take snapshot with camera position option
+ ///
+ /// Heading measured in degrees clockwise from true north
+ final double? heading;
+
+ /// URL of the map style to snapshot. The URL may be a full HTTP or HTTPS URL, a Mapbox style URL
+ final String? styleUri;
+
+ /// StyleJson of the map style to snapshot
+ final String? styleJson;
+
+ /// Android Only: The flag indicating to show the Mapbox logo
+ final bool withLogo;
+
+ /// True: Save snapshot in cache and return path
+ /// False: Return base64 value
+ final bool writeToDisk;
+
+ ///The [width] and [height] arguments must not be null
+ SnapshotOptions(
+ {required this.width,
+ required this.height,
+ this.centerCoordinate,
+ this.bounds,
+ this.zoomLevel,
+ double? pitch,
+ double? heading,
+ this.styleUri,
+ this.styleJson,
+ bool? withLogo,
+ bool? writeToDisk})
+ : this.withLogo = withLogo ?? false,
+ this.writeToDisk = writeToDisk ?? true,
+ this.pitch = pitch ?? 0,
+ this.heading = heading ?? 0;
+
+ Map toJson() {
+ final Map json = {};
+
+ void addIfPresent(String fieldName, dynamic value) {
+ if (value != null) {
+ json[fieldName] = value;
+ }
+ }
+
+ addIfPresent('width', Platform.isAndroid ? width.toInt() : width);
+ addIfPresent('height', Platform.isAndroid ? height.toInt() : height);
+
+ if (bounds != null) {
+ if (Platform.isAndroid) {
+ final featureCollection = {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ bounds!.northeast.longitude,
+ bounds!.northeast.latitude
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ bounds!.southwest.longitude,
+ bounds!.southwest.latitude
+ ]
+ }
+ }
+ ]
+ };
+ addIfPresent("bounds", featureCollection.toString());
+ } else {
+ final list = [
+ [
+ bounds!.southwest.latitude,
+ bounds!.southwest.longitude,
+ ],
+ [
+ bounds!.northeast.latitude,
+ bounds!.northeast.longitude,
+ ]
+ ];
+ addIfPresent("bounds", list);
+ }
+ }
+ if (centerCoordinate != null && zoomLevel != null) {
+ if (Platform.isAndroid) {
+ final feature = {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ centerCoordinate!.longitude,
+ centerCoordinate!.latitude
+ ]
+ }
+ };
+ addIfPresent('centerCoordinate', feature.toString());
+ } else {
+ final list = [
+ centerCoordinate!.latitude,
+ centerCoordinate!.longitude,
+ ];
+ addIfPresent('centerCoordinate', list);
+ }
+
+ addIfPresent('zoomLevel', zoomLevel);
+ }
+ addIfPresent('pitch', pitch);
+ addIfPresent('heading', heading);
+ addIfPresent('styleUri', styleUri);
+ addIfPresent('styleJson', styleJson);
+ addIfPresent('withLogo', withLogo);
+ addIfPresent('writeToDisk', writeToDisk);
+ return json;
+ }
+}
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index ed2918556..1bd6e5244 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -75,6 +75,7 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
zoom: camera['zoom'],
bearing: camera['bearing'],
pitch: camera['tilt'],
+ preserveDrawingBuffer: true,
),
);
_map.on('load', _onStyleLoaded);
@@ -983,6 +984,24 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
}
}
+ @override
+ Future takeSnapshot(SnapshotOptions snapshotOptions) async {
+ if (snapshotOptions.styleUri != null || snapshotOptions.styleJson != null) {
+ throw UnsupportedError("style option is not supported");
+ }
+ if (snapshotOptions.bounds != null) {
+ throw UnsupportedError("bounds option is not supported");
+ }
+ if (snapshotOptions.centerCoordinate != null ||
+ snapshotOptions.zoomLevel != null ||
+ snapshotOptions.pitch != 0 ||
+ snapshotOptions.heading != 0) {
+ throw UnsupportedError("camera posision option is not supported");
+ }
+ final base64String = await _map.getCanvas().toDataUrl('image/jpeg');
+ return base64String;
+ }
+
@override
void resizeWebMap() {
_onMapResize();
diff --git a/pubspec.lock b/pubspec.lock
index 23ff03728..194fe4780 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -21,7 +21,7 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
- version: "1.15.0"
+ version: "1.16.0"
crypto:
dependency: transitive
description:
@@ -52,7 +52,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
- version: "0.6.3"
+ version: "0.6.4"
mapbox_gl_dart:
dependency: transitive
description:
@@ -80,7 +80,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
- version: "0.1.3"
+ version: "0.1.4"
meta:
dependency: transitive
description:
@@ -120,7 +120,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
xml:
dependency: transitive
description:
@@ -129,5 +129,5 @@ packages:
source: hosted
version: "5.1.0"
sdks:
- dart: ">=2.14.0 <3.0.0"
+ dart: ">=2.17.0-0 <3.0.0"
flutter: ">=2.0.0"
From 2ebb7a53ccfc77a852becba1260146902b7fb835 Mon Sep 17 00:00:00 2001
From: Simon Irmancnik
Date: Mon, 10 Oct 2022 12:09:34 +0200
Subject: [PATCH 08/22] Workaround for disposal crash with flutter3 (#1172)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Ugly workaround for disposal crash with flutter3
* Fixed linter errors
* Fixed formatting issues
* Parameters for hybrid composition and delayed disposal
* Fixing bad merge
* Added useDelayedDisposal note to README
* Warning message on using useDelayedDisposal
* Fixed linter errors for flutter 2.10
* Fixed formatting
Co-authored-by: Simon Irmančnik
---
README.md | 3 +
example/lib/line.dart | 1 -
example/lib/place_fill.dart | 1 -
example/lib/place_source.dart | 1 -
example/lib/place_symbol.dart | 1 -
lib/src/mapbox_map.dart | 13 +-
.../lib/mapbox_gl_platform_interface.dart | 1 +
.../lib/src/method_channel_mapbox_gl.dart | 45 ++-
.../lib/src/view_wrappers.dart | 299 ++++++++++++++++++
9 files changed, 350 insertions(+), 15 deletions(-)
create mode 100644 mapbox_gl_platform_interface/lib/src/view_wrappers.dart
diff --git a/README.md b/README.md
index 6ca9d0fc7..b92caa2d6 100644
--- a/README.md
+++ b/README.md
@@ -251,6 +251,9 @@ xml ...
[Recommended](https://docs.mapbox.com/help/tutorials/first-steps-ios-sdk/#display-the-users-location) explanation about "Shows your location on the map and helps improve the map".
+## Flutter 3.x.x issues and experimental workarounds
+Since Flutter 3.x.x was introduced, it exposed some race conditions resulting in occasional crashes upon map disposal. The parameter `useDelayedDisposal` was introduced as a workaround for this issue until Flutter and/or Mapbox fix this issue properly. Use with caution - this is not yet production ready since several users still report crashes after using this workaround.
+
## Running the example code
See the [documentation about this topic](doc/RUNNING_EXAMPLE_CODE.md)
diff --git a/example/lib/line.dart b/example/lib/line.dart
index 1dd281b90..cc8e541df 100644
--- a/example/lib/line.dart
+++ b/example/lib/line.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
-// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/place_fill.dart b/example/lib/place_fill.dart
index b323307d3..5b6420968 100644
--- a/example/lib/place_fill.dart
+++ b/example/lib/place_fill.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
-// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/place_source.dart b/example/lib/place_source.dart
index 10a1b45c2..426a7760d 100644
--- a/example/lib/place_source.dart
+++ b/example/lib/place_source.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
-// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/example/lib/place_symbol.dart b/example/lib/place_symbol.dart
index 055d27c9e..2edc59051 100644
--- a/example/lib/place_symbol.dart
+++ b/example/lib/place_symbol.dart
@@ -5,7 +5,6 @@
import 'dart:async'; // ignore: unnecessary_import
import 'dart:core';
import 'dart:math';
-// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart
index ffd86c02e..34b380acc 100644
--- a/lib/src/mapbox_map.dart
+++ b/lib/src/mapbox_map.dart
@@ -55,6 +55,8 @@ class MapboxMap extends StatefulWidget {
AnnotationType.line,
AnnotationType.circle,
],
+ this.useDelayedDisposal,
+ this.useHybridCompositionOverride,
}) : assert(annotationOrder.length <= 4),
assert(annotationConsumeTapEvents.length > 0),
super(key: key);
@@ -222,6 +224,13 @@ class MapboxMap extends StatefulWidget {
/// * All fade/transition animations have completed
final OnMapIdleCallback? onMapIdle;
+ /// Use delayed disposal of Android View Controller to avoid flutter 3.x.x crashes
+ /// Use with caution - this is not yet production ready since several users still report crashes after using this workaround
+ final bool? useDelayedDisposal;
+
+ /// Override hybrid mode per map instance
+ final bool? useHybridCompositionOverride;
+
/// Set `MapboxMap.useHybridComposition` to `false` in order use Virtual-Display
/// (better for Android 9 and below but may result in errors on Android 12)
/// or leave it `true` (default) to use Hybrid composition (Slower on Android 9 and below).
@@ -251,7 +260,9 @@ class _MapboxMapState extends State {
'options': _MapboxMapOptions.fromWidget(widget).toMap(),
'accessToken': widget.accessToken,
'onAttributionClickOverride': widget.onAttributionClick != null,
- 'dragEnabled': widget.dragEnabled
+ 'dragEnabled': widget.dragEnabled,
+ 'useDelayedDisposal': widget.useDelayedDisposal,
+ 'useHybridCompositionOverride': widget.useHybridCompositionOverride,
};
return _mapboxGlPlatform.buildView(
creationParams, onPlatformViewCreated, widget.gestureRecognizers);
diff --git a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
index 5281f1f0d..748773d4e 100644
--- a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
@@ -11,6 +11,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
+part 'src/view_wrappers.dart';
part 'src/annotation.dart';
part 'src/callbacks.dart';
part 'src/camera.dart';
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index 63a080e46..a04925003 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -141,7 +141,12 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
OnPlatformViewCreatedCallback onPlatformViewCreated,
Set>? gestureRecognizers) {
if (defaultTargetPlatform == TargetPlatform.android) {
- if (useHybridComposition) {
+ final useDelayedDisposalParam =
+ (creationParams['useDelayedDisposal'] ?? false) as bool;
+ final useHybridCompositionParam =
+ (creationParams['useHybridCompositionOverride'] ??
+ useHybridComposition) as bool;
+ if (useHybridCompositionParam) {
return PlatformViewLink(
viewType: 'plugins.flutter.io/mapbox_gl',
surfaceFactory: (
@@ -156,15 +161,26 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
- final SurfaceAndroidViewController controller =
- PlatformViewsService.initSurfaceAndroidView(
- id: params.id,
- viewType: 'plugins.flutter.io/mapbox_gl',
- layoutDirection: TextDirection.ltr,
- creationParams: creationParams,
- creationParamsCodec: const StandardMessageCodec(),
- onFocus: () => params.onFocusChanged(true),
- );
+ late AndroidViewController controller;
+ if (useDelayedDisposalParam) {
+ controller = WrappedPlatformViewsService.initAndroidView(
+ id: params.id,
+ viewType: 'plugins.flutter.io/mapbox_gl',
+ layoutDirection: TextDirection.ltr,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ onFocus: () => params.onFocusChanged(true),
+ );
+ } else {
+ controller = PlatformViewsService.initAndroidView(
+ id: params.id,
+ viewType: 'plugins.flutter.io/mapbox_gl',
+ layoutDirection: TextDirection.ltr,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ onFocus: () => params.onFocusChanged(true),
+ );
+ }
controller.addOnPlatformViewCreatedListener(
params.onPlatformViewCreated,
);
@@ -177,6 +193,15 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
},
);
} else {
+ if (useDelayedDisposalParam) {
+ return AndroidViewWithWrappedController(
+ viewType: 'plugins.flutter.io/mapbox_gl',
+ onPlatformViewCreated: onPlatformViewCreated,
+ gestureRecognizers: gestureRecognizers,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ );
+ }
return AndroidView(
viewType: 'plugins.flutter.io/mapbox_gl',
onPlatformViewCreated: onPlatformViewCreated,
diff --git a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
new file mode 100644
index 000000000..11efef517
--- /dev/null
+++ b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
@@ -0,0 +1,299 @@
+part of mapbox_gl_platform_interface;
+
+/// This file wrapps AndroidViewController classes in order to delay disposal process.
+/// It is an workaround for flutter 3, where resourses get disposed quicker than before, while Mapbox behaves badly
+/// and tries to access those resources after they had been disposed, resulting in a native crash.
+
+class WrappedPlatformViewsService {
+ static AndroidViewController initAndroidView({
+ required int id,
+ required String viewType,
+ required TextDirection layoutDirection,
+ dynamic creationParams,
+ MessageCodec? creationParamsCodec,
+ VoidCallback? onFocus,
+ }) {
+ final view = PlatformViewsService.initAndroidView(
+ id: id,
+ viewType: viewType,
+ layoutDirection: layoutDirection,
+ creationParams: creationParams,
+ creationParamsCodec: creationParamsCodec,
+ onFocus: onFocus,
+ );
+ return TextureAndroidViewControllerWrapper(
+ view as TextureAndroidViewController);
+ }
+}
+
+class TextureAndroidViewControllerWrapper
+ implements TextureAndroidViewController {
+ TextureAndroidViewControllerWrapper(this._controller);
+
+ final TextureAndroidViewController _controller;
+
+ @override
+ PointTransformer get pointTransformer => _controller.pointTransformer;
+ set pointTransformer(PointTransformer transformer) =>
+ _controller.pointTransformer = transformer;
+
+ @override
+ void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) =>
+ _controller.addOnPlatformViewCreatedListener(listener);
+
+ @override
+ bool get awaitingCreation => _controller.awaitingCreation;
+
+ @override
+ Future clearFocus() => _controller.clearFocus();
+
+ @override
+ Future create({Size? size}) => _controller.create(size: size);
+
+ @override
+ // ignore: invalid_use_of_visible_for_testing_member
+ List get createdCallbacks =>
+ _controller.createdCallbacks;
+
+ @override
+ Future dispatchPointerEvent(PointerEvent event) =>
+ _controller.dispatchPointerEvent(event);
+
+ @override
+ //! workaround for flutter 3.0
+ Future dispose() {
+ //? instead of this
+ // _controller.dispose();
+ //? we do this
+ unawaited(Future.delayed(Duration(seconds: 5), _controller.dispose));
+ return Future(() {});
+ }
+
+ @override
+ bool get isCreated => _controller.isCreated;
+
+ @override
+ void removeOnPlatformViewCreatedListener(
+ PlatformViewCreatedCallback listener) =>
+ _controller.removeOnPlatformViewCreatedListener(listener);
+
+ @override
+ Future sendMotionEvent(AndroidMotionEvent event) =>
+ _controller.sendMotionEvent(event);
+
+ @override
+ Future setLayoutDirection(TextDirection layoutDirection) =>
+ _controller.setLayoutDirection(layoutDirection);
+
+ @override
+ Future setOffset(Offset off) => _controller.setOffset(off);
+
+ @override
+ Future setSize(Size size) => _controller.setSize(size);
+
+ @override
+ int? get textureId => _controller.textureId;
+
+ @override
+ int get viewId => _controller.viewId;
+}
+
+class AndroidViewWithWrappedController extends StatefulWidget {
+ const AndroidViewWithWrappedController({
+ Key? key,
+ required this.viewType,
+ this.onPlatformViewCreated,
+ this.hitTestBehavior = PlatformViewHitTestBehavior.opaque,
+ this.layoutDirection,
+ this.gestureRecognizers,
+ this.creationParams,
+ this.creationParamsCodec,
+ this.clipBehavior = Clip.hardEdge,
+ }) : assert(viewType != null),
+ assert(hitTestBehavior != null),
+ assert(creationParams == null || creationParamsCodec != null),
+ assert(clipBehavior != null),
+ super(key: key);
+
+ final String viewType;
+ final PlatformViewCreatedCallback? onPlatformViewCreated;
+ final PlatformViewHitTestBehavior hitTestBehavior;
+ final TextDirection? layoutDirection;
+ final Set>? gestureRecognizers;
+ final dynamic creationParams;
+ final MessageCodec? creationParamsCodec;
+ final Clip clipBehavior;
+
+ @override
+ State createState() =>
+ _AndroidViewWithWrappedControllerState();
+}
+
+class _AndroidViewWithWrappedControllerState
+ extends State {
+ int? _id;
+ late AndroidViewController _controller;
+ TextDirection? _layoutDirection;
+ bool _initialized = false;
+ FocusNode? _focusNode;
+
+ static final Set> _emptyRecognizersSet =
+ >{};
+
+ @override
+ Widget build(BuildContext context) {
+ return Focus(
+ focusNode: _focusNode,
+ onFocusChange: _onFocusChange,
+ child: _CopyPastedAndroidPlatformView(
+ controller: _controller,
+ hitTestBehavior: widget.hitTestBehavior,
+ gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet,
+ clipBehavior: widget.clipBehavior,
+ ),
+ );
+ }
+
+ void _initializeOnce() {
+ if (_initialized) {
+ return;
+ }
+ _initialized = true;
+ _createNewAndroidView();
+ _focusNode = FocusNode(debugLabel: 'AndroidView(id: $_id)');
+ }
+
+ @override
+ void didChangeDependencies() {
+ super.didChangeDependencies();
+ final TextDirection newLayoutDirection = _findLayoutDirection();
+ final bool didChangeLayoutDirection =
+ _layoutDirection != newLayoutDirection;
+ _layoutDirection = newLayoutDirection;
+
+ _initializeOnce();
+ if (didChangeLayoutDirection) {
+ // The native view will update asynchronously, in the meantime we don't want
+ // to block the framework. (so this is intentionally not awaiting).
+ _controller.setLayoutDirection(_layoutDirection!);
+ }
+ }
+
+ @override
+ void didUpdateWidget(AndroidViewWithWrappedController oldWidget) {
+ super.didUpdateWidget(oldWidget);
+
+ final TextDirection newLayoutDirection = _findLayoutDirection();
+ final bool didChangeLayoutDirection =
+ _layoutDirection != newLayoutDirection;
+ _layoutDirection = newLayoutDirection;
+
+ if (widget.viewType != oldWidget.viewType) {
+ _controller.dispose();
+ _createNewAndroidView();
+ return;
+ }
+
+ if (didChangeLayoutDirection) {
+ _controller.setLayoutDirection(_layoutDirection!);
+ }
+ }
+
+ TextDirection _findLayoutDirection() {
+ assert(
+ widget.layoutDirection != null || debugCheckHasDirectionality(context));
+ return widget.layoutDirection ?? Directionality.of(context);
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ void _createNewAndroidView() {
+ _id = platformViewsRegistry.getNextPlatformViewId();
+ _controller = WrappedPlatformViewsService.initAndroidView(
+ id: _id!,
+ viewType: widget.viewType,
+ layoutDirection: _layoutDirection!,
+ creationParams: widget.creationParams,
+ creationParamsCodec: widget.creationParamsCodec,
+ onFocus: () {
+ _focusNode!.requestFocus();
+ },
+ );
+ if (widget.onPlatformViewCreated != null) {
+ _controller
+ .addOnPlatformViewCreatedListener(widget.onPlatformViewCreated!);
+ }
+ }
+
+ void _onFocusChange(bool isFocused) {
+ if (!_controller.isCreated) {
+ return;
+ }
+ if (!isFocused) {
+ _controller.clearFocus().catchError((dynamic e) {
+ if (e is MissingPluginException) {
+ // We land the framework part of Android platform views keyboard
+ // support before the engine part. There will be a commit range where
+ // clearFocus isn't implemented in the engine. When that happens we
+ // just swallow the error here. Once the engine part is rolled to the
+ // framework I'll remove this.
+ // TODO(amirh): remove this once the engine's clearFocus is rolled.
+ return;
+ }
+ });
+ return;
+ }
+ SystemChannels.textInput.invokeMethod(
+ 'TextInput.setPlatformViewClient',
+ {'platformViewId': _id},
+ ).catchError((dynamic e) {
+ if (e is MissingPluginException) {
+ // We land the framework part of Android platform views keyboard
+ // support before the engine part. There will be a commit range where
+ // setPlatformViewClient isn't implemented in the engine. When that
+ // happens we just swallow the error here. Once the engine part is
+ // rolled to the framework I'll remove this.
+ // TODO(amirh): remove this once the engine's clearFocus is rolled.
+ return;
+ }
+ });
+ }
+}
+
+class _CopyPastedAndroidPlatformView extends LeafRenderObjectWidget {
+ const _CopyPastedAndroidPlatformView({
+ required this.controller,
+ required this.hitTestBehavior,
+ required this.gestureRecognizers,
+ this.clipBehavior = Clip.hardEdge,
+ }) : assert(controller != null),
+ assert(hitTestBehavior != null),
+ assert(gestureRecognizers != null),
+ assert(clipBehavior != null);
+
+ final AndroidViewController controller;
+ final PlatformViewHitTestBehavior hitTestBehavior;
+ final Set> gestureRecognizers;
+ final Clip clipBehavior;
+
+ @override
+ RenderObject createRenderObject(BuildContext context) => RenderAndroidView(
+ viewController: controller,
+ hitTestBehavior: hitTestBehavior,
+ gestureRecognizers: gestureRecognizers,
+ clipBehavior: clipBehavior,
+ );
+
+ @override
+ void updateRenderObject(
+ BuildContext context, RenderAndroidView renderObject) {
+ renderObject.controller = controller;
+ renderObject.hitTestBehavior = hitTestBehavior;
+ renderObject.updateGestureRecognizers(gestureRecognizers);
+ renderObject.clipBehavior = clipBehavior;
+ }
+}
From 83a5cda295ef4612144e861fcde61f5225ea1264 Mon Sep 17 00:00:00 2001
From: Hung Tran <32390731+hungtrn75@users.noreply.github.com>
Date: Mon, 10 Oct 2022 18:10:52 +0700
Subject: [PATCH 09/22] Support heatmap layer & fill extrusion layer & update
image source (#1187)
* fix: can not find path when run script in macOS & nested dart types errors
* fix: can not sync project
* feat: support heatmap layer
* feat: support heatmap layer
* feat: support heatmap layer
* feat: support heatmap layer
* local pbxproj
* fix: error when not await future setSymboAllowOverlap in example
* fix: error when not await future setSymboAllowOverlap in example
* add fill extrusion example
* fix: error generate code swift template with snake case
* migration: edit swift template & add ios rename props
* feat: support fill extrusion layer
* ci: run jobs
* docs: support fill extrusion layer
* docs: support fill extrusion layer
* refactor codegen
* add update image source example
* feat: update image for image source
* remove xcode teamid
---
.gitignore | 1 +
Makefile | 3 +
README.md | 3 +-
.../gradle/wrapper/gradle-wrapper.properties | 2 +-
.../mapboxgl/LayerPropertyConverter.java | 79 ++
.../mapbox/mapboxgl/MapboxMapController.java | 143 +++-
.../assets/fill-extrusion/indoor_3d_map.json | 685 ++++++++++++++++++
example/ios/Runner.xcodeproj/project.pbxproj | 2 +-
example/lib/place_source.dart | 48 +-
example/lib/place_symbol.dart | 7 +-
example/lib/sources.dart | 99 +++
example/pubspec.yaml | 1 +
ios/Classes/LayerPropertyConverter.swift | 64 ++
ios/Classes/MapboxMapController.swift | 156 ++++
lib/src/controller.dart | 90 +++
lib/src/layer_properties.dart | 308 ++++++++
.../lib/src/mapbox_gl_platform_interface.dart | 19 +
.../lib/src/method_channel_mapbox_gl.dart | 57 ++
.../lib/src/source_properties.dart | 8 +-
.../lib/src/mapbox_web_gl_platform.dart | 39 +
scripts/lib/conversions.dart | 3 +
scripts/lib/generate.dart | 14 +-
.../LayerPropertyConverter.swift.template | 12 +-
23 files changed, 1809 insertions(+), 34 deletions(-)
create mode 100644 example/assets/fill-extrusion/indoor_3d_map.json
diff --git a/.gitignore b/.gitignore
index 64487b8f3..19d24b075 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
.buildlog/
.history
.svn/
+.fvm/
# IntelliJ related
*.iml
diff --git a/Makefile b/Makefile
index 93faecdf7..71ae0f508 100644
--- a/Makefile
+++ b/Makefile
@@ -11,3 +11,6 @@ format:
install_formatting:
./install_formatting_tools.sh
+
+codegen:
+ dart scripts/lib/generate.dart
\ No newline at end of file
diff --git a/README.md b/README.md
index b92caa2d6..112b4c2fd 100644
--- a/README.md
+++ b/README.md
@@ -130,8 +130,9 @@ MapboxMap(
| Circle Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Line Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Fill Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| Fill Extrusion Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Hillshade Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
-| Heatmap Layer | :x: | :x: | :x: |
+| Heatmap Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Vector Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Raster Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| GeoJson Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index e7a6f4b2f..8428c0be2 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip
\ No newline at end of file
diff --git a/android/src/main/java/com/mapbox/mapboxgl/LayerPropertyConverter.java b/android/src/main/java/com/mapbox/mapboxgl/LayerPropertyConverter.java
index 4dcb9b034..b10599dbb 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/LayerPropertyConverter.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/LayerPropertyConverter.java
@@ -372,6 +372,50 @@ static PropertyValue[] interpretFillLayerProperties(Object o) {
return properties.toArray(new PropertyValue[properties.size()]);
}
+ static PropertyValue[] interpretFillExtrusionLayerProperties(Object o) {
+ final Map data = (Map) toMap(o);
+ final List properties = new LinkedList();
+ final JsonParser parser = new JsonParser();
+
+ for (Map.Entry entry : data.entrySet()) {
+ final JsonElement jsonElement = parser.parse(entry.getValue());
+ Expression expression = Expression.Converter.convert(jsonElement);
+ switch (entry.getKey()) {
+ case "fill-extrusion-opacity":
+ properties.add(PropertyFactory.fillExtrusionOpacity(expression));
+ break;
+ case "fill-extrusion-color":
+ properties.add(PropertyFactory.fillExtrusionColor(expression));
+ break;
+ case "fill-extrusion-translate":
+ properties.add(PropertyFactory.fillExtrusionTranslate(expression));
+ break;
+ case "fill-extrusion-translate-anchor":
+ properties.add(PropertyFactory.fillExtrusionTranslateAnchor(expression));
+ break;
+ case "fill-extrusion-pattern":
+ properties.add(PropertyFactory.fillExtrusionPattern(expression));
+ break;
+ case "fill-extrusion-height":
+ properties.add(PropertyFactory.fillExtrusionHeight(expression));
+ break;
+ case "fill-extrusion-base":
+ properties.add(PropertyFactory.fillExtrusionBase(expression));
+ break;
+ case "fill-extrusion-vertical-gradient":
+ properties.add(PropertyFactory.fillExtrusionVerticalGradient(expression));
+ break;
+ case "visibility":
+ properties.add(PropertyFactory.visibility(entry.getValue()));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return properties.toArray(new PropertyValue[properties.size()]);
+ }
+
static PropertyValue[] interpretRasterLayerProperties(Object o) {
final Map data = (Map) toMap(o);
final List properties = new LinkedList();
@@ -453,4 +497,39 @@ static PropertyValue[] interpretHillshadeLayerProperties(Object o) {
return properties.toArray(new PropertyValue[properties.size()]);
}
+
+ static PropertyValue[] interpretHeatmapLayerProperties(Object o) {
+ final Map data = (Map) toMap(o);
+ final List properties = new LinkedList();
+ final JsonParser parser = new JsonParser();
+
+ for (Map.Entry entry : data.entrySet()) {
+ final JsonElement jsonElement = parser.parse(entry.getValue());
+ Expression expression = Expression.Converter.convert(jsonElement);
+ switch (entry.getKey()) {
+ case "heatmap-radius":
+ properties.add(PropertyFactory.heatmapRadius(expression));
+ break;
+ case "heatmap-weight":
+ properties.add(PropertyFactory.heatmapWeight(expression));
+ break;
+ case "heatmap-intensity":
+ properties.add(PropertyFactory.heatmapIntensity(expression));
+ break;
+ case "heatmap-color":
+ properties.add(PropertyFactory.heatmapColor(expression));
+ break;
+ case "heatmap-opacity":
+ properties.add(PropertyFactory.heatmapOpacity(expression));
+ break;
+ case "visibility":
+ properties.add(PropertyFactory.visibility(entry.getValue()));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return properties.toArray(new PropertyValue[properties.size()]);
+ }
}
diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
index fe2a9f4db..22f9d28a8 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
@@ -105,6 +105,7 @@ final class MapboxMapController
OnMapReadyCallback,
OnCameraTrackingChangedListener,
PlatformView {
+
private static final String TAG = "MapboxMapController";
private final int id;
private final MethodChannel methodChannel;
@@ -395,6 +396,7 @@ private void addSymbolLayer(
PropertyValue[] properties,
boolean enableInteraction,
Expression filter) {
+
SymbolLayer symbolLayer = new SymbolLayer(layerName, sourceName);
symbolLayer.setProperties(properties);
if (sourceLayer != null) {
@@ -487,6 +489,40 @@ private void addFillLayer(
}
}
+ private void addFillExtrusionLayer(
+ String layerName,
+ String sourceName,
+ String belowLayerId,
+ String sourceLayer,
+ Float minZoom,
+ Float maxZoom,
+ PropertyValue[] properties,
+ boolean enableInteraction,
+ Expression filter) {
+ FillExtrusionLayer fillLayer = new FillExtrusionLayer(layerName, sourceName);
+ fillLayer.setProperties(properties);
+ if (sourceLayer != null) {
+ fillLayer.setSourceLayer(sourceLayer);
+ }
+ if (minZoom != null) {
+ fillLayer.setMinZoom(minZoom);
+ }
+ if (maxZoom != null) {
+ fillLayer.setMaxZoom(maxZoom);
+ }
+ if (filter != null) {
+ fillLayer.setFilter(filter);
+ }
+ if (belowLayerId != null) {
+ style.addLayerBelow(fillLayer, belowLayerId);
+ } else {
+ style.addLayer(fillLayer);
+ }
+ if (enableInteraction) {
+ interactiveFeatureLayerIds.add(layerName);
+ }
+ }
+
private void addCircleLayer(
String layerName,
String sourceName,
@@ -573,13 +609,38 @@ private void addHillshadeLayer(
}
}
+ private void addHeatmapLayer(
+ String layerName,
+ String sourceName,
+ Float minZoom,
+ Float maxZoom,
+ String belowLayerId,
+ PropertyValue[] properties,
+ Expression filter) {
+ HeatmapLayer layer = new HeatmapLayer(layerName, sourceName);
+ layer.setProperties(properties);
+ if (minZoom != null) {
+ layer.setMinZoom(minZoom);
+ }
+ if (maxZoom != null) {
+ layer.setMaxZoom(maxZoom);
+ }
+ if (belowLayerId != null) {
+ style.addLayerBelow(layer, belowLayerId);
+ } else {
+ style.addLayer(layer);
+ }
+ }
+
private Feature firstFeatureOnLayers(RectF in) {
if (style != null) {
final List layers = style.getLayers();
final List layersInOrder = new ArrayList();
for (Layer layer : layers) {
String id = layer.getId();
- if (interactiveFeatureLayerIds.contains(id)) layersInOrder.add(id);
+ if (interactiveFeatureLayerIds.contains(id)) {
+ layersInOrder.add(id);
+ }
}
Collections.reverse(layersInOrder);
@@ -923,6 +984,37 @@ public void onError(@NonNull String message) {
filterExpression);
updateLocationComponentLayer();
+ result.success(null);
+ break;
+ }
+ case "fillExtrusionLayer#add":
+ {
+ final String sourceId = call.argument("sourceId");
+ final String layerId = call.argument("layerId");
+ final String belowLayerId = call.argument("belowLayerId");
+ final String sourceLayer = call.argument("sourceLayer");
+ final Double minzoom = call.argument("minzoom");
+ final Double maxzoom = call.argument("maxzoom");
+ final String filter = call.argument("filter");
+ final boolean enableInteraction = call.argument("enableInteraction");
+ final PropertyValue[] properties =
+ LayerPropertyConverter.interpretFillExtrusionLayerProperties(
+ call.argument("properties"));
+
+ Expression filterExpression = parseFilter(filter);
+
+ addFillExtrusionLayer(
+ layerId,
+ sourceId,
+ belowLayerId,
+ sourceLayer,
+ minzoom != null ? minzoom.floatValue() : null,
+ maxzoom != null ? maxzoom.floatValue() : null,
+ properties,
+ enableInteraction,
+ filterExpression);
+ updateLocationComponentLayer();
+
result.success(null);
break;
}
@@ -997,6 +1089,28 @@ public void onError(@NonNull String message) {
null);
updateLocationComponentLayer();
+ result.success(null);
+ break;
+ }
+ case "heatmapLayer#add":
+ {
+ final String sourceId = call.argument("sourceId");
+ final String layerId = call.argument("layerId");
+ final String belowLayerId = call.argument("belowLayerId");
+ final Double minzoom = call.argument("minzoom");
+ final Double maxzoom = call.argument("maxzoom");
+ final PropertyValue[] properties =
+ LayerPropertyConverter.interpretHeatmapLayerProperties(call.argument("properties"));
+ addHeatmapLayer(
+ layerId,
+ sourceId,
+ minzoom != null ? minzoom.floatValue() : null,
+ maxzoom != null ? maxzoom.floatValue() : null,
+ belowLayerId,
+ properties,
+ null);
+ updateLocationComponentLayer();
+
result.success(null);
break;
}
@@ -1065,6 +1179,32 @@ public void onFailure(@NonNull Exception exception) {
result.success(null);
break;
}
+ case "style#updateImageSource":
+ {
+ if (style == null) {
+ result.error(
+ "STYLE IS NULL",
+ "The style is null. Has onStyleLoaded() already been invoked?",
+ null);
+ }
+ ImageSource imageSource = style.getSourceAs(call.argument("imageSourceId"));
+ List coordinates = Convert.toLatLngList(call.argument("coordinates"), false);
+ if (coordinates != null) {
+ // https://github.com/mapbox/mapbox-maps-android/issues/302
+ imageSource.setCoordinates(
+ new LatLngQuad(
+ coordinates.get(0),
+ coordinates.get(1),
+ coordinates.get(2),
+ coordinates.get(3)));
+ }
+ byte[] bytes = call.argument("bytes");
+ if (bytes != null) {
+ imageSource.setImage(BitmapFactory.decodeByteArray(bytes, 0, call.argument("length")));
+ }
+ result.success(null);
+ break;
+ }
case "style#addSource":
{
final String id = Convert.toString(call.argument("sourceId"));
@@ -1844,6 +1984,7 @@ void stopDragging() {
/** Simple Listener to listen for the status of camera movements. */
public class OnCameraMoveFinishedListener implements MapboxMap.CancelableCallback {
+
@Override
public void onFinish() {}
diff --git a/example/assets/fill-extrusion/indoor_3d_map.json b/example/assets/fill-extrusion/indoor_3d_map.json
new file mode 100644
index 000000000..ac81fd8ca
--- /dev/null
+++ b/example/assets/fill-extrusion/indoor_3d_map.json
@@ -0,0 +1,685 @@
+{
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Bird Exhibit",
+ "height": 0,
+ "base_height": 0,
+ "color": "orange"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.618312, 41.866282],
+ [-87.61832, 41.866674],
+ [-87.618087, 41.866676],
+ [-87.618087, 41.866661],
+ [-87.617423, 41.86667],
+ [-87.617416, 41.8663],
+ [-87.618312, 41.866282]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "06e8fa0b3f851e3ae0e1da5fc17e111e"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Bird Exhibit",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617808, 41.866266],
+ [-87.617806, 41.866293],
+ [-87.617415, 41.866298],
+ [-87.617424, 41.866671],
+ [-87.617382, 41.866669],
+ [-87.617371, 41.866274],
+ [-87.617808, 41.866266]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "08a10ab2bf15c4d14669b588062f7f08"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "East Hallway",
+ "base_height": 0,
+ "height": 0,
+ "color": "indigo"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616704, 41.866141],
+ [-87.616707, 41.866338],
+ [-87.61572, 41.866355],
+ [-87.61572, 41.866148],
+ [-87.616704, 41.866141]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "09ead44537d94ece576c7bc001c33e53"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "East Entrance",
+ "base_height": 0,
+ "height": 0,
+ "color": "violet"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.61544, 41.866149],
+ [-87.615449, 41.866358],
+ [-87.615721, 41.866355],
+ [-87.61572, 41.866143],
+ [-87.61544, 41.866149]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "12251ebf764b36cf3b8c5ad42e2deb29"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "height": 0,
+ "base_height": 0,
+ "level": 1,
+ "name": "Under the Earth",
+ "color": "red"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616701, 41.865816],
+ [-87.616705, 41.866115],
+ [-87.615712, 41.866125],
+ [-87.615706, 41.865802],
+ [-87.615936, 41.865801],
+ [-87.615938, 41.865824],
+ [-87.616701, 41.865816]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "1ce4f8c186a40b9927006644e27bfd69"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Atrium",
+ "base_height": 0,
+ "height": 0,
+ "color": "yellow"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617365, 41.865799],
+ [-87.6167, 41.865815],
+ [-87.616718, 41.866672],
+ [-87.617384, 41.86667],
+ [-87.617365, 41.865799]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "369f5d8865e677120b7576b1de6082eb"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Ancient Egypt",
+ "height": 0,
+ "base_height": 0,
+ "color": "blue"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.61807, 41.865761],
+ [-87.618299, 41.865758],
+ [-87.618307, 41.866139],
+ [-87.617407, 41.86615],
+ [-87.617399, 41.865799],
+ [-87.61807, 41.865789],
+ [-87.61807, 41.865761]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "3e9f198afe6d7dff01ac81c6eaa511fb"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "West Arch",
+ "height": 40,
+ "base_height": 30,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617424, 41.86667],
+ [-87.617384, 41.86667],
+ [-87.617365, 41.865799],
+ [-87.617405, 41.865799],
+ [-87.617424, 41.86667]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "402706f28b793d27c78d9615fff747f2"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Bird Exhibit",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.618312, 41.866283],
+ [-87.61797, 41.866288],
+ [-87.617972, 41.866265],
+ [-87.618312, 41.866259],
+ [-87.618312, 41.866283]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "43e1e2fc8399dee075ad59764f2a2878"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "Arch",
+ "level": 1,
+ "color": "grey",
+ "base_height": 30,
+ "height": 40
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617971, 41.866291],
+ [-87.617973, 41.866265],
+ [-87.617805, 41.866267],
+ [-87.617806, 41.866294],
+ [-87.617971, 41.866291]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "4528ad9b9264cbec65bb2e55ac0012c1"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "color": "grey",
+ "base_height": 30,
+ "height": 40,
+ "name": "Arch"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617979, 41.866167],
+ [-87.617797, 41.866168],
+ [-87.617799, 41.866145],
+ [-87.617976, 41.866144],
+ [-87.617979, 41.866167]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "4be6817c3b595042c8d971eebd0c4fd3"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "Kids Zone",
+ "level": 1,
+ "base_height": 0,
+ "height": 0,
+ "color": "green"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616718, 41.866675],
+ [-87.616709, 41.866371],
+ [-87.615725, 41.866381],
+ [-87.615732, 41.866711],
+ [-87.61596, 41.866711],
+ [-87.61596, 41.866688],
+ [-87.616718, 41.866675]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "5775eba03674ea1cb3550ffb38321432"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "color": "grey",
+ "name": "Arch",
+ "height": 40,
+ "base_height": 30
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616286, 41.866119],
+ [-87.616286, 41.866144],
+ [-87.616089, 41.866149],
+ [-87.616086, 41.86612],
+ [-87.616286, 41.866119]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "598832b1512dc9facc855c5367251531"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Arch",
+ "color": "grey",
+ "height": 40,
+ "base_height": 30
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616287, 41.866343],
+ [-87.616288, 41.866374],
+ [-87.616076, 41.866378],
+ [-87.616077, 41.866347],
+ [-87.616287, 41.866343]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "59ed13d4411ff5f88714d9af539788d2"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "color": "grey",
+ "name": "center_arch",
+ "base_height": 30,
+ "height": 40
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.61737, 41.866198],
+ [-87.617372, 41.86624],
+ [-87.616708, 41.866243],
+ [-87.616708, 41.866203],
+ [-87.61737, 41.866198]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "67f8952a18dfe21ee0744a3e86bc41d8"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Kids Zone",
+ "height": 0,
+ "base_height": 40,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.615719, 41.866354],
+ [-87.615718, 41.866381],
+ [-87.616077, 41.866378],
+ [-87.616077, 41.866347],
+ [-87.615719, 41.866354]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "6bb2c92386bcf4678113d6b3e400ae3b"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Under the Earth",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616089, 41.866149],
+ [-87.616089, 41.866119],
+ [-87.615711, 41.866124],
+ [-87.615713, 41.866147],
+ [-87.616089, 41.866149]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "844c87987089df6b9db3923f6a00e4d6"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Under the Earth",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616707, 41.866115],
+ [-87.616286, 41.866119],
+ [-87.616285, 41.866144],
+ [-87.616705, 41.866141],
+ [-87.616707, 41.866115]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "85547a1ecdbd2ca1fdc6324aea3c6b70"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "North Entrance",
+ "height": 0,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617386, 41.86667],
+ [-87.617388, 41.866786],
+ [-87.617228, 41.866786],
+ [-87.617226, 41.866848],
+ [-87.616867, 41.866849],
+ [-87.616868, 41.866791],
+ [-87.616718, 41.866791],
+ [-87.616718, 41.866672],
+ [-87.617386, 41.86667]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "91ab1ee01729c33568c7b57eb258e699"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Ancient Egypt",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617394, 41.865799],
+ [-87.617405, 41.86615],
+ [-87.617802, 41.866147],
+ [-87.6178, 41.866167],
+ [-87.617369, 41.866172],
+ [-87.617364, 41.865799],
+ [-87.617394, 41.865799]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "943171d55d207024791e15294def7e8f"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Ancient Egypt",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617976, 41.866166],
+ [-87.617976, 41.866143],
+ [-87.618309, 41.866139],
+ [-87.618309, 41.866161],
+ [-87.617976, 41.866166]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "a37230898905988cab9b72927dc99258"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "West Hallway",
+ "level": 1,
+ "base_height": 0,
+ "height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.618309, 41.866161],
+ [-87.618312, 41.86626],
+ [-87.61737, 41.866272],
+ [-87.61737, 41.86617],
+ [-87.618309, 41.866161]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "c7cc79da8711d746985f23a9b22c1d5e"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "Kids Zone",
+ "height": 40,
+ "base_height": 0,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616286, 41.866343],
+ [-87.616286, 41.866374],
+ [-87.61671, 41.866371],
+ [-87.616708, 41.866338],
+ [-87.616286, 41.866343]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "cfbf2aee6a73a45c12e7fc7432d6009e"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "South Entrance",
+ "height": 0,
+ "base_height": 0,
+ "color": "teal"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.617359, 41.865801],
+ [-87.617355, 41.865674],
+ [-87.617221, 41.865677],
+ [-87.617219, 41.86559],
+ [-87.616824, 41.865595],
+ [-87.616826, 41.86568],
+ [-87.616695, 41.865683],
+ [-87.6167, 41.865813],
+ [-87.617359, 41.865801]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "d71ab89467076ad023161c37f4ff0d5f"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "East Arch",
+ "height": 40,
+ "base_height": 30,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.616688, 41.866675],
+ [-87.616716, 41.866675],
+ [-87.616699, 41.865814],
+ [-87.616665, 41.865814],
+ [-87.616688, 41.866675]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "dd2baec57e4079eb65dcae5be495da62"
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "level": 1,
+ "name": "outer-walls",
+ "base_height": 0,
+ "height": 40,
+ "color": "grey"
+ },
+ "geometry": {
+ "coordinates": [
+ [
+ [-87.618087, 41.86666],
+ [-87.618087, 41.866674],
+ [-87.618319, 41.866674],
+ [-87.618298, 41.865757],
+ [-87.618326, 41.865756],
+ [-87.618347, 41.866693],
+ [-87.618068, 41.866696],
+ [-87.618067, 41.866675],
+ [-87.61741, 41.866684],
+ [-87.617416, 41.866802],
+ [-87.617247, 41.866803],
+ [-87.617246, 41.866866],
+ [-87.616846, 41.866868],
+ [-87.616848, 41.866807],
+ [-87.61669, 41.866811],
+ [-87.616693, 41.866693],
+ [-87.615992, 41.866701],
+ [-87.615992, 41.866729],
+ [-87.615703, 41.866733],
+ [-87.615689, 41.866377],
+ [-87.615412, 41.866382],
+ [-87.615411, 41.866122],
+ [-87.615689, 41.866119],
+ [-87.615682, 41.865781],
+ [-87.615966, 41.865779],
+ [-87.615969, 41.865798],
+ [-87.616669, 41.865794],
+ [-87.616663, 41.865662],
+ [-87.6168, 41.865661],
+ [-87.616796, 41.865571],
+ [-87.61726, 41.865559],
+ [-87.617258, 41.865654],
+ [-87.617388, 41.865652],
+ [-87.617395, 41.865778],
+ [-87.618045, 41.865773],
+ [-87.618044, 41.865742],
+ [-87.618325, 41.865739],
+ [-87.618326, 41.865758],
+ [-87.61807, 41.865761],
+ [-87.61807, 41.865789],
+ [-87.617356, 41.8658],
+ [-87.617356, 41.865672],
+ [-87.617218, 41.865677],
+ [-87.617215, 41.86559],
+ [-87.616822, 41.865595],
+ [-87.616827, 41.86568],
+ [-87.616694, 41.865681],
+ [-87.616697, 41.865813],
+ [-87.615939, 41.865824],
+ [-87.615937, 41.8658],
+ [-87.615706, 41.865802],
+ [-87.615713, 41.866143],
+ [-87.615441, 41.86615],
+ [-87.61545, 41.866357],
+ [-87.615724, 41.866353],
+ [-87.615733, 41.866712],
+ [-87.615959, 41.86671],
+ [-87.615959, 41.866688],
+ [-87.616719, 41.866672],
+ [-87.61672, 41.866791],
+ [-87.616869, 41.86679],
+ [-87.616868, 41.86685],
+ [-87.617223, 41.866847],
+ [-87.617227, 41.866786],
+ [-87.617387, 41.866785],
+ [-87.617383, 41.86667],
+ [-87.618087, 41.86666]
+ ]
+ ],
+ "type": "Polygon"
+ },
+ "id": "ef6512f46485e27963c248bcc945c3db"
+ }
+ ],
+ "type": "FeatureCollection"
+}
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index bfc583f29..846740d33 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
diff --git a/example/lib/place_source.dart b/example/lib/place_source.dart
index 426a7760d..428dc74aa 100644
--- a/example/lib/place_source.dart
+++ b/example/lib/place_source.dart
@@ -64,6 +64,23 @@ class PlaceSymbolBodyState extends State {
);
}
+ /// Update an asset image as a source to the currently displayed style
+ Future updateImageSourceFromAsset(
+ String imageSourceId, String assetName) async {
+ final ByteData bytes = await rootBundle.load(assetName);
+ final Uint8List list = bytes.buffer.asUint8List();
+ return controller.updateImageSource(
+ imageSourceId,
+ list,
+ const LatLngQuad(
+ bottomRight: LatLng(-33.89884564291081, 151.25229835510254),
+ bottomLeft: LatLng(-33.89884564291081, 151.20131492614746),
+ topLeft: LatLng(-33.934601369931634, 151.20131492614746),
+ topRight: LatLng(-33.934601369931634, 151.25229835510254),
+ ),
+ );
+ }
+
Future removeImageSource(String imageSourceId) {
return controller.removeSource(imageSourceId);
}
@@ -97,17 +114,14 @@ class PlaceSymbolBodyState extends State {
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
- Center(
- child: SizedBox(
- width: 300.0,
- height: 200.0,
- child: MapboxMap(
- accessToken: MapsDemo.ACCESS_TOKEN,
- onMapCreated: _onMapCreated,
- initialCameraPosition: const CameraPosition(
- target: LatLng(-33.852, 151.211),
- zoom: 11.0,
- ),
+ SizedBox(
+ height: 300.0,
+ child: MapboxMap(
+ accessToken: MapsDemo.ACCESS_TOKEN,
+ onMapCreated: _onMapCreated,
+ initialCameraPosition: const CameraPosition(
+ target: LatLng(-33.852, 151.211),
+ zoom: 10.0,
),
),
),
@@ -130,6 +144,18 @@ class PlaceSymbolBodyState extends State {
});
},
),
+ TextButton(
+ child: const Text('Update source (asset image)'),
+ onPressed: !sourceAdded
+ ? null
+ : () {
+ updateImageSourceFromAsset(SOURCE_ID,
+ 'assets/symbols/custom-icon.png')
+ .then((value) {
+ setState(() => sourceAdded = true);
+ });
+ },
+ ),
TextButton(
child: const Text('Remove source (asset image)'),
onPressed: sourceAdded
diff --git a/example/lib/place_symbol.dart b/example/lib/place_symbol.dart
index 2edc59051..c4aa68eb9 100644
--- a/example/lib/place_symbol.dart
+++ b/example/lib/place_symbol.dart
@@ -275,8 +275,8 @@ class PlaceSymbolBodyState extends State {
setState(() {
_iconAllowOverlap = !_iconAllowOverlap;
});
- controller!.setSymbolIconAllowOverlap(_iconAllowOverlap);
- controller!.setSymbolTextAllowOverlap(_iconAllowOverlap);
+ await controller!.setSymbolIconAllowOverlap(_iconAllowOverlap);
+ await controller!.setSymbolTextAllowOverlap(_iconAllowOverlap);
}
@override
@@ -287,8 +287,7 @@ class PlaceSymbolBodyState extends State {
children: [
Center(
child: SizedBox(
- width: 300.0,
- height: 200.0,
+ height: 300.0,
child: MapboxMap(
accessToken: MapsDemo.ACCESS_TOKEN,
onMapCreated: _onMapCreated,
diff --git a/example/lib/sources.dart b/example/lib/sources.dart
index 86200c154..04e4c9e76 100644
--- a/example/lib/sources.dart
+++ b/example/lib/sources.dart
@@ -1,5 +1,8 @@
+import 'dart:convert';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:mapbox_gl/mapbox_gl.dart';
import 'main.dart';
@@ -99,6 +102,89 @@ class FullMapState extends State {
));
}
+ static Future addGeojsonHeatmap(MapboxMapController controller) async {
+ await controller.addSource(
+ "earthquakes-heatmap-source",
+ GeojsonSourceProperties(
+ data:
+ 'https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson',
+ ));
+ await controller.addLayer(
+ "earthquakes-heatmap-source",
+ "earthquakes-heatmap-layer",
+ HeatmapLayerProperties(
+ heatmapColor: [
+ Expressions.interpolate,
+ ["linear"],
+ ["heatmap-density"],
+ 0,
+ "rgba(33.0, 102.0, 172.0, 0.0)",
+ 0.2,
+ "rgb(103.0, 169.0, 207.0)",
+ 0.4,
+ "rgb(209.0, 229.0, 240.0)",
+ 0.6,
+ "rgb(253.0, 219.0, 240.0)",
+ 0.8,
+ "rgb(239.0, 138.0, 98.0)",
+ 1,
+ "rgb(178.0, 24.0, 43.0)",
+ ],
+ heatmapWeight: [
+ Expressions.interpolate,
+ ["linear"],
+ [Expressions.get, "mag"],
+ 0,
+ 0,
+ 6,
+ 1,
+ ],
+ heatmapIntensity: [
+ Expressions.interpolate,
+ ["linear"],
+ [Expressions.zoom],
+ 0,
+ 1,
+ 9,
+ 3,
+ ],
+ heatmapRadius: [
+ Expressions.interpolate,
+ ["linear"],
+ [Expressions.zoom],
+ 0,
+ 2,
+ 9,
+ 20,
+ ],
+ heatmapOpacity: [
+ Expressions.interpolate,
+ ["linear"],
+ [Expressions.zoom],
+ 7,
+ 1,
+ 9,
+ 0.5
+ ],
+ ));
+ }
+
+ static Future addIndoorBuilding(MapboxMapController controller) async {
+ final jsonStr =
+ await rootBundle.loadString("assets/fill-extrusion/indoor_3d_map.json");
+ await controller.addGeoJsonSource(
+ "indoor-building-source", jsonDecode(jsonStr));
+ await controller.addFillExtrusionLayer(
+ "indoor-building-source",
+ "indoor-building-layer",
+ FillExtrusionLayerProperties(
+ fillExtrusionOpacity: 0.5,
+ fillExtrusionHeight: [Expressions.get, "height"],
+ fillExtrusionBase: [Expressions.get, "base_height"],
+ fillExtrusionColor: [Expressions.get, "color"],
+ ));
+ }
+
static Future addVector(MapboxMapController controller) async {
await controller.addSource(
"terrain",
@@ -191,6 +277,19 @@ class FullMapState extends State {
addDetails: addGeojsonCluster,
position: CameraPosition(target: LatLng(33.5, -118.1), zoom: 5),
),
+ StyleInfo(
+ name: "Geojson heatmap",
+ baseStyle: MapboxStyles.DARK,
+ addDetails: addGeojsonHeatmap,
+ position: CameraPosition(target: LatLng(33.5, -118.1), zoom: 5),
+ ),
+ StyleInfo(
+ name: "Indoor Building",
+ baseStyle: MapboxStyles.LIGHT,
+ addDetails: addIndoorBuilding,
+ position: CameraPosition(
+ target: LatLng(41.86625, -87.61694), zoom: 16, tilt: 20, bearing: 40),
+ ),
StyleInfo(
name: "Raster",
baseStyle: MapboxStyles.EMPTY,
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 9731f59c0..f27a043bc 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -53,6 +53,7 @@ flutter:
assets:
- assets/fill/cat_silhouette_pattern.png
+ - assets/fill-extrusion/indoor_3d_map.json
- assets/symbols/custom-icon.png
- assets/symbols/2.0x/custom-icon.png
- assets/symbols/3.0x/custom-icon.png
diff --git a/ios/Classes/LayerPropertyConverter.swift b/ios/Classes/LayerPropertyConverter.swift
index e36fbba54..b711adcb9 100644
--- a/ios/Classes/LayerPropertyConverter.swift
+++ b/ios/Classes/LayerPropertyConverter.swift
@@ -251,6 +251,41 @@ class LayerPropertyConverter {
}
}
+ class func addFillExtrusionProperties(
+ fillExtrusionLayer: MGLFillExtrusionStyleLayer,
+ properties: [String: String]
+ ) {
+ for (propertyName, propertyValue) in properties {
+ let expression = interpretExpression(
+ propertyName: propertyName,
+ expression: propertyValue
+ )
+ switch propertyName {
+ case "fill-extrusion-opacity":
+ fillExtrusionLayer.fillExtrusionOpacity = expression
+ case "fill-extrusion-color":
+ fillExtrusionLayer.fillExtrusionColor = expression
+ case "fill-extrusion-translate":
+ fillExtrusionLayer.fillExtrusionTranslation = expression
+ case "fill-extrusion-translate-anchor":
+ fillExtrusionLayer.fillExtrusionTranslationAnchor = expression
+ case "fill-extrusion-pattern":
+ fillExtrusionLayer.fillExtrusionPattern = expression
+ case "fill-extrusion-height":
+ fillExtrusionLayer.fillExtrusionHeight = expression
+ case "fill-extrusion-base":
+ fillExtrusionLayer.fillExtrusionBase = expression
+ case "fill-extrusion-vertical-gradient":
+ fillExtrusionLayer.fillExtrusionHasVerticalGradient = expression
+ case "visibility":
+ fillExtrusionLayer.isVisible = propertyValue == "visible"
+
+ default:
+ break
+ }
+ }
+ }
+
class func addRasterProperties(rasterLayer: MGLRasterStyleLayer, properties: [String: String]) {
for (propertyName, propertyValue) in properties {
let expression = interpretExpression(
@@ -314,6 +349,35 @@ class LayerPropertyConverter {
}
}
+ class func addHeatmapProperties(
+ heatmapLayer: MGLHeatmapStyleLayer,
+ properties: [String: String]
+ ) {
+ for (propertyName, propertyValue) in properties {
+ let expression = interpretExpression(
+ propertyName: propertyName,
+ expression: propertyValue
+ )
+ switch propertyName {
+ case "heatmap-radius":
+ heatmapLayer.heatmapRadius = expression
+ case "heatmap-weight":
+ heatmapLayer.heatmapWeight = expression
+ case "heatmap-intensity":
+ heatmapLayer.heatmapIntensity = expression
+ case "heatmap-color":
+ heatmapLayer.heatmapColor = expression
+ case "heatmap-opacity":
+ heatmapLayer.heatmapOpacity = expression
+ case "visibility":
+ heatmapLayer.isVisible = propertyValue == "visible"
+
+ default:
+ break
+ }
+ }
+ }
+
private class func interpretExpression(propertyName: String,
expression: String) -> NSExpression?
{
diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift
index 7fa523e0e..f9d56dbae 100644
--- a/ios/Classes/MapboxMapController.swift
+++ b/ios/Classes/MapboxMapController.swift
@@ -426,6 +426,34 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
case let .failure(error): result(error.flutterError)
}
+ case "fillExtrusionLayer#add":
+ guard let arguments = methodCall.arguments as? [String: Any] else { return }
+ guard let sourceId = arguments["sourceId"] as? String else { return }
+ guard let layerId = arguments["layerId"] as? String else { return }
+ guard let properties = arguments["properties"] as? [String: String] else { return }
+ guard let enableInteraction = arguments["enableInteraction"] as? Bool else { return }
+ let belowLayerId = arguments["belowLayerId"] as? String
+ let sourceLayer = arguments["sourceLayer"] as? String
+ let minzoom = arguments["minzoom"] as? Double
+ let maxzoom = arguments["maxzoom"] as? Double
+ let filter = arguments["filter"] as? String
+
+ let addResult = addFillExtrusionLayer(
+ sourceId: sourceId,
+ layerId: layerId,
+ belowLayerId: belowLayerId,
+ sourceLayerIdentifier: sourceLayer,
+ minimumZoomLevel: minzoom,
+ maximumZoomLevel: maxzoom,
+ filter: filter,
+ enableInteraction: enableInteraction,
+ properties: properties
+ )
+ switch addResult {
+ case .success: result(nil)
+ case let .failure(error): result(error.flutterError)
+ }
+
case "circleLayer#add":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }
@@ -472,6 +500,24 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
)
result(nil)
+ case "heatmapLayer#add":
+ guard let arguments = methodCall.arguments as? [String: Any] else { return }
+ guard let sourceId = arguments["sourceId"] as? String else { return }
+ guard let layerId = arguments["layerId"] as? String else { return }
+ guard let properties = arguments["properties"] as? [String: String] else { return }
+ let belowLayerId = arguments["belowLayerId"] as? String
+ let minzoom = arguments["minzoom"] as? Double
+ let maxzoom = arguments["maxzoom"] as? Double
+ addHeatmapLayer(
+ sourceId: sourceId,
+ layerId: layerId,
+ belowLayerId: belowLayerId,
+ minimumZoomLevel: minzoom,
+ maximumZoomLevel: maxzoom,
+ properties: properties
+ )
+ result(nil)
+
case "rasterLayer#add":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }
@@ -550,6 +596,41 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
mapView.style?.addSource(source)
result(nil)
+ case "style#updateImageSource":
+ guard let arguments = methodCall.arguments as? [String: Any] else { return }
+ guard let imageSourceId = arguments["imageSourceId"] as? String else { return }
+ guard let imageSource = mapView.style?
+ .source(withIdentifier: imageSourceId) as? MGLImageSource else { return }
+ let bytes = arguments["bytes"] as? FlutterStandardTypedData
+ if bytes != nil {
+ guard let data = bytes!.data as? Data else { return }
+ guard let image = UIImage(data: data) else { return }
+ imageSource.image = image
+ }
+ let coordinates = arguments["coordinates"] as? [[Double]]
+ if coordinates != nil {
+ let quad = MGLCoordinateQuad(
+ topLeft: CLLocationCoordinate2D(
+ latitude: coordinates![0][0],
+ longitude: coordinates![0][1]
+ ),
+ bottomLeft: CLLocationCoordinate2D(
+ latitude: coordinates![3][0],
+ longitude: coordinates![3][1]
+ ),
+ bottomRight: CLLocationCoordinate2D(
+ latitude: coordinates![2][0],
+ longitude: coordinates![2][1]
+ ),
+ topRight: CLLocationCoordinate2D(
+ latitude: coordinates![1][0],
+ longitude: coordinates![1][1]
+ )
+ )
+ imageSource.coordinates = quad
+ }
+ result(nil)
+
case "style#removeSource":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }
@@ -1217,6 +1298,51 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
return .success(())
}
+ func addFillExtrusionLayer(
+ sourceId: String,
+ layerId: String,
+ belowLayerId: String?,
+ sourceLayerIdentifier: String?,
+ minimumZoomLevel: Double?,
+ maximumZoomLevel: Double?,
+ filter: String?,
+ enableInteraction: Bool,
+ properties: [String: String]
+ ) -> Result {
+ if let style = mapView.style {
+ if let source = style.source(withIdentifier: sourceId) {
+ let layer = MGLFillExtrusionStyleLayer(identifier: layerId, source: source)
+ LayerPropertyConverter.addFillExtrusionProperties(
+ fillExtrusionLayer: layer,
+ properties: properties
+ )
+ if let sourceLayerIdentifier = sourceLayerIdentifier {
+ layer.sourceLayerIdentifier = sourceLayerIdentifier
+ }
+ if let minimumZoomLevel = minimumZoomLevel {
+ layer.minimumZoomLevel = Float(minimumZoomLevel)
+ }
+ if let maximumZoomLevel = maximumZoomLevel {
+ layer.maximumZoomLevel = Float(maximumZoomLevel)
+ }
+ if let filter = filter {
+ if case let .failure(error) = setFilter(layer, filter) {
+ return .failure(error)
+ }
+ }
+ if let id = belowLayerId, let belowLayer = style.layer(withIdentifier: id) {
+ style.insertLayer(layer, below: belowLayer)
+ } else {
+ style.addLayer(layer)
+ }
+ if enableInteraction {
+ interactiveFeatureLayerIds.insert(layerId)
+ }
+ }
+ }
+ return .success(())
+ }
+
func addCircleLayer(
sourceId: String,
layerId: String,
@@ -1315,6 +1441,36 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
}
}
+ func addHeatmapLayer(
+ sourceId: String,
+ layerId: String,
+ belowLayerId: String?,
+ minimumZoomLevel: Double?,
+ maximumZoomLevel: Double?,
+ properties: [String: String]
+ ) {
+ if let style = mapView.style {
+ if let source = style.source(withIdentifier: sourceId) {
+ let layer = MGLHeatmapStyleLayer(identifier: layerId, source: source)
+ LayerPropertyConverter.addHeatmapProperties(
+ heatmapLayer: layer,
+ properties: properties
+ )
+ if let minimumZoomLevel = minimumZoomLevel {
+ layer.minimumZoomLevel = Float(minimumZoomLevel)
+ }
+ if let maximumZoomLevel = maximumZoomLevel {
+ layer.maximumZoomLevel = Float(maximumZoomLevel)
+ }
+ if let id = belowLayerId, let belowLayer = style.layer(withIdentifier: id) {
+ style.insertLayer(layer, below: belowLayer)
+ } else {
+ style.addLayer(layer)
+ }
+ }
+ }
+ }
+
func addRasterLayer(
sourceId: String,
layerId: String,
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index 3a5257cd3..f58a17bb9 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -467,6 +467,46 @@ class MapboxMapController extends ChangeNotifier {
);
}
+ /// Add a fill extrusion layer to the map with the given properties
+ ///
+ /// Consider using [addLayer] for an unified layer api.
+ ///
+ /// The returned [Future] completes after the change has been made on the
+ /// platform side.
+ ///
+ /// Setting [belowLayerId] adds the new layer below the given id.
+ /// If [enableInteraction] is set the layer is considered for touch or drag
+ /// events. [sourceLayer] is used to selected a specific source layer from
+ /// Vector source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
+ /// [filter] determines which features should be rendered in the layer.
+ /// Filters are written as [expressions].
+ ///
+ /// [expressions]: https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions
+ Future addFillExtrusionLayer(
+ String sourceId, String layerId, FillExtrusionLayerProperties properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom,
+ dynamic filter,
+ bool enableInteraction = true}) async {
+ await _mapboxGlPlatform.addFillExtrusionLayer(
+ sourceId,
+ layerId,
+ properties.toJson(),
+ belowLayerId: belowLayerId,
+ sourceLayer: sourceLayer,
+ minzoom: minzoom,
+ maxzoom: maxzoom,
+ filter: filter,
+ enableInteraction: enableInteraction,
+ );
+ }
+
/// Add a circle layer to the map with the given properties
///
/// Consider using [addLayer] for an unified layer api.
@@ -569,6 +609,37 @@ class MapboxMapController extends ChangeNotifier {
);
}
+ /// Add a heatmap layer to the map with the given properties
+ ///
+ /// Consider using [addLayer] for an unified layer api.
+ ///
+ /// The returned [Future] completes after the change has been made on the
+ /// platform side.
+ ///
+ /// Setting [belowLayerId] adds the new layer below the given id.
+ /// [sourceLayer] is used to selected a specific source layer from
+ /// Raster source.
+ /// [minzoom] is the minimum (inclusive) zoom level at which the layer is
+ /// visible.
+ /// [maxzoom] is the maximum (exclusive) zoom level at which the layer is
+ /// visible.
+ Future addHeatmapLayer(
+ String sourceId, String layerId, HeatmapLayerProperties properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom}) async {
+ await _mapboxGlPlatform.addHeatmapLayer(
+ sourceId,
+ layerId,
+ properties.toJson(),
+ belowLayerId: belowLayerId,
+ sourceLayer: sourceLayer,
+ minzoom: minzoom,
+ maxzoom: maxzoom,
+ );
+ }
+
/// Updates user location tracking mode.
///
/// The returned [Future] completes after the change has been made on the
@@ -1087,6 +1158,13 @@ class MapboxMapController extends ChangeNotifier {
return _mapboxGlPlatform.addImageSource(imageSourceId, bytes, coordinates);
}
+ /// Update an image source to the style currently displayed in the map, so that it can later be referred to by the provided id.
+ Future updateImageSource(
+ String imageSourceId, Uint8List? bytes, LatLngQuad? coordinates) {
+ return _mapboxGlPlatform.updateImageSource(
+ imageSourceId, bytes, coordinates);
+ }
+
/// Removes previously added image source by id
@Deprecated("This method was renamed to removeSource")
Future removeImageSource(String imageSourceId) {
@@ -1196,6 +1274,12 @@ class MapboxMapController extends ChangeNotifier {
minzoom: minzoom,
maxzoom: maxzoom,
filter: filter);
+ } else if (properties is FillExtrusionLayerProperties) {
+ addFillExtrusionLayer(sourceId, layerId, properties,
+ belowLayerId: belowLayerId,
+ sourceLayer: sourceLayer,
+ minzoom: minzoom,
+ maxzoom: maxzoom);
} else if (properties is LineLayerProperties) {
addLineLayer(sourceId, layerId, properties,
belowLayerId: belowLayerId,
@@ -1238,6 +1322,12 @@ class MapboxMapController extends ChangeNotifier {
sourceLayer: sourceLayer,
minzoom: minzoom,
maxzoom: maxzoom);
+ } else if (properties is HeatmapLayerProperties) {
+ addHeatmapLayer(sourceId, layerId, properties,
+ belowLayerId: belowLayerId,
+ sourceLayer: sourceLayer,
+ minzoom: minzoom,
+ maxzoom: maxzoom);
} else {
throw UnimplementedError("Unknown layer type $properties");
}
diff --git a/lib/src/layer_properties.dart b/lib/src/layer_properties.dart
index 4ada94868..8624c23d9 100644
--- a/lib/src/layer_properties.dart
+++ b/lib/src/layer_properties.dart
@@ -1751,6 +1751,188 @@ class FillLayerProperties implements LayerProperties {
}
}
+class FillExtrusionLayerProperties implements LayerProperties {
+ // Paint Properties
+ /// The opacity of the entire fill extrusion layer. This is rendered on a
+ /// per-layer, not per-feature, basis, and data-driven styling is not
+ /// available.
+ ///
+ /// Type: number
+ /// default: 1
+ /// minimum: 0
+ /// maximum: 1
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic fillExtrusionOpacity;
+
+ /// The base color of the extruded fill. The extrusion's surfaces will be
+ /// shaded differently based on this color in combination with the root
+ /// `light` settings. If this color is specified as `rgba` with an alpha
+ /// component, the alpha component will be ignored; use
+ /// `fill-extrusion-opacity` to set layer opacity.
+ ///
+ /// Type: color
+ /// default: #000000
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ /// data-driven styling with js, android, ios, macos
+ final dynamic fillExtrusionColor;
+
+ /// The geometry's offset. Values are [x, y] where negatives indicate left
+ /// and up (on the flat plane), respectively.
+ ///
+ /// Type: array
+ /// default: [0, 0]
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic fillExtrusionTranslate;
+
+ /// Controls the frame of reference for `fill-extrusion-translate`.
+ ///
+ /// Type: enum
+ /// default: map
+ /// Options:
+ /// "map"
+ /// The fill extrusion is translated relative to the map.
+ /// "viewport"
+ /// The fill extrusion is translated relative to the viewport.
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic fillExtrusionTranslateAnchor;
+
+ /// Name of image in sprite to use for drawing images on extruded fills.
+ /// For seamless patterns, image width and height must be a factor of two
+ /// (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be
+ /// evaluated only at integer zoom levels.
+ ///
+ /// Type: resolvedImage
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ /// data-driven styling with js, android, macos, ios
+ final dynamic fillExtrusionPattern;
+
+ /// The height with which to extrude this layer.
+ ///
+ /// Type: number
+ /// default: 0
+ /// minimum: 0
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ /// data-driven styling with js, android, ios, macos
+ final dynamic fillExtrusionHeight;
+
+ /// The height with which to extrude the base of this layer. Must be less
+ /// than or equal to `fill-extrusion-height`.
+ ///
+ /// Type: number
+ /// default: 0
+ /// minimum: 0
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ /// data-driven styling with js, android, ios, macos
+ final dynamic fillExtrusionBase;
+
+ /// Whether to apply a vertical gradient to the sides of a fill-extrusion
+ /// layer. If true, sides will be shaded slightly darker farther down.
+ ///
+ /// Type: boolean
+ /// default: true
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, ios, macos
+ final dynamic fillExtrusionVerticalGradient;
+
+ // Layout Properties
+ /// Whether this layer is displayed.
+ ///
+ /// Type: enum
+ /// default: visible
+ /// Options:
+ /// "visible"
+ /// The layer is shown.
+ /// "none"
+ /// The layer is not shown.
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic visibility;
+
+ const FillExtrusionLayerProperties({
+ this.fillExtrusionOpacity,
+ this.fillExtrusionColor,
+ this.fillExtrusionTranslate,
+ this.fillExtrusionTranslateAnchor,
+ this.fillExtrusionPattern,
+ this.fillExtrusionHeight,
+ this.fillExtrusionBase,
+ this.fillExtrusionVerticalGradient,
+ this.visibility,
+ });
+
+ FillExtrusionLayerProperties copyWith(FillExtrusionLayerProperties changes) {
+ return FillExtrusionLayerProperties(
+ fillExtrusionOpacity:
+ changes.fillExtrusionOpacity ?? fillExtrusionOpacity,
+ fillExtrusionColor: changes.fillExtrusionColor ?? fillExtrusionColor,
+ fillExtrusionTranslate:
+ changes.fillExtrusionTranslate ?? fillExtrusionTranslate,
+ fillExtrusionTranslateAnchor:
+ changes.fillExtrusionTranslateAnchor ?? fillExtrusionTranslateAnchor,
+ fillExtrusionPattern:
+ changes.fillExtrusionPattern ?? fillExtrusionPattern,
+ fillExtrusionHeight: changes.fillExtrusionHeight ?? fillExtrusionHeight,
+ fillExtrusionBase: changes.fillExtrusionBase ?? fillExtrusionBase,
+ fillExtrusionVerticalGradient: changes.fillExtrusionVerticalGradient ??
+ fillExtrusionVerticalGradient,
+ visibility: changes.visibility ?? visibility,
+ );
+ }
+
+ Map toJson() {
+ final Map json = {};
+
+ void addIfPresent(String fieldName, dynamic value) {
+ if (value != null) {
+ json[fieldName] = value;
+ }
+ }
+
+ addIfPresent('fill-extrusion-opacity', fillExtrusionOpacity);
+ addIfPresent('fill-extrusion-color', fillExtrusionColor);
+ addIfPresent('fill-extrusion-translate', fillExtrusionTranslate);
+ addIfPresent(
+ 'fill-extrusion-translate-anchor', fillExtrusionTranslateAnchor);
+ addIfPresent('fill-extrusion-pattern', fillExtrusionPattern);
+ addIfPresent('fill-extrusion-height', fillExtrusionHeight);
+ addIfPresent('fill-extrusion-base', fillExtrusionBase);
+ addIfPresent(
+ 'fill-extrusion-vertical-gradient', fillExtrusionVerticalGradient);
+ addIfPresent('visibility', visibility);
+ return json;
+ }
+
+ factory FillExtrusionLayerProperties.fromJson(Map json) {
+ return FillExtrusionLayerProperties(
+ fillExtrusionOpacity: json['fill-extrusion-opacity'],
+ fillExtrusionColor: json['fill-extrusion-color'],
+ fillExtrusionTranslate: json['fill-extrusion-translate'],
+ fillExtrusionTranslateAnchor: json['fill-extrusion-translate-anchor'],
+ fillExtrusionPattern: json['fill-extrusion-pattern'],
+ fillExtrusionHeight: json['fill-extrusion-height'],
+ fillExtrusionBase: json['fill-extrusion-base'],
+ fillExtrusionVerticalGradient: json['fill-extrusion-vertical-gradient'],
+ visibility: json['visibility'],
+ );
+ }
+}
+
class RasterLayerProperties implements LayerProperties {
// Paint Properties
/// The opacity at which the image will be drawn.
@@ -2070,3 +2252,129 @@ class HillshadeLayerProperties implements LayerProperties {
);
}
}
+
+class HeatmapLayerProperties implements LayerProperties {
+ // Paint Properties
+ /// Radius of influence of one heatmap point in pixels. Increasing the
+ /// value makes the heatmap smoother, but less detailed.
+ ///
+ /// Type: number
+ /// default: 30
+ /// minimum: 1
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ /// data-driven styling with js, android, ios, macos
+ final dynamic heatmapRadius;
+
+ /// A measure of how much an individual point contributes to the heatmap.
+ /// A value of 10 would be equivalent to having 10 points of weight 1 in
+ /// the same spot. Especially useful when combined with clustering.
+ ///
+ /// Type: number
+ /// default: 1
+ /// minimum: 0
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ /// data-driven styling with js, android, ios, macos
+ final dynamic heatmapWeight;
+
+ /// Similar to `heatmap-weight` but controls the intensity of the heatmap
+ /// globally. Primarily used for adjusting the heatmap based on zoom
+ /// level.
+ ///
+ /// Type: number
+ /// default: 1
+ /// minimum: 0
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic heatmapIntensity;
+
+ /// Defines the color of each pixel based on its density value in a
+ /// heatmap. Should be an expression that uses `["heatmap-density"]` as
+ /// input.
+ ///
+ /// Type: color
+ /// default: [interpolate, [linear], [heatmap-density], 0, rgba(0, 0, 255, 0), 0.1, royalblue, 0.3, cyan, 0.5, lime, 0.7, yellow, 1, red]
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic heatmapColor;
+
+ /// The global opacity at which the heatmap layer will be drawn.
+ ///
+ /// Type: number
+ /// default: 1
+ /// minimum: 0
+ /// maximum: 1
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic heatmapOpacity;
+
+ // Layout Properties
+ /// Whether this layer is displayed.
+ ///
+ /// Type: enum
+ /// default: visible
+ /// Options:
+ /// "visible"
+ /// The layer is shown.
+ /// "none"
+ /// The layer is not shown.
+ ///
+ /// Sdk Support:
+ /// basic functionality with js, android, ios, macos
+ final dynamic visibility;
+
+ const HeatmapLayerProperties({
+ this.heatmapRadius,
+ this.heatmapWeight,
+ this.heatmapIntensity,
+ this.heatmapColor,
+ this.heatmapOpacity,
+ this.visibility,
+ });
+
+ HeatmapLayerProperties copyWith(HeatmapLayerProperties changes) {
+ return HeatmapLayerProperties(
+ heatmapRadius: changes.heatmapRadius ?? heatmapRadius,
+ heatmapWeight: changes.heatmapWeight ?? heatmapWeight,
+ heatmapIntensity: changes.heatmapIntensity ?? heatmapIntensity,
+ heatmapColor: changes.heatmapColor ?? heatmapColor,
+ heatmapOpacity: changes.heatmapOpacity ?? heatmapOpacity,
+ visibility: changes.visibility ?? visibility,
+ );
+ }
+
+ Map toJson() {
+ final Map json = {};
+
+ void addIfPresent(String fieldName, dynamic value) {
+ if (value != null) {
+ json[fieldName] = value;
+ }
+ }
+
+ addIfPresent('heatmap-radius', heatmapRadius);
+ addIfPresent('heatmap-weight', heatmapWeight);
+ addIfPresent('heatmap-intensity', heatmapIntensity);
+ addIfPresent('heatmap-color', heatmapColor);
+ addIfPresent('heatmap-opacity', heatmapOpacity);
+ addIfPresent('visibility', visibility);
+ return json;
+ }
+
+ factory HeatmapLayerProperties.fromJson(Map json) {
+ return HeatmapLayerProperties(
+ heatmapRadius: json['heatmap-radius'],
+ heatmapWeight: json['heatmap-weight'],
+ heatmapIntensity: json['heatmap-intensity'],
+ heatmapColor: json['heatmap-color'],
+ heatmapOpacity: json['heatmap-opacity'],
+ visibility: json['visibility'],
+ );
+ }
+}
diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
index a86ae1c57..c542fdea8 100644
--- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
@@ -81,6 +81,9 @@ abstract class MapboxGlPlatform {
Future addImageSource(
String imageSourceId, Uint8List bytes, LatLngQuad coordinates);
+ Future updateImageSource(
+ String imageSourceId, Uint8List? bytes, LatLngQuad? coordinates);
+
Future addLayer(String imageLayerId, String imageSourceId,
double? minzoom, double? maxzoom);
@@ -145,6 +148,15 @@ abstract class MapboxGlPlatform {
dynamic filter,
required bool enableInteraction});
+ Future addFillExtrusionLayer(
+ String sourceId, String layerId, Map properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom,
+ dynamic filter,
+ required bool enableInteraction});
+
Future addRasterLayer(
String sourceId, String layerId, Map properties,
{String? belowLayerId,
@@ -159,6 +171,13 @@ abstract class MapboxGlPlatform {
double? minzoom,
double? maxzoom});
+ Future addHeatmapLayer(
+ String sourceId, String layerId, Map properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom});
+
Future addSource(String sourceId, SourceProperties properties);
Future takeSnapshot(SnapshotOptions snapshotOptions);
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index a04925003..ebfe7963b 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -411,6 +411,22 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
}
}
+ @override
+ Future updateImageSource(
+ String imageSourceId, Uint8List? bytes, LatLngQuad? coordinates) async {
+ try {
+ return await _channel
+ .invokeMethod('style#updateImageSource', {
+ 'imageSourceId': imageSourceId,
+ 'bytes': bytes,
+ 'length': bytes?.length,
+ 'coordinates': coordinates?.toList()
+ });
+ } on PlatformException catch (e) {
+ return new Future.error(e);
+ }
+ }
+
@override
Future toScreenLocation(LatLng latLng) async {
try {
@@ -647,6 +663,29 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
});
}
+ @override
+ Future addFillExtrusionLayer(
+ String sourceId, String layerId, Map properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom,
+ dynamic filter,
+ required bool enableInteraction}) async {
+ await _channel.invokeMethod('fillExtrusionLayer#add', {
+ 'sourceId': sourceId,
+ 'layerId': layerId,
+ 'belowLayerId': belowLayerId,
+ 'sourceLayer': sourceLayer,
+ 'minzoom': minzoom,
+ 'maxzoom': maxzoom,
+ 'filter': jsonEncode(filter),
+ 'enableInteraction': enableInteraction,
+ 'properties': properties
+ .map((key, value) => MapEntry(key, jsonEncode(value)))
+ });
+ }
+
@override
void dispose() {
super.dispose();
@@ -697,6 +736,24 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
});
}
+ @override
+ Future addHeatmapLayer(
+ String sourceId, String layerId, Map properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom}) async {
+ await _channel.invokeMethod('heatmapLayer#add', {
+ 'sourceId': sourceId,
+ 'layerId': layerId,
+ 'belowLayerId': belowLayerId,
+ 'minzoom': minzoom,
+ 'maxzoom': maxzoom,
+ 'properties': properties
+ .map((key, value) => MapEntry(key, jsonEncode(value)))
+ });
+ }
+
Future setFeatureForGeoJsonSource(
String sourceId, Map geojsonFeature) async {
await _channel.invokeMethod('source#setFeature', {
diff --git a/mapbox_gl_platform_interface/lib/src/source_properties.dart b/mapbox_gl_platform_interface/lib/src/source_properties.dart
index 17351e9b0..c6b450c7c 100644
--- a/mapbox_gl_platform_interface/lib/src/source_properties.dart
+++ b/mapbox_gl_platform_interface/lib/src/source_properties.dart
@@ -601,7 +601,7 @@ class VideoSourceProperties implements SourceProperties {
/// Corners of video specified in longitude, latitude pairs.
///
/// Type: array
- final List? coordinates;
+ final List? coordinates;
const VideoSourceProperties({
this.urls,
@@ -610,7 +610,7 @@ class VideoSourceProperties implements SourceProperties {
VideoSourceProperties copyWith(
List? urls,
- List? coordinates,
+ List? coordinates,
) {
return VideoSourceProperties(
urls: urls ?? this.urls,
@@ -650,7 +650,7 @@ class ImageSourceProperties implements SourceProperties {
/// Corners of image specified in longitude, latitude pairs.
///
/// Type: array
- final List? coordinates;
+ final List? coordinates;
const ImageSourceProperties({
this.url,
@@ -659,7 +659,7 @@ class ImageSourceProperties implements SourceProperties {
ImageSourceProperties copyWith(
String? url,
- List? coordinates,
+ List? coordinates,
) {
return ImageSourceProperties(
url: url ?? this.url,
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index 1bd6e5244..2b4a69f83 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -766,6 +766,24 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
enableInteraction: enableInteraction);
}
+ @override
+ Future addFillExtrusionLayer(
+ String sourceId, String layerId, Map properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom,
+ dynamic filter,
+ required bool enableInteraction}) async {
+ return _addLayer(sourceId, layerId, properties, "fill-extrusion",
+ belowLayerId: belowLayerId,
+ sourceLayer: sourceLayer,
+ minzoom: minzoom,
+ maxzoom: maxzoom,
+ filter: filter,
+ enableInteraction: enableInteraction);
+ }
+
@override
Future addLineLayer(
String sourceId, String layerId, Map properties,
@@ -817,6 +835,21 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
enableInteraction: false);
}
+ @override
+ Future addHeatmapLayer(
+ String sourceId, String layerId, Map properties,
+ {String? belowLayerId,
+ String? sourceLayer,
+ double? minzoom,
+ double? maxzoom}) async {
+ return _addLayer(sourceId, layerId, properties, "heatmap",
+ belowLayerId: belowLayerId,
+ sourceLayer: sourceLayer,
+ minzoom: minzoom,
+ maxzoom: maxzoom,
+ enableInteraction: false);
+ }
+
@override
Future addRasterLayer(
String sourceId, String layerId, Map properties,
@@ -944,6 +977,12 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
throw UnimplementedError();
}
+ Future updateImageSource(
+ String imageSourceId, Uint8List? bytes, LatLngQuad? coordinates) {
+ // TODO: implement addImageSource
+ throw UnimplementedError();
+ }
+
@override
Future addLayer(String imageLayerId, String imageSourceId,
double? minzoom, double? maxzoom) {
diff --git a/scripts/lib/conversions.dart b/scripts/lib/conversions.dart
index 1cd5a411c..016f8f86a 100644
--- a/scripts/lib/conversions.dart
+++ b/scripts/lib/conversions.dart
@@ -35,6 +35,9 @@ const renamedIosProperties = {
"visibility": "isVisible",
"rasterBrightnessMin": "minimumRasterBrightness",
"rasterBrightnessMax": "maximumRasterBrightness",
+ "fillExtrusionTranslate": "fillExtrusionTranslation",
+ "fillExtrusionTranslateAnchor": "fillExtrusionTranslationAnchor",
+ "fillExtrusionVerticalGradient": "fillExtrusionHasVerticalGradient",
};
const dartTypeMappingTable = {
diff --git a/scripts/lib/generate.dart b/scripts/lib/generate.dart
index e797bb688..3ee9fed6b 100644
--- a/scripts/lib/generate.dart
+++ b/scripts/lib/generate.dart
@@ -15,8 +15,10 @@ main() async {
"circle",
"line",
"fill",
+ "fill-extrusion",
"raster",
- "hillshade"
+ "hillshade",
+ "heatmap"
];
final sourceTypes = [
"vector",
@@ -33,6 +35,7 @@ main() async {
{
"type": type,
"typePascal": ReCase(type).pascalCase,
+ "typeCamel": ReCase(type).camelCase,
"paint_properties": buildStyleProperties(styleJson, "paint_$type"),
"layout_properties": buildStyleProperties(styleJson, "layout_$type"),
},
@@ -42,6 +45,7 @@ main() async {
{
"type": type.replaceAll("_", "-"),
"typePascal": ReCase(type).pascalCase,
+ "typeCamel": ReCase(type).camelCase,
"properties": buildSourceProperties(styleJson, "source_$type"),
},
],
@@ -54,7 +58,7 @@ main() async {
...type["layout_properties"].map((p) => p["value"]).toList()
].toSet().map((p) => {"property": p}).toList();
- const templates = [
+ final templates = [
"android/src/main/java/com/mapbox/mapboxgl/LayerPropertyConverter.java",
"ios/Classes/LayerPropertyConverter.swift",
"lib/src/layer_expressions.dart",
@@ -76,7 +80,7 @@ Future render(
print("Rendering $filename");
var templateFile =
- await File('./scripts/templates/$filename.template').readAsString();
+ await File('scripts/templates/$filename.template').readAsString();
var template = Template(templateFile);
var outputFile = File('$outputPath/$filename');
@@ -122,9 +126,9 @@ Map buildSourceProperty(
final typeDart = dartTypeMappingTable[value["type"]];
final typeSwift = swiftTypeMappingTable[value["type"]];
final nestedTypeDart = dartTypeMappingTable[value["value"]] ??
- dartTypeMappingTable[value["value"]["type"]];
+ dartTypeMappingTable[value["value"]?["type"]];
final nestedTypeSwift = swiftTypeMappingTable[value["value"]] ??
- swiftTypeMappingTable[value["value"]["type"]];
+ swiftTypeMappingTable[value["value"]?["type"]];
var defaultValue = value["default"];
if (defaultValue is List) {
diff --git a/scripts/templates/LayerPropertyConverter.swift.template b/scripts/templates/LayerPropertyConverter.swift.template
index 4719a9cdb..a06903db4 100644
--- a/scripts/templates/LayerPropertyConverter.swift.template
+++ b/scripts/templates/LayerPropertyConverter.swift.template
@@ -6,17 +6,17 @@ import MapboxAnnotationExtension
class LayerPropertyConverter {
{{#layerTypes}}
- class func add{{typePascal}}Properties({{type}}Layer: MGL{{typePascal}}StyleLayer, properties: [String: String]) {
+ class func add{{typePascal}}Properties({{typeCamel}}Layer: MGL{{typePascal}}StyleLayer, properties: [String: String]) {
for (propertyName, propertyValue) in properties {
let expression = interpretExpression(propertyName: propertyName, expression: propertyValue)
switch propertyName {
{{#paint_properties}}
case "{{{value}}}":
{{#isIosAsCamelCase}}
- {{type}}Layer.{{iosAsCamelCase}} = expression;
+ {{typeCamel}}Layer.{{iosAsCamelCase}} = expression;
{{/isIosAsCamelCase}}
{{^isIosAsCamelCase}}
- {{type}}Layer.{{valueAsCamelCase}} = expression;
+ {{typeCamel}}Layer.{{valueAsCamelCase}} = expression;
{{/isIosAsCamelCase}}
break;
{{/paint_properties}}
@@ -24,14 +24,14 @@ class LayerPropertyConverter {
case "{{{value}}}":
{{^isVisibilityProperty}}
{{#isIosAsCamelCase}}
- {{type}}Layer.{{iosAsCamelCase}} = expression;
+ {{typeCamel}}Layer.{{iosAsCamelCase}} = expression;
{{/isIosAsCamelCase}}
{{^isIosAsCamelCase}}
- {{type}}Layer.{{valueAsCamelCase}} = expression;
+ {{typeCamel}}Layer.{{valueAsCamelCase}} = expression;
{{/isIosAsCamelCase}}
{{/isVisibilityProperty}}
{{#isVisibilityProperty}}
- {{type}}Layer.{{iosAsCamelCase}} = propertyValue == "visible";
+ {{typeCamel}}Layer.{{iosAsCamelCase}} = propertyValue == "visible";
{{/isVisibilityProperty}}
break;
{{/layout_properties}}
From d220d07ad66e0b76dbd68c8da8622628c987a4fd Mon Sep 17 00:00:00 2001
From: Simon Irmancnik
Date: Wed, 26 Oct 2022 14:02:01 +0200
Subject: [PATCH 10/22] Backwards compatibility for delayed disposal in flutter
2.x (#1217)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Backwards compatibility for delayed disposal in flutter 2.x
* Backwards compatible TextureAndroidViewController with support for size in create method
* Formatting cleanup
Co-authored-by: Simon Irmančnik
---
.../lib/src/view_wrappers.dart | 77 +++++++++++++------
1 file changed, 52 insertions(+), 25 deletions(-)
diff --git a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
index 11efef517..ef11fe3fd 100644
--- a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
+++ b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
@@ -32,35 +32,54 @@ class TextureAndroidViewControllerWrapper
final TextureAndroidViewController _controller;
- @override
+ // @override
PointTransformer get pointTransformer => _controller.pointTransformer;
set pointTransformer(PointTransformer transformer) =>
_controller.pointTransformer = transformer;
- @override
+ // @override
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) =>
_controller.addOnPlatformViewCreatedListener(listener);
- @override
- bool get awaitingCreation => _controller.awaitingCreation;
+ /// Beginning with flutter 3, [_controller.awaitingCreation] should be called.
+ ///
+ /// A false value is returned in order to consolidate usage with flutter 2.
+ /// A false return value does not necessarily indicate that the Future
+ /// returned by create has completed, only that creation has been started.
+ // @override
+ bool awaitingCreation = true;
- @override
+ // @override
Future clearFocus() => _controller.clearFocus();
- @override
- Future create({Size? size}) => _controller.create(size: size);
+ /// Beginning with flutter 3, [_controller.create] with [size] should be called.
+ ///
+ /// size is the view's initial size in logical pixel. size can be omitted
+ /// if the concrete implementation doesn't require an initial size to create
+ /// the platform view.
+ Future create({Size? size}) async {
+ await _controller.create();
+ awaitingCreation = false;
+ if (size != null) {
+ await _controller.setSize(size);
+ }
+ }
- @override
+ /// Beginning with flutter 3, [_controller.createdCallbacks] should be called.
+ ///
+ /// This is for testing purposes only and is not relevant for production code.
+ // @override
// ignore: invalid_use_of_visible_for_testing_member
- List get createdCallbacks =>
- _controller.createdCallbacks;
+ List get createdCallbacks => [];
- @override
+ // @override
Future dispatchPointerEvent(PointerEvent event) =>
_controller.dispatchPointerEvent(event);
- @override
- //! workaround for flutter 3.0
+ /// Beginning with flutter 3, disposal is called to soon, resulting in crashes.
+ ///
+ /// This is a workaround for users to be able to use this plugin with flutter 3.
+ // ignore: invalid_use_of_visible_for_testing_member
Future dispose() {
//? instead of this
// _controller.dispose();
@@ -69,32 +88,40 @@ class TextureAndroidViewControllerWrapper
return Future(() {});
}
- @override
+ // @override
bool get isCreated => _controller.isCreated;
- @override
+ // @override
void removeOnPlatformViewCreatedListener(
PlatformViewCreatedCallback listener) =>
_controller.removeOnPlatformViewCreatedListener(listener);
- @override
+ // @override
Future sendMotionEvent(AndroidMotionEvent event) =>
_controller.sendMotionEvent(event);
- @override
+ // @override
Future setLayoutDirection(TextDirection layoutDirection) =>
_controller.setLayoutDirection(layoutDirection);
- @override
- Future setOffset(Offset off) => _controller.setOffset(off);
-
- @override
- Future setSize(Size size) => _controller.setSize(size);
+ /// Beginning with flutter 3, [_controller.setOffset(off)] should be called.
+ ///
+ /// off is the view's new offset in logical pixel.
+ /// On Android, this allows the Android native view to draw the a11y highlights
+ /// in the same location on the screen as the platform view widget in the Flutter framework.
+ // @override
+ Future setOffset(Offset off) => Future(() => {});
+
+ // @override
+ Future setSize(Size size) async {
+ await _controller.setSize(size);
+ return size;
+ }
- @override
+ // @override
int? get textureId => _controller.textureId;
- @override
+ // @override
int get viewId => _controller.viewId;
}
@@ -291,7 +318,7 @@ class _CopyPastedAndroidPlatformView extends LeafRenderObjectWidget {
@override
void updateRenderObject(
BuildContext context, RenderAndroidView renderObject) {
- renderObject.controller = controller;
+ //renderObject.controller = controller;
renderObject.hitTestBehavior = hitTestBehavior;
renderObject.updateGestureRecognizers(gestureRecognizers);
renderObject.clipBehavior = clipBehavior;
From c6c4f5af3bb5efa78bcb9f163cf6d0f1ff30a66c Mon Sep 17 00:00:00 2001
From: Aaron Eri <36516690+arkeri@users.noreply.github.com>
Date: Wed, 26 Oct 2022 06:22:38 -0600
Subject: [PATCH 11/22] LayerId to match zoo example for mapbox base map
(#1186)
Co-authored-by: Aaron S Kennedy <36516690+aaronsamkennedy@users.noreply.github.com>
---
example/lib/map_ui.dart | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/example/lib/map_ui.dart b/example/lib/map_ui.dart
index b10a0334a..5be6b2bd8 100644
--- a/example/lib/map_ui.dart
+++ b/example/lib/map_ui.dart
@@ -351,7 +351,7 @@ class MapUiBodyState extends State {
"Map click: ${point.x},${point.y} ${latLng.latitude}/${latLng.longitude}");
print("Filter $_featureQueryFilter");
List features = await mapController!
- .queryRenderedFeatures(point, [], _featureQueryFilter);
+ .queryRenderedFeatures(point, ["landuse"], _featureQueryFilter);
print('# features: ${features.length}');
_clearFill();
if (features.isEmpty && _featureQueryFilter != null) {
From f505fcf5c79b75c165703028af77f3af62d382ae Mon Sep 17 00:00:00 2001
From: Thijs van der Burgt
Date: Wed, 26 Oct 2022 15:52:58 +0200
Subject: [PATCH 12/22] enable texture mode to fix black screens (#1216)
* enable texture mode to fix black screens
* run make format
Co-authored-by: Thijs van der Burgt
---
.../src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java
index a7a27e866..44121b20c 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java
@@ -14,7 +14,8 @@
class MapboxMapBuilder implements MapboxMapOptionsSink {
public final String TAG = getClass().getSimpleName();
- private final MapboxMapOptions options = new MapboxMapOptions().attributionEnabled(true);
+ private final MapboxMapOptions options =
+ new MapboxMapOptions().textureMode(true).attributionEnabled(true);
private boolean trackCameraPosition = false;
private boolean myLocationEnabled = false;
private boolean dragEnabled = true;
From dd20462f2db58a23f9a4bc0333f6a5398e57b479 Mon Sep 17 00:00:00 2001
From: Eduardo Folly
Date: Thu, 30 Mar 2023 14:58:16 -0300
Subject: [PATCH 13/22] Fixing view_wrappers to Flutter 3.7. (#1273)
---
mapbox_gl_platform_interface/lib/src/view_wrappers.dart | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
index ef11fe3fd..aee2c2a0b 100644
--- a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
+++ b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
@@ -34,6 +34,7 @@ class TextureAndroidViewControllerWrapper
// @override
PointTransformer get pointTransformer => _controller.pointTransformer;
+
set pointTransformer(PointTransformer transformer) =>
_controller.pointTransformer = transformer;
@@ -57,11 +58,14 @@ class TextureAndroidViewControllerWrapper
/// size is the view's initial size in logical pixel. size can be omitted
/// if the concrete implementation doesn't require an initial size to create
/// the platform view.
- Future create({Size? size}) async {
+ Future create({Size? size, Offset? position}) async {
await _controller.create();
awaitingCreation = false;
if (size != null) {
await _controller.setSize(size);
+ if (position != null) {
+ await _controller.setOffset(position);
+ }
}
}
@@ -123,6 +127,9 @@ class TextureAndroidViewControllerWrapper
// @override
int get viewId => _controller.viewId;
+
+ // @override
+ bool get requiresViewComposition => _controller.requiresViewComposition;
}
class AndroidViewWithWrappedController extends StatefulWidget {
From 9eef30d649d4b62fc000a7ebc38bb79cdda78e14 Mon Sep 17 00:00:00 2001
From: Felix Horvat
Date: Thu, 6 Apr 2023 19:31:02 +0200
Subject: [PATCH 14/22] Fix flutter 3.x crashes (#1293)
* fix flutter crash
* fix crash
* fix format
* refactoring
---
.../mapbox/mapboxgl/MapboxMapController.java | 4 +-
lib/src/mapbox_map.dart | 5 +-
.../lib/mapbox_gl_platform_interface.dart | 1 -
.../lib/src/method_channel_mapbox_gl.dart | 38 +-
.../lib/src/view_wrappers.dart | 333 ------------------
pubspec.lock | 54 +--
6 files changed, 47 insertions(+), 388 deletions(-)
delete mode 100644 mapbox_gl_platform_interface/lib/src/view_wrappers.dart
diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
index 22f9d28a8..e8ca9d319 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
@@ -1554,12 +1554,14 @@ private void destroyMapViewIfNecessary() {
return;
}
+ mapView.onStop();
+ mapView.onDestroy();
+
if (locationComponent != null) {
locationComponent.setLocationComponentEnabled(false);
}
stopListeningForLocationUpdates();
- mapView.onDestroy();
mapView = null;
}
diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart
index 34b380acc..6649c8c24 100644
--- a/lib/src/mapbox_map.dart
+++ b/lib/src/mapbox_map.dart
@@ -224,8 +224,8 @@ class MapboxMap extends StatefulWidget {
/// * All fade/transition animations have completed
final OnMapIdleCallback? onMapIdle;
- /// Use delayed disposal of Android View Controller to avoid flutter 3.x.x crashes
- /// Use with caution - this is not yet production ready since several users still report crashes after using this workaround
+ // This flag has no effect anymore and will be removed in the next major release.
+ @deprecated
final bool? useDelayedDisposal;
/// Override hybrid mode per map instance
@@ -261,7 +261,6 @@ class _MapboxMapState extends State {
'accessToken': widget.accessToken,
'onAttributionClickOverride': widget.onAttributionClick != null,
'dragEnabled': widget.dragEnabled,
- 'useDelayedDisposal': widget.useDelayedDisposal,
'useHybridCompositionOverride': widget.useHybridCompositionOverride,
};
return _mapboxGlPlatform.buildView(
diff --git a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
index 748773d4e..5281f1f0d 100644
--- a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart
@@ -11,7 +11,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
-part 'src/view_wrappers.dart';
part 'src/annotation.dart';
part 'src/callbacks.dart';
part 'src/camera.dart';
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index ebfe7963b..e54cf7cae 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -141,8 +141,6 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
OnPlatformViewCreatedCallback onPlatformViewCreated,
Set>? gestureRecognizers) {
if (defaultTargetPlatform == TargetPlatform.android) {
- final useDelayedDisposalParam =
- (creationParams['useDelayedDisposal'] ?? false) as bool;
final useHybridCompositionParam =
(creationParams['useHybridCompositionOverride'] ??
useHybridComposition) as bool;
@@ -162,25 +160,14 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
},
onCreatePlatformView: (PlatformViewCreationParams params) {
late AndroidViewController controller;
- if (useDelayedDisposalParam) {
- controller = WrappedPlatformViewsService.initAndroidView(
- id: params.id,
- viewType: 'plugins.flutter.io/mapbox_gl',
- layoutDirection: TextDirection.ltr,
- creationParams: creationParams,
- creationParamsCodec: const StandardMessageCodec(),
- onFocus: () => params.onFocusChanged(true),
- );
- } else {
- controller = PlatformViewsService.initAndroidView(
- id: params.id,
- viewType: 'plugins.flutter.io/mapbox_gl',
- layoutDirection: TextDirection.ltr,
- creationParams: creationParams,
- creationParamsCodec: const StandardMessageCodec(),
- onFocus: () => params.onFocusChanged(true),
- );
- }
+ controller = PlatformViewsService.initAndroidView(
+ id: params.id,
+ viewType: 'plugins.flutter.io/mapbox_gl',
+ layoutDirection: TextDirection.ltr,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ onFocus: () => params.onFocusChanged(true),
+ );
controller.addOnPlatformViewCreatedListener(
params.onPlatformViewCreated,
);
@@ -193,15 +180,6 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
},
);
} else {
- if (useDelayedDisposalParam) {
- return AndroidViewWithWrappedController(
- viewType: 'plugins.flutter.io/mapbox_gl',
- onPlatformViewCreated: onPlatformViewCreated,
- gestureRecognizers: gestureRecognizers,
- creationParams: creationParams,
- creationParamsCodec: const StandardMessageCodec(),
- );
- }
return AndroidView(
viewType: 'plugins.flutter.io/mapbox_gl',
onPlatformViewCreated: onPlatformViewCreated,
diff --git a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart b/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
deleted file mode 100644
index aee2c2a0b..000000000
--- a/mapbox_gl_platform_interface/lib/src/view_wrappers.dart
+++ /dev/null
@@ -1,333 +0,0 @@
-part of mapbox_gl_platform_interface;
-
-/// This file wrapps AndroidViewController classes in order to delay disposal process.
-/// It is an workaround for flutter 3, where resourses get disposed quicker than before, while Mapbox behaves badly
-/// and tries to access those resources after they had been disposed, resulting in a native crash.
-
-class WrappedPlatformViewsService {
- static AndroidViewController initAndroidView({
- required int id,
- required String viewType,
- required TextDirection layoutDirection,
- dynamic creationParams,
- MessageCodec? creationParamsCodec,
- VoidCallback? onFocus,
- }) {
- final view = PlatformViewsService.initAndroidView(
- id: id,
- viewType: viewType,
- layoutDirection: layoutDirection,
- creationParams: creationParams,
- creationParamsCodec: creationParamsCodec,
- onFocus: onFocus,
- );
- return TextureAndroidViewControllerWrapper(
- view as TextureAndroidViewController);
- }
-}
-
-class TextureAndroidViewControllerWrapper
- implements TextureAndroidViewController {
- TextureAndroidViewControllerWrapper(this._controller);
-
- final TextureAndroidViewController _controller;
-
- // @override
- PointTransformer get pointTransformer => _controller.pointTransformer;
-
- set pointTransformer(PointTransformer transformer) =>
- _controller.pointTransformer = transformer;
-
- // @override
- void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) =>
- _controller.addOnPlatformViewCreatedListener(listener);
-
- /// Beginning with flutter 3, [_controller.awaitingCreation] should be called.
- ///
- /// A false value is returned in order to consolidate usage with flutter 2.
- /// A false return value does not necessarily indicate that the Future
- /// returned by create has completed, only that creation has been started.
- // @override
- bool awaitingCreation = true;
-
- // @override
- Future clearFocus() => _controller.clearFocus();
-
- /// Beginning with flutter 3, [_controller.create] with [size] should be called.
- ///
- /// size is the view's initial size in logical pixel. size can be omitted
- /// if the concrete implementation doesn't require an initial size to create
- /// the platform view.
- Future create({Size? size, Offset? position}) async {
- await _controller.create();
- awaitingCreation = false;
- if (size != null) {
- await _controller.setSize(size);
- if (position != null) {
- await _controller.setOffset(position);
- }
- }
- }
-
- /// Beginning with flutter 3, [_controller.createdCallbacks] should be called.
- ///
- /// This is for testing purposes only and is not relevant for production code.
- // @override
- // ignore: invalid_use_of_visible_for_testing_member
- List get createdCallbacks => [];
-
- // @override
- Future dispatchPointerEvent(PointerEvent event) =>
- _controller.dispatchPointerEvent(event);
-
- /// Beginning with flutter 3, disposal is called to soon, resulting in crashes.
- ///
- /// This is a workaround for users to be able to use this plugin with flutter 3.
- // ignore: invalid_use_of_visible_for_testing_member
- Future dispose() {
- //? instead of this
- // _controller.dispose();
- //? we do this
- unawaited(Future.delayed(Duration(seconds: 5), _controller.dispose));
- return Future(() {});
- }
-
- // @override
- bool get isCreated => _controller.isCreated;
-
- // @override
- void removeOnPlatformViewCreatedListener(
- PlatformViewCreatedCallback listener) =>
- _controller.removeOnPlatformViewCreatedListener(listener);
-
- // @override
- Future sendMotionEvent(AndroidMotionEvent event) =>
- _controller.sendMotionEvent(event);
-
- // @override
- Future setLayoutDirection(TextDirection layoutDirection) =>
- _controller.setLayoutDirection(layoutDirection);
-
- /// Beginning with flutter 3, [_controller.setOffset(off)] should be called.
- ///
- /// off is the view's new offset in logical pixel.
- /// On Android, this allows the Android native view to draw the a11y highlights
- /// in the same location on the screen as the platform view widget in the Flutter framework.
- // @override
- Future setOffset(Offset off) => Future(() => {});
-
- // @override
- Future setSize(Size size) async {
- await _controller.setSize(size);
- return size;
- }
-
- // @override
- int? get textureId => _controller.textureId;
-
- // @override
- int get viewId => _controller.viewId;
-
- // @override
- bool get requiresViewComposition => _controller.requiresViewComposition;
-}
-
-class AndroidViewWithWrappedController extends StatefulWidget {
- const AndroidViewWithWrappedController({
- Key? key,
- required this.viewType,
- this.onPlatformViewCreated,
- this.hitTestBehavior = PlatformViewHitTestBehavior.opaque,
- this.layoutDirection,
- this.gestureRecognizers,
- this.creationParams,
- this.creationParamsCodec,
- this.clipBehavior = Clip.hardEdge,
- }) : assert(viewType != null),
- assert(hitTestBehavior != null),
- assert(creationParams == null || creationParamsCodec != null),
- assert(clipBehavior != null),
- super(key: key);
-
- final String viewType;
- final PlatformViewCreatedCallback? onPlatformViewCreated;
- final PlatformViewHitTestBehavior hitTestBehavior;
- final TextDirection? layoutDirection;
- final Set>? gestureRecognizers;
- final dynamic creationParams;
- final MessageCodec? creationParamsCodec;
- final Clip clipBehavior;
-
- @override
- State createState() =>
- _AndroidViewWithWrappedControllerState();
-}
-
-class _AndroidViewWithWrappedControllerState
- extends State {
- int? _id;
- late AndroidViewController _controller;
- TextDirection? _layoutDirection;
- bool _initialized = false;
- FocusNode? _focusNode;
-
- static final Set> _emptyRecognizersSet =
- >{};
-
- @override
- Widget build(BuildContext context) {
- return Focus(
- focusNode: _focusNode,
- onFocusChange: _onFocusChange,
- child: _CopyPastedAndroidPlatformView(
- controller: _controller,
- hitTestBehavior: widget.hitTestBehavior,
- gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet,
- clipBehavior: widget.clipBehavior,
- ),
- );
- }
-
- void _initializeOnce() {
- if (_initialized) {
- return;
- }
- _initialized = true;
- _createNewAndroidView();
- _focusNode = FocusNode(debugLabel: 'AndroidView(id: $_id)');
- }
-
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- final TextDirection newLayoutDirection = _findLayoutDirection();
- final bool didChangeLayoutDirection =
- _layoutDirection != newLayoutDirection;
- _layoutDirection = newLayoutDirection;
-
- _initializeOnce();
- if (didChangeLayoutDirection) {
- // The native view will update asynchronously, in the meantime we don't want
- // to block the framework. (so this is intentionally not awaiting).
- _controller.setLayoutDirection(_layoutDirection!);
- }
- }
-
- @override
- void didUpdateWidget(AndroidViewWithWrappedController oldWidget) {
- super.didUpdateWidget(oldWidget);
-
- final TextDirection newLayoutDirection = _findLayoutDirection();
- final bool didChangeLayoutDirection =
- _layoutDirection != newLayoutDirection;
- _layoutDirection = newLayoutDirection;
-
- if (widget.viewType != oldWidget.viewType) {
- _controller.dispose();
- _createNewAndroidView();
- return;
- }
-
- if (didChangeLayoutDirection) {
- _controller.setLayoutDirection(_layoutDirection!);
- }
- }
-
- TextDirection _findLayoutDirection() {
- assert(
- widget.layoutDirection != null || debugCheckHasDirectionality(context));
- return widget.layoutDirection ?? Directionality.of(context);
- }
-
- @override
- void dispose() {
- _controller.dispose();
- super.dispose();
- }
-
- void _createNewAndroidView() {
- _id = platformViewsRegistry.getNextPlatformViewId();
- _controller = WrappedPlatformViewsService.initAndroidView(
- id: _id!,
- viewType: widget.viewType,
- layoutDirection: _layoutDirection!,
- creationParams: widget.creationParams,
- creationParamsCodec: widget.creationParamsCodec,
- onFocus: () {
- _focusNode!.requestFocus();
- },
- );
- if (widget.onPlatformViewCreated != null) {
- _controller
- .addOnPlatformViewCreatedListener(widget.onPlatformViewCreated!);
- }
- }
-
- void _onFocusChange(bool isFocused) {
- if (!_controller.isCreated) {
- return;
- }
- if (!isFocused) {
- _controller.clearFocus().catchError((dynamic e) {
- if (e is MissingPluginException) {
- // We land the framework part of Android platform views keyboard
- // support before the engine part. There will be a commit range where
- // clearFocus isn't implemented in the engine. When that happens we
- // just swallow the error here. Once the engine part is rolled to the
- // framework I'll remove this.
- // TODO(amirh): remove this once the engine's clearFocus is rolled.
- return;
- }
- });
- return;
- }
- SystemChannels.textInput.invokeMethod(
- 'TextInput.setPlatformViewClient',
- {'platformViewId': _id},
- ).catchError((dynamic e) {
- if (e is MissingPluginException) {
- // We land the framework part of Android platform views keyboard
- // support before the engine part. There will be a commit range where
- // setPlatformViewClient isn't implemented in the engine. When that
- // happens we just swallow the error here. Once the engine part is
- // rolled to the framework I'll remove this.
- // TODO(amirh): remove this once the engine's clearFocus is rolled.
- return;
- }
- });
- }
-}
-
-class _CopyPastedAndroidPlatformView extends LeafRenderObjectWidget {
- const _CopyPastedAndroidPlatformView({
- required this.controller,
- required this.hitTestBehavior,
- required this.gestureRecognizers,
- this.clipBehavior = Clip.hardEdge,
- }) : assert(controller != null),
- assert(hitTestBehavior != null),
- assert(gestureRecognizers != null),
- assert(clipBehavior != null);
-
- final AndroidViewController controller;
- final PlatformViewHitTestBehavior hitTestBehavior;
- final Set> gestureRecognizers;
- final Clip clipBehavior;
-
- @override
- RenderObject createRenderObject(BuildContext context) => RenderAndroidView(
- viewController: controller,
- hitTestBehavior: hitTestBehavior,
- gestureRecognizers: gestureRecognizers,
- clipBehavior: clipBehavior,
- );
-
- @override
- void updateRenderObject(
- BuildContext context, RenderAndroidView renderObject) {
- //renderObject.controller = controller;
- renderObject.hitTestBehavior = hitTestBehavior;
- renderObject.updateGestureRecognizers(gestureRecognizers);
- renderObject.clipBehavior = clipBehavior;
- }
-}
diff --git a/pubspec.lock b/pubspec.lock
index 194fe4780..3064bfbed 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,28 +5,32 @@ packages:
dependency: transitive
description:
name: archive
- url: "https://pub.dartlang.org"
+ sha256: a92e39b291073bb840a72cf43d96d2a63c74e9a485d227833e8ea0054d16ad16
+ url: "https://pub.dev"
source: hosted
version: "3.1.2"
characters:
dependency: transitive
description:
name: characters
- url: "https://pub.dartlang.org"
+ sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
+ url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.2.1"
collection:
dependency: "direct main"
description:
name: collection
- url: "https://pub.dartlang.org"
+ sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
+ url: "https://pub.dev"
source: hosted
- version: "1.16.0"
+ version: "1.17.0"
crypto:
dependency: transitive
description:
name: crypto
- url: "https://pub.dartlang.org"
+ sha256: cf75650c66c0316274e21d7c43d3dea246273af5955bd94e8184837cd577575c
+ url: "https://pub.dev"
source: hosted
version: "3.0.1"
flutter:
@@ -43,21 +47,24 @@ packages:
dependency: transitive
description:
name: image
- url: "https://pub.dartlang.org"
+ sha256: "3e5c9ef82c0af7823be4cb5294a829a6e0548a6f6b4e261e6386509a9e03bcab"
+ url: "https://pub.dev"
source: hosted
version: "3.0.2"
js:
dependency: transitive
description:
name: js
- url: "https://pub.dartlang.org"
+ sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
+ url: "https://pub.dev"
source: hosted
- version: "0.6.4"
+ version: "0.6.5"
mapbox_gl_dart:
dependency: transitive
description:
name: mapbox_gl_dart
- url: "https://pub.dartlang.org"
+ sha256: de6d03718e5eb05c9eb1ddaae7f0383b28acb5afa16405e1deed7ff04dd34f3d
+ url: "https://pub.dev"
source: hosted
version: "0.2.1"
mapbox_gl_platform_interface:
@@ -78,28 +85,32 @@ packages:
dependency: transitive
description:
name: material_color_utilities
- url: "https://pub.dartlang.org"
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
+ url: "https://pub.dev"
source: hosted
- version: "0.1.4"
+ version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
- url: "https://pub.dartlang.org"
+ sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
+ url: "https://pub.dev"
source: hosted
- version: "1.7.0"
+ version: "1.8.0"
path:
dependency: transitive
description:
name: path
- url: "https://pub.dartlang.org"
+ sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb"
+ url: "https://pub.dev"
source: hosted
version: "1.8.0"
petitparser:
dependency: transitive
description:
name: petitparser
- url: "https://pub.dartlang.org"
+ sha256: "85e8f8b118afcccf948a9844d199e56260117400bd9b9982d87bf1d62ebc1690"
+ url: "https://pub.dev"
source: hosted
version: "4.1.0"
sky_engine:
@@ -111,21 +122,24 @@ packages:
dependency: transitive
description:
name: typed_data
- url: "https://pub.dartlang.org"
+ sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee"
+ url: "https://pub.dev"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
- url: "https://pub.dartlang.org"
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.4"
xml:
dependency: transitive
description:
name: xml
- url: "https://pub.dartlang.org"
+ sha256: "88d26fad429944d29b7c418177d156d04bbef049f295cf48130eccc84f0b8b4b"
+ url: "https://pub.dev"
source: hosted
version: "5.1.0"
sdks:
From 622146f649bdf29410dca73ddcb4ec818916f7af Mon Sep 17 00:00:00 2001
From: Simon Irmancnik
Date: Fri, 14 Apr 2023 15:09:59 +0200
Subject: [PATCH 15/22] Setting style as a json object directly for web (#1279)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Simon Irmančnik
---
mapbox_gl_web/lib/mapbox_gl_web.dart | 2 ++
mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart | 8 +++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/mapbox_gl_web/lib/mapbox_gl_web.dart b/mapbox_gl_web/lib/mapbox_gl_web.dart
index 70756d3be..0c154a4e6 100644
--- a/mapbox_gl_web/lib/mapbox_gl_web.dart
+++ b/mapbox_gl_web/lib/mapbox_gl_web.dart
@@ -1,6 +1,7 @@
library mapbox_gl_web;
import 'dart:async';
+import 'dart:convert';
// FIXED HERE: https://github.com/dart-lang/linter/pull/1985
// ignore_for_file: avoid_web_libraries_in_flutter
import 'dart:html';
@@ -21,6 +22,7 @@ import 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart';
import 'package:mapbox_gl_dart/mapbox_gl_dart.dart' hide Point, Source;
import 'package:mapbox_gl_dart/mapbox_gl_dart.dart' as mapbox show Point;
import 'package:image/image.dart' hide Point;
+import 'package:js/js_util.dart' as jsUtil;
import 'package:mapbox_gl_web/src/layer_tools.dart';
part 'src/convert.dart';
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index 2b4a69f83..e447db391 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -641,7 +641,13 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
}
_interactiveFeatureLayerIds.clear();
- _map.setStyle(styleString);
+ try {
+ final styleJson = jsonDecode(styleString ?? '');
+ final styleJsObject = jsUtil.jsify(styleJson);
+ _map.setStyle(styleJsObject);
+ } catch(_) {
+ _map.setStyle(styleString);
+ }
// catch style loaded for later style changes
if (_mapReady) {
_map.once("styledata", _onStyleLoaded);
From 61b435e0143d8c41b5d1cee138cf25c00a013919 Mon Sep 17 00:00:00 2001
From: Felix Horvat
Date: Mon, 17 Apr 2023 21:35:47 +0200
Subject: [PATCH 16/22] Fix annotation manager crashes (#1297)
* fixed crash caused be race condition in layer deletion
* better layer ids for annotations
* added overlap button to example
---
.../mapbox/mapboxgl/MapboxMapController.java | 17 ++++++-
example/lib/click_annotations.dart | 44 +++++++++++++------
ios/Classes/MapboxMapController.swift | 23 +++++++---
lib/src/annotation_manager.dart | 21 ++++++---
lib/src/controller.dart | 2 +-
.../lib/src/mapbox_web_gl_platform.dart | 10 +++--
6 files changed, 87 insertions(+), 30 deletions(-)
diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
index e8ca9d319..cb9046a83 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
@@ -912,6 +912,7 @@ public void onError(@NonNull String message) {
Expression filterExpression = parseFilter(filter);
+ removeLayer(layerId);
addSymbolLayer(
layerId,
sourceId,
@@ -942,6 +943,7 @@ public void onError(@NonNull String message) {
Expression filterExpression = parseFilter(filter);
+ removeLayer(layerId);
addLineLayer(
layerId,
sourceId,
@@ -972,6 +974,7 @@ public void onError(@NonNull String message) {
Expression filterExpression = parseFilter(filter);
+ removeLayer(layerId);
addFillLayer(
layerId,
sourceId,
@@ -1003,6 +1006,7 @@ public void onError(@NonNull String message) {
Expression filterExpression = parseFilter(filter);
+ removeLayer(layerId);
addFillExtrusionLayer(
layerId,
sourceId,
@@ -1033,6 +1037,7 @@ public void onError(@NonNull String message) {
Expression filterExpression = parseFilter(filter);
+ removeLayer(layerId);
addCircleLayer(
layerId,
sourceId,
@@ -1057,6 +1062,8 @@ public void onError(@NonNull String message) {
final Double maxzoom = call.argument("maxzoom");
final PropertyValue[] properties =
LayerPropertyConverter.interpretRasterLayerProperties(call.argument("properties"));
+
+ removeLayer(layerId);
addRasterLayer(
layerId,
sourceId,
@@ -1281,8 +1288,7 @@ public void onFailure(@NonNull Exception exception) {
null);
}
String layerId = call.argument("layerId");
- style.removeLayer(layerId);
- interactiveFeatureLayerIds.remove(layerId);
+ removeLayer(layerId);
result.success(null);
break;
@@ -1958,6 +1964,13 @@ boolean onMove(MoveGestureDetector detector) {
return true;
}
+ void removeLayer(String layerId) {
+ if (style != null && layerId != null) {
+ style.removeLayer(layerId);
+ interactiveFeatureLayerIds.remove(layerId);
+ }
+ }
+
void onMoveEnd(MoveGestureDetector detector) {
PointF pointf = detector.getFocalPoint();
invokeFeatureDrag(pointf, "end");
diff --git a/example/lib/click_annotations.dart b/example/lib/click_annotations.dart
index 1a43c3b84..77a1876ac 100644
--- a/example/lib/click_annotations.dart
+++ b/example/lib/click_annotations.dart
@@ -28,6 +28,7 @@ class ClickAnnotationBody extends StatefulWidget {
class ClickAnnotationBodyState extends State {
ClickAnnotationBodyState();
static const LatLng center = const LatLng(-33.88, 151.16);
+ bool overlapping = false;
MapboxMapController? controller;
@@ -131,20 +132,37 @@ class ClickAnnotationBodyState extends State {
@override
Widget build(BuildContext context) {
- return MapboxMap(
- accessToken: MapsDemo.ACCESS_TOKEN,
- annotationOrder: [
- AnnotationType.fill,
- AnnotationType.line,
- AnnotationType.circle,
- AnnotationType.symbol,
- ],
- onMapCreated: _onMapCreated,
- onStyleLoadedCallback: _onStyleLoaded,
- initialCameraPosition: const CameraPosition(
- target: center,
- zoom: 12.0,
+ return Scaffold(
+ body: MapboxMap(
+ accessToken: MapsDemo.ACCESS_TOKEN,
+ annotationOrder: [
+ AnnotationType.fill,
+ AnnotationType.line,
+ AnnotationType.circle,
+ AnnotationType.symbol,
+ ],
+ onMapCreated: _onMapCreated,
+ onStyleLoadedCallback: _onStyleLoaded,
+ initialCameraPosition: const CameraPosition(
+ target: center,
+ zoom: 12.0,
+ ),
),
+ floatingActionButton: ElevatedButton(
+ onPressed: () {
+ setState(() {
+ overlapping = !overlapping;
+ });
+ controller!.setSymbolIconAllowOverlap(overlapping);
+ controller!.setSymbolIconIgnorePlacement(overlapping);
+
+ controller!.setSymbolTextAllowOverlap(overlapping);
+ controller!.setSymbolTextIgnorePlacement(overlapping);
+ },
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text("Toggle overlapping"),
+ )),
);
}
}
diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift
index f9d56dbae..a4f939095 100644
--- a/ios/Classes/MapboxMapController.swift
+++ b/ios/Classes/MapboxMapController.swift
@@ -354,6 +354,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let maxzoom = arguments["maxzoom"] as? Double
let filter = arguments["filter"] as? String
+ removeLayer(layerId: layerId)
let addResult = addSymbolLayer(
sourceId: sourceId,
layerId: layerId,
@@ -382,6 +383,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let maxzoom = arguments["maxzoom"] as? Double
let filter = arguments["filter"] as? String
+ removeLayer(layerId: layerId)
let addResult = addLineLayer(
sourceId: sourceId,
layerId: layerId,
@@ -410,6 +412,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let maxzoom = arguments["maxzoom"] as? Double
let filter = arguments["filter"] as? String
+ removeLayer(layerId: layerId)
let addResult = addFillLayer(
sourceId: sourceId,
layerId: layerId,
@@ -438,6 +441,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let maxzoom = arguments["maxzoom"] as? Double
let filter = arguments["filter"] as? String
+ removeLayer(layerId: layerId)
let addResult = addFillExtrusionLayer(
sourceId: sourceId,
layerId: layerId,
@@ -466,6 +470,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let maxzoom = arguments["maxzoom"] as? Double
let filter = arguments["filter"] as? String
+ removeLayer(layerId: layerId)
let addResult = addCircleLayer(
sourceId: sourceId,
layerId: layerId,
@@ -490,6 +495,8 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let belowLayerId = arguments["belowLayerId"] as? String
let minzoom = arguments["minzoom"] as? Double
let maxzoom = arguments["maxzoom"] as? Double
+
+ removeLayer(layerId: layerId)
addHillshadeLayer(
sourceId: sourceId,
layerId: layerId,
@@ -508,6 +515,8 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
let belowLayerId = arguments["belowLayerId"] as? String
let minzoom = arguments["minzoom"] as? Double
let maxzoom = arguments["maxzoom"] as? Double
+
+ removeLayer(layerId: layerId)
addHeatmapLayer(
sourceId: sourceId,
layerId: layerId,
@@ -730,12 +739,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
case "style#removeLayer":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let layerId = arguments["layerId"] as? String else { return }
- guard let layer = mapView.style?.layer(withIdentifier: layerId) else {
- result(nil)
- return
- }
- interactiveFeatureLayerIds.remove(layerId)
- mapView.style?.removeLayer(layer)
+ removeLayer(layerId: layerId)
result(nil)
case "style#setFilter":
@@ -897,6 +901,13 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
}
}
+ private func removeLayer(layerId: String) {
+ if let layer = mapView.style?.layer(withIdentifier: layerId) {
+ mapView.style?.removeLayer(layer)
+ interactiveFeatureLayerIds.remove(layerId)
+ }
+ }
+
private func loadIconImage(name: String) -> UIImage? {
// Build up the full path of the asset.
// First find the last '/' ans split the image name in the asset directory and the image file name.
diff --git a/lib/src/annotation_manager.dart b/lib/src/annotation_manager.dart
index 2aa47c0c8..65bb22426 100644
--- a/lib/src/annotation_manager.dart
+++ b/lib/src/annotation_manager.dart
@@ -9,7 +9,11 @@ abstract class AnnotationManager {
final void Function(T)? onTap;
/// base id of the manager. User [layerdIds] to get the actual ids.
- final String id;
+ String get id => "${managerType}_$randomPostFix";
+
+ final String managerType;
+
+ final String randomPostFix;
List get layerIds =>
[for (int i = 0; i < allLayerProperties.length; i++) _makeLayerId(i)];
@@ -29,9 +33,13 @@ abstract class AnnotationManager {
Set get annotations => _idToAnnotation.values.toSet();
- AnnotationManager(this.controller,
- {this.onTap, this.selectLayer, required this.enableInteraction})
- : id = getRandomString() {
+ AnnotationManager(
+ this.controller, {
+ required this.managerType,
+ this.onTap,
+ this.selectLayer,
+ required this.enableInteraction,
+ }) : randomPostFix = getRandomString() {
for (var i = 0; i < allLayerProperties.length; i++) {
final layerId = _makeLayerId(i);
controller.addGeoJsonSource(layerId, buildFeatureCollection([]),
@@ -50,7 +58,6 @@ abstract class AnnotationManager {
Future _rebuildLayers() async {
for (var i = 0; i < allLayerProperties.length; i++) {
final layerId = _makeLayerId(i);
- await controller.removeLayer(layerId);
await controller.addLayer(layerId, layerId, allLayerProperties[i]);
}
}
@@ -172,6 +179,7 @@ class LineManager extends AnnotationManager {
{void Function(Line)? onTap, bool enableInteraction = true})
: super(
controller,
+ managerType: "line",
onTap: onTap,
enableInteraction: enableInteraction,
selectLayer: (Line line) => line.options.linePattern == null ? 0 : 1,
@@ -201,6 +209,7 @@ class FillManager extends AnnotationManager {
bool enableInteraction = true,
}) : super(
controller,
+ managerType: "fill",
onTap: onTap,
enableInteraction: enableInteraction,
selectLayer: (Fill fill) => fill.options.fillPattern == null ? 0 : 1,
@@ -228,6 +237,7 @@ class CircleManager extends AnnotationManager {
bool enableInteraction = true,
}) : super(
controller,
+ managerType: "circle",
enableInteraction: enableInteraction,
onTap: onTap,
);
@@ -260,6 +270,7 @@ class SymbolManager extends AnnotationManager {
_textIgnorePlacement = textIgnorePlacement,
super(
controller,
+ managerType: "symbol",
enableInteraction: enableInteraction,
onTap: onTap,
);
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index f58a17bb9..eb707b185 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -1241,7 +1241,7 @@ class MapboxMapController extends ChangeNotifier {
/// Add a layer to the map with the given properties
///
/// The returned [Future] completes after the change has been made on the
- /// platform side.
+ /// platform side. If the layer already exists, the layer is updated.
///
/// Setting [belowLayerId] adds the new layer below the given id.
/// If [enableInteraction] is set the layer is considered for touch or drag
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index e447db391..807134991 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -645,7 +645,7 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
final styleJson = jsonDecode(styleString ?? '');
final styleJsObject = jsUtil.jsify(styleJson);
_map.setStyle(styleJsObject);
- } catch(_) {
+ } catch (_) {
_map.setStyle(styleString);
}
// catch style loaded for later style changes
@@ -692,8 +692,10 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
@override
Future removeLayer(String layerId) async {
- _interactiveFeatureLayerIds.remove(layerId);
- _map.removeLayer(layerId);
+ if (_map.getLayer(layerId) != null) {
+ _interactiveFeatureLayerIds.remove(layerId);
+ _map.removeLayer(layerId);
+ }
}
@override
@@ -884,6 +886,8 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
final paint = Map.fromEntries(
properties.entries.where((entry) => !isLayoutProperty(entry.key)));
+ removeLayer(layerId);
+
_map.addLayer({
'id': layerId,
'type': layerType,
From 318078a937858e34452555f375020d4a0fe4327b Mon Sep 17 00:00:00 2001
From: Simon Irmancnik
Date: Fri, 5 May 2023 22:51:17 +0200
Subject: [PATCH 17/22] Implementation of toggling layer visibility (#1197)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Implementation of toggling layer visibility
* Added documentation to setVisibility, added check if layerId exists
* Fixed linter errors
Co-authored-by: Simon Irmančnik
---
.../mapbox/mapboxgl/MapboxMapController.java | 21 +++++++++++++++++++
example/lib/layer.dart | 8 +++++++
ios/Classes/MapboxMapController.swift | 11 ++++++++++
lib/src/controller.dart | 7 +++++++
.../lib/src/mapbox_gl_platform_interface.dart | 2 ++
.../lib/src/method_channel_mapbox_gl.dart | 10 +++++++++
.../lib/src/mapbox_web_gl_platform.dart | 9 ++++++++
7 files changed, 68 insertions(+)
diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
index cb9046a83..3499d4ef5 100644
--- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
+++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
@@ -4,6 +4,9 @@
package com.mapbox.mapboxgl;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -1330,6 +1333,24 @@ public void onFailure(@NonNull Exception exception) {
break;
}
+ result.success(null);
+ break;
+ }
+ case "style#setVisibility":
+ {
+ if (style == null) {
+ result.error(
+ "STYLE IS NULL",
+ "The style is null. Has onStyleLoaded() already been invoked?",
+ null);
+ }
+ String layerId = call.argument("layerId");
+ boolean isVisible = call.argument("isVisible");
+ Layer layer = style.getLayer(layerId);
+ if (layer != null) {
+ layer.setProperties(isVisible ? visibility(VISIBLE) : visibility(NONE));
+ }
+
result.success(null);
break;
}
diff --git a/example/lib/layer.dart b/example/lib/layer.dart
index 240383629..bdcd8828e 100644
--- a/example/lib/layer.dart
+++ b/example/lib/layer.dart
@@ -24,7 +24,9 @@ class LayerState extends State {
late MapboxMapController controller;
Timer? bikeTimer;
Timer? filterTimer;
+ Timer? visibilityTimer;
int filteredId = 0;
+ bool isVisible = true;
@override
Widget build(BuildContext context) {
@@ -148,12 +150,18 @@ class LayerState extends State {
filteredId = filteredId == 0 ? 1 : 0;
controller.setFilter('fills', ['==', 'id', filteredId]);
});
+
+ visibilityTimer = Timer.periodic(Duration(seconds: 5), (t) {
+ isVisible = !isVisible;
+ controller.setVisibility('water', isVisible);
+ });
}
@override
void dispose() {
bikeTimer?.cancel();
filterTimer?.cancel();
+ visibilityTimer?.cancel();
super.dispose();
}
}
diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift
index a4f939095..e9b992653 100644
--- a/ios/Classes/MapboxMapController.swift
+++ b/ios/Classes/MapboxMapController.swift
@@ -755,6 +755,17 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
case let .failure(error): result(error.flutterError)
}
+ case "map#setVisibility":
+ guard let arguments = methodCall.arguments as? [String: Any] else { return }
+ guard let layerId = arguments["layerId"] as? String else { return }
+ guard let isVisible = arguments["isVisible"] as? Bool else { return }
+ guard let layer = mapView.style?.layer(withIdentifier: layerId) else {
+ result(nil)
+ return
+ }
+ layer.isVisible = isVisible
+ result(nil)
+
case "source#addGeoJson":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index eb707b185..4c97274fa 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -1208,6 +1208,13 @@ class MapboxMapController extends ChangeNotifier {
return _mapboxGlPlatform.setFilter(layerId, filter);
}
+ /// Sets the visibility by specifying [isVisible] of the layer with
+ /// the specified id [layerId].
+ /// Returns silently if [layerId] does not exist.
+ Future setVisibility(String layerId, bool isVisible) {
+ return _mapboxGlPlatform.setVisibility(layerId, isVisible);
+ }
+
/// Returns the point on the screen that corresponds to a geographical coordinate ([latLng]). The screen location is in screen pixels (not display pixels) relative to the top left of the map (not of the whole screen)
///
/// Note: The resulting x and y coordinates are rounded to [int] on web, on other platforms they may differ very slightly (in the range of about 10^-10) from the actual nearest screen coordinate.
diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
index c542fdea8..346998bd0 100644
--- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
+++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart
@@ -94,6 +94,8 @@ abstract class MapboxGlPlatform {
Future setFilter(String layerId, dynamic filter);
+ Future setVisibility(String layerId, bool isVisible);
+
Future toScreenLocation(LatLng latLng);
Future> toScreenLocationBatch(Iterable latLngs);
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index e54cf7cae..28a8e268c 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -504,6 +504,16 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
}
}
+ @override
+ Future setVisibility(String layerId, bool isVisible) async {
+ try {
+ return await _channel.invokeMethod('style#setVisibility',
+ {'layerId': layerId, 'isVisible': isVisible});
+ } on PlatformException catch (e) {
+ return new Future.error(e);
+ }
+ }
+
@override
Future toLatLng(Point screenLocation) async {
try {
diff --git a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
index 807134991..c3872cf50 100644
--- a/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
+++ b/mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
@@ -703,6 +703,15 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
_map.setFilter(layerId, filter);
}
+ @override
+ Future setVisibility(String layerId, bool isVisible) async {
+ final layer = _map.getLayer(layerId);
+ if (layer != null) {
+ _map.setLayoutProperty(
+ layerId, 'visibility', isVisible ? 'visible' : 'none');
+ }
+ }
+
@override
Future addGeoJsonSource(String sourceId, Map geojson,
{String? promoteId}) async {
From fa824ce167da3554e580ea92b92c5aa992b1fd07 Mon Sep 17 00:00:00 2001
From: Keith Gulbro
Date: Fri, 5 May 2023 14:53:55 -0600
Subject: [PATCH 18/22] Update pubspec.yaml (#1309)
---
mapbox_gl_web/pubspec.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mapbox_gl_web/pubspec.yaml b/mapbox_gl_web/pubspec.yaml
index 769890992..d14051e77 100644
--- a/mapbox_gl_web/pubspec.yaml
+++ b/mapbox_gl_web/pubspec.yaml
@@ -21,7 +21,7 @@ dependencies:
url: https://github.com/tobrun/flutter-mapbox-gl.git
path: mapbox_gl_platform_interface
mapbox_gl_dart: ^0.2.1
- image: ^3.0.0
+ image: '>=3.0.0 <5.0.0'
dependency_overrides:
mapbox_gl_platform_interface:
From ddad6562d0b3c21785b71e26dfde0d108690c1c6 Mon Sep 17 00:00:00 2001
From: Aaron Eri <36516690+arkeri@users.noreply.github.com>
Date: Thu, 1 Jun 2023 10:55:37 -0600
Subject: [PATCH 19/22] Swift CGRect constructor for
QueryRenderedFeaturesInRect (#1208)
* fix swift CGRect constructor for query in rect
* make formating changes
---------
Co-authored-by: Aaron S Kennedy <36516690+aaronsamkennedy@users.noreply.github.com>
---
ios/Classes/MapboxMapController.swift | 7 ++++---
.../lib/src/method_channel_mapbox_gl.dart | 5 +++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift
index e9b992653..4af7ff3b4 100644
--- a/ios/Classes/MapboxMapController.swift
+++ b/ios/Classes/MapboxMapController.swift
@@ -227,12 +227,13 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
)
}
if let top = arguments["top"] as? Double,
- let bottom = arguments["bottom"] as? Double,
let left = arguments["left"] as? Double,
- let right = arguments["right"] as? Double
+ let width = arguments["width"] as? Double,
+ let height = arguments["height"] as? Double
{
+ dump(arguments)
features = mapView.visibleFeatures(
- in: CGRect(x: left, y: top, width: right, height: bottom),
+ in: CGRect(x: left, y: top, width: width, height: height),
styleLayerIdentifiers: styleLayerIdentifiers,
predicate: filterExpression
)
diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
index 28a8e268c..2187f21c8 100644
--- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
+++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
@@ -302,8 +302,13 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
{
'left': rect.left,
'top': rect.top,
+ //specific arguments needed for android rect function
'right': rect.right,
'bottom': rect.bottom,
+ //specific arguments needed for iOS rect function
+ 'width': rect.width,
+ 'height': rect.height,
+ //arguments for mapbox
'layerIds': layerIds,
'filter': filter,
},
From b2bfef669e42397704b6f43d55b5df67b18c0fa8 Mon Sep 17 00:00:00 2001
From: Felix Horvat
Date: Tue, 12 Sep 2023 13:29:33 +0200
Subject: [PATCH 20/22] add dispose guard (#1323)
* add dispose guard
* fix ci
---
.github/workflows/flutter_ci.yml | 2 +-
lib/src/controller.dart | 54 ++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/flutter_ci.yml b/.github/workflows/flutter_ci.yml
index 4f16f71fd..cf1a18f55 100644
--- a/.github/workflows/flutter_ci.yml
+++ b/.github/workflows/flutter_ci.yml
@@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v1
- uses: subosito/flutter-action@v1
- name: Check Dart formatting
- run: flutter format --set-exit-if-changed .
+ run: dart format --set-exit-if-changed .
check-swift-formatting:
name: "Check Swift formatting"
diff --git a/lib/src/controller.dart b/lib/src/controller.dart
index 4c97274fa..e70e06a27 100644
--- a/lib/src/controller.dart
+++ b/lib/src/controller.dart
@@ -173,6 +173,7 @@ class MapboxMapController extends ChangeNotifier {
onUserLocationUpdated?.call(location);
});
}
+ bool _disposed = false;
FillManager? fillManager;
LineManager? lineManager;
@@ -253,6 +254,7 @@ class MapboxMapController extends ChangeNotifier {
///
/// The returned [Future] completes after listeners have been notified.
Future _updateMapOptions(Map optionsUpdate) async {
+ _disposeGuard();
_cameraPosition = await _mapboxGlPlatform.updateMapOptions(optionsUpdate);
notifyListeners();
}
@@ -264,14 +266,24 @@ class MapboxMapController extends ChangeNotifier {
///
/// To force resize map (without any checks) have a look at forceResizeWebMap()
void resizeWebMap() {
+ _disposeGuard();
_mapboxGlPlatform.resizeWebMap();
}
/// Triggers a hard map resize event on web and does not check if it is required or not.
void forceResizeWebMap() {
+ _disposeGuard();
_mapboxGlPlatform.forceResizeWebMap();
}
+ void _disposeGuard() {
+ if (_disposed) {
+ throw StateError(
+ 'This MapboxMapController has already been disposed. This happens if flutter disposes a MapboxMap and you try to use its Controller afterwards.',
+ );
+ }
+ }
+
/// Starts an animated change of the map camera position.
///
/// [duration] is the amount of time, that the transition animation should take.
@@ -282,6 +294,7 @@ class MapboxMapController extends ChangeNotifier {
/// Note: this currently always returns immediately with a value of null on iOS
Future animateCamera(CameraUpdate cameraUpdate,
{Duration? duration}) async {
+ _disposeGuard();
return _mapboxGlPlatform.animateCamera(cameraUpdate, duration: duration);
}
@@ -293,6 +306,7 @@ class MapboxMapController extends ChangeNotifier {
/// It returns true if the camera was successfully moved and false if the movement was canceled.
/// Note: this currently always returns immediately with a value of null on iOS
Future moveCamera(CameraUpdate cameraUpdate) async {
+ _disposeGuard();
return _mapboxGlPlatform.moveCamera(cameraUpdate);
}
@@ -310,6 +324,7 @@ class MapboxMapController extends ChangeNotifier {
///
Future addGeoJsonSource(String sourceId, Map geojson,
{String? promoteId}) async {
+ _disposeGuard();
await _mapboxGlPlatform.addGeoJsonSource(sourceId, geojson,
promoteId: promoteId);
}
@@ -327,6 +342,7 @@ class MapboxMapController extends ChangeNotifier {
/// platform side.
Future