From 6a60e32378dad5c2e790d66178271e6d69cdee2f Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 30 Aug 2023 14:40:07 +0100 Subject: [PATCH 01/71] add dark and light mode to map --- assets/json/mapstyle_dark.json | 191 +++++++++++++++++++++++++++++++ assets/json/mapstyle_light.json | 176 ++++++++++++++++++++++++++++ lib/interactive_maps_marker.dart | 45 +++++--- pubspec.lock | 34 +++--- 4 files changed, 416 insertions(+), 30 deletions(-) create mode 100644 assets/json/mapstyle_dark.json create mode 100644 assets/json/mapstyle_light.json diff --git a/assets/json/mapstyle_dark.json b/assets/json/mapstyle_dark.json new file mode 100644 index 0000000..31c1819 --- /dev/null +++ b/assets/json/mapstyle_dark.json @@ -0,0 +1,191 @@ +[ + { + "elementType": "geometry", + "stylers": [ + { + "color": "#212121" + } + ] + }, + { + "elementType": "labels.icon", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#212121" + } + ] + }, + { + "featureType": "administrative", + "elementType": "geometry", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "administrative.country", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9e9e9e" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "administrative.locality", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#bdbdbd" + } + ] + }, + { + "featureType": "landscape.man_made", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.stroke", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi.attraction", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi.business", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#2c2c2c" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#8a8a8a" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "geometry", + "stylers": [ + { + "color": "#373737" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#3c3c3c" + } + ] + }, + { + "featureType": "road.highway.controlled_access", + "elementType": "geometry", + "stylers": [ + { + "color": "#4e4e4e" + } + ] + }, + { + "featureType": "road.local", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#616161" + } + ] + }, + { + "featureType": "transit", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#000000" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#3d3d3d" + } + ] + } + ] \ No newline at end of file diff --git a/assets/json/mapstyle_light.json b/assets/json/mapstyle_light.json new file mode 100644 index 0000000..45a830e --- /dev/null +++ b/assets/json/mapstyle_light.json @@ -0,0 +1,176 @@ +[ + { + "featureType": "all", + "elementType": "all", + "stylers": [ + { + "visibility": "simplified" + }, + { + "saturation": "0" + } + ] + }, + { + "featureType": "administrative", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "featureType": "administrative.country", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "featureType": "administrative.province", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "featureType": "administrative.locality", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "featureType": "administrative.neighborhood", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "elementType": "geometry", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "featureType": "landscape", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + }, + { + "color": "#F6F6F4" + } + ] + }, + { + "featureType": "landscape.man_made", + "elementType": "all", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi", + "elementType": "all", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.stroke", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi.attraction", + "elementType": "all", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi.business", + "elementType": "all", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "road", + "elementType": "all", + "stylers": [ + { + "visibility": "on" + }, + { + "saturation": "-100" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#ffffff" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#dfdfdf" + } + ] + }, + { + "featureType": "water", + "elementType": "all", + "stylers": [ + { + "color": "#afdef9" + } + ] + }, + { + "featureType": "water", + "elementType": "labels", + "stylers": [ + { + "visibility": "simplified" + }, + { + "color": "#ffffff" + } + ] + } +] \ No newline at end of file diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 08ec1e2..a392094 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -16,7 +16,8 @@ class MarkerItem { double latitude; double longitude; - MarkerItem({required this.id, required this.latitude, required this.longitude}); + MarkerItem( + {required this.id, required this.latitude, required this.longitude}); } class InteractiveMapsMarker extends StatefulWidget { @@ -50,16 +51,20 @@ class InteractiveMapsMarker extends StatefulWidget { this.contentAlignment = Alignment.bottomCenter, this.controller, this.onLastItem, - }){ - if(itemBuilder == null && itemContent == null){ + }) { + if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } readIcons(); } void readIcons() async { - if (markerIcon == null) markerIcon = await getBytesFromAsset('packages/interactive_maps_marker/assets/marker.png', 100); - if (markerIconSelected == null) markerIconSelected = await getBytesFromAsset('packages/interactive_maps_marker/assets/marker_selected.png', 100); + if (markerIcon == null) + markerIcon = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker.png', 100); + if (markerIconSelected == null) + markerIconSelected = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker_selected.png', 100); } Uint8List? markerIcon; @@ -68,7 +73,7 @@ class InteractiveMapsMarker extends StatefulWidget { @override InteractiveMapsMarkerState createState() { var state = InteractiveMapsMarkerState(); - if(controller != null){ + if (controller != null) { controller!.currentState(state); } return state; @@ -119,7 +124,9 @@ class InteractiveMapsMarkerState extends State { itemCount: widget.items.length, controller: pageController, onPageChanged: _pageChanged, - itemBuilder: widget.itemBuilder != null ? widget.itemBuilder! : _buildItem, + itemBuilder: widget.itemBuilder != null + ? widget.itemBuilder! + : _buildItem, ), ), ), @@ -141,7 +148,16 @@ class InteractiveMapsMarkerState extends State { markers: markers, myLocationEnabled: true, myLocationButtonEnabled: false, - onMapCreated: _onMapCreated, + onMapCreated: (GoogleMapController controller) async { + mapController = controller; + await mapController?.setMapStyle( + await DefaultAssetBundle.of(context).loadString( + Theme.of(context).brightness != Brightness.dark + ? "assets/json/mapstyle_light.json" + : "assets/json/mapstyle_dark.json", + ), + ); + }, initialCameraPosition: CameraPosition( target: widget.center, zoom: widget.zoom, @@ -178,7 +194,7 @@ class InteractiveMapsMarkerState extends State { void _pageChanged(int index) { try { setState(() => currentIndex = index); - if(widget.onLastItem != null && index == widget.items.length - 1){ + if (widget.onLastItem != null && index == widget.items.length - 1) { widget.onLastItem!(); } rebuildMarkers(index); @@ -203,7 +219,7 @@ class InteractiveMapsMarkerState extends State { } Future rebuildMarkers(int index) async { - if(widget.items.length == 0) return; + if (widget.items.length == 0) return; int current = widget.items[index].id; Set _markers = Set(); @@ -214,7 +230,8 @@ class InteractiveMapsMarkerState extends State { markerId: MarkerId(item.id.toString()), position: LatLng(item.latitude, item.longitude), onTap: () { - int tappedIndex = widget.items.indexWhere((element) => element.id == item.id); + int tappedIndex = + widget.items.indexWhere((element) => element.id == item.id); pageController.animateToPage( tappedIndex, duration: Duration(milliseconds: 300), @@ -222,7 +239,9 @@ class InteractiveMapsMarkerState extends State { ); _pageChanged(tappedIndex); }, - icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current ? BitmapDescriptor.hueGreen : BitmapDescriptor.hueRed), + icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current + ? BitmapDescriptor.hueGreen + : BitmapDescriptor.hueRed), // icon: item.id == current ? BitmapDescriptor.fromBytes(widget.markerIconSelected!) : BitmapDescriptor.fromBytes(widget.markerIcon!), ), ); @@ -236,7 +255,7 @@ class InteractiveMapsMarkerState extends State { // selectedMarker.notifyListeners(); } - void setIndex(int index){ + void setIndex(int index) { pageController.animateToPage( index, duration: Duration(milliseconds: 300), diff --git a/pubspec.lock b/pubspec.lock index 911f33b..5e9a045 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" fake_async: dependency: transitive description: @@ -103,18 +103,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -127,18 +127,18 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" plugin_platform_interface: dependency: transitive description: @@ -204,10 +204,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" vector_math: dependency: transitive description: @@ -217,5 +217,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.0.0-0 <4.0.0" flutter: ">=3.3.0" From d4c77895a52b315678aa7d0723c37d2363250c03 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 30 Aug 2023 15:10:25 +0100 Subject: [PATCH 02/71] Feature Add dark mode compatibility --- lib/interactive_maps_marker.dart | 5 - pubspec.lock | 221 ------------------------------- 2 files changed, 226 deletions(-) delete mode 100644 pubspec.lock diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index a392094..f05f7bb 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -101,11 +101,6 @@ class InteractiveMapsMarkerState extends State { super.didChangeDependencies(); } - void _onMapCreated(GoogleMapController controller) { - mapController = controller; - _controller.complete(controller); - } - @override Widget build(BuildContext context) { return StreamBuilder( diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 5e9a045..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,221 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" - url: "https://pub.dev" - source: hosted - version: "1.17.1" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: c224ac897bed083dabf11f238dd11a239809b446740be0c2044608c50029ffdf - url: "https://pub.dev" - source: hosted - version: "2.0.9" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - google_maps_flutter: - dependency: "direct main" - description: - name: google_maps_flutter - sha256: "24392ef192f3b00bcd93151375676805a9933574423a5bd5509a0ead2e8a4215" - url: "https://pub.dev" - source: hosted - version: "2.2.5" - google_maps_flutter_android: - dependency: transitive - description: - name: google_maps_flutter_android - sha256: ee3c1a63983b8ba17a9a7c1233c3542fbfbc0ebf540d3aa9b8fd5162681e0219 - url: "https://pub.dev" - source: hosted - version: "2.4.10" - google_maps_flutter_ios: - dependency: transitive - description: - name: google_maps_flutter_ios - sha256: e9ad74415a222573625a2c1717adc1e375b18e8ce660fc12db734d1bda1132d4 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - google_maps_flutter_platform_interface: - dependency: transitive - description: - name: google_maps_flutter_platform_interface - sha256: a07811d2b82055815ede75e1fe4b7b76f71a0b4820b26f71bdaddd157d6a3e20 - url: "https://pub.dev" - source: hosted - version: "2.2.6" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - matcher: - dependency: transitive - description: - name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" - url: "https://pub.dev" - source: hosted - version: "0.12.15" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" - source: hosted - version: "1.9.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb - url: "https://pub.dev" - source: hosted - version: "0.5.1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" -sdks: - dart: ">=3.0.0-0 <4.0.0" - flutter: ">=3.3.0" From 516f0dc94365610b2e13daab23b77bfe79d71ad3 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 14 Sep 2023 21:38:42 +0100 Subject: [PATCH 03/71] add new marker icons --- lib/interactive_maps_marker.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index f05f7bb..845a77a 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -234,10 +234,13 @@ class InteractiveMapsMarkerState extends State { ); _pageChanged(tappedIndex); }, - icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current + /* icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current ? BitmapDescriptor.hueGreen : BitmapDescriptor.hueRed), - // icon: item.id == current ? BitmapDescriptor.fromBytes(widget.markerIconSelected!) : BitmapDescriptor.fromBytes(widget.markerIcon!), + // */ + icon: item.id == current + ? BitmapDescriptor.fromBytes(widget.markerIconSelected!) + : BitmapDescriptor.fromBytes(widget.markerIcon!), ), ); }); From 47a1f7e61308b2936c9a14a9aabe13f8e3070471 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 15:33:52 +0100 Subject: [PATCH 04/71] add custom icons --- lib/interactive_maps_marker.dart | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 845a77a..ce5f6a0 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -1,6 +1,7 @@ library interactive_maps_marker; // interactive_marker_list import 'dart:async'; +import 'dart:ui' as ui; import "package:flutter/material.dart"; import 'package:flutter/widgets.dart'; @@ -55,17 +56,18 @@ class InteractiveMapsMarker extends StatefulWidget { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } - readIcons(); +/* readIcons(); + */ } - void readIcons() async { + /* void readIcons() async { if (markerIcon == null) markerIcon = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/marker.png', 100); if (markerIconSelected == null) markerIconSelected = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/marker_selected.png', 100); - } + } */ Uint8List? markerIcon; Uint8List? markerIconSelected; @@ -213,13 +215,28 @@ class InteractiveMapsMarkerState extends State { } } + /* Future getBytesFromAsset(String path, int width) async { + ByteData data = await rootBundle.load(path); + ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), + targetWidth: width); + ui.FrameInfo fi = await codec.getNextFrame(); + return (await fi.image.toByteData(format: ui.ImageByteFormat.png))! + .buffer + .asUint8List(); + } */ + Future rebuildMarkers(int index) async { if (widget.items.length == 0) return; int current = widget.items[index].id; Set _markers = Set(); - - widget.items.forEach((item) { + if (widget.markerIcon == null) + widget.markerIcon = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker.png', 100); + if (widget.markerIconSelected == null) + widget.markerIconSelected = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker_selected.png', 100); + widget.items.forEach((item) async { _markers.add( Marker( markerId: MarkerId(item.id.toString()), @@ -239,8 +256,8 @@ class InteractiveMapsMarkerState extends State { : BitmapDescriptor.hueRed), // */ icon: item.id == current - ? BitmapDescriptor.fromBytes(widget.markerIconSelected!) - : BitmapDescriptor.fromBytes(widget.markerIcon!), + ? BitmapDescriptor.fromBytes(widget.markerIconSelected as Uint8List) + : BitmapDescriptor.fromBytes(widget.markerIcon as Uint8List), ), ); }); From ca1a1d168b3b175718ec8aaeb15ffeb4bd0bb52f Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 15:37:38 +0100 Subject: [PATCH 05/71] change assets files --- assets/marker.png | Bin 1672 -> 36815 bytes assets/marker_selected.png | Bin 1632 -> 45931 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/marker.png b/assets/marker.png index 47606c00e1578c6a85c60eaa548802792858a699..35a73dc87975ecedab6b75ca69b17dc8bbf061fd 100644 GIT binary patch literal 36815 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%0Al{OM$I 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 PY$jDjErl{U%P0Q_e7GXu literal 1672 zcmV;326y?1P)Z00004b3#c}2nYxW zdyA z=Vp$@UeKEl9CgCj5|9Ldm5`~py(88}w;;;j`m zxG#UOYe3GPyNKm=^*A2VQm_oH(L;fk&4NS(;#J-&rh_Qo40yY?29?u&u8ysM-FpI9 zUbi}xL^Sre9@)R!5F`l5if;=M%{6O$RKEfu?76OkjYsfWO~rKkDJY1df*Lz;o2 zaK}%4GI>iz`7u!gN&VT0fM~b*`9o}q>5ff7;N1C(m{c(i{rmSzA!;Sh9IGN(^#9(skglV^ljZoCjBQq?WkWs6J19fqyvg$iT zpbA6RZ5Cupz-|_BXS6`q4|LonpiEgnByBbu&h;E%C1=+GnQ>GP2jX)+PGIE7M|$&^gJMXzsS9HG4ldF9Q-0dK>YBk{?7m`wt{nw#CLmxc(c}KLNfoQg@gAP ze(oH}$|jHTNS;=^{EC_oMAD6DLr)Z(aBO!bjAIz}S~!px3Q4D8ui`rZqDf9gCPsC+ z>W!a@3kwV4hY``?+S*PhPQ~Vtn;Lq=sJnBxrZxVN5&`j`Sn*Gn2z#=v@=Wo}{EZ$C z&NAbz_X5!wHGqmUHJS~syPJwnwlpNA+2&&6n+=J!Wn(xaEMu=04$QD(?XG5$toYlA zFft><8G;xwuO8Zed#ah1?cK0g@z0V7J2JHG3S;0~JruA#=Q-YvW!0wzzEs{dC259R6{;4bQpj%aB)cs3^j za+z-eNz~W_0v~Ik;4A6$T|d!y72iG(IUj|ctTW$;-jeQbZEd*HBLYQbQ%gJ&N3xCr zqfQG4YTfi)BOnZT72kFcJ>_Q1kTduqby0z)1(I(sHU=^xFuH8g@I1-$oAt|yGa+82 zg@TE5W{*-}D9ftvW1OzZ&ozZ6y-#P=rz3GfcqddMf68!l; zClIfNG+)q!oPlk6C@?2e=UGJ{kqX6kjEKrpo$$@>n_Ul_JlS~ReexOmB7FIT8$s9AS+s0xq0JcSV7XABiLB~x2n6hr}NI5HrD)DXRHg>73ak- z$R~)LYGme@ueaH8lReukYgWpDbQDB_BBWZxtfK|KFtX()V}A^6O%~+`K)l zu}VImfzzxkHr8lcH%}K!nC#$UX@})^ceO)Q;*LJ=iM7PJdFs2lIm@t~w{-Qf{Ofb9 zq9qP1!wNM;2 zn2jaQ^8e@H|FnfZ@8<0030r_1!mKQRKF6FtukGn(>);H(@X}R2$AVEhFD57{CdMno zC%7Y+h6Wnr>V>m(wZ>u;Wmw^~d=3saXd7VxNvwzvmRCefM3C1?M8t;IQWPt|D=H!( zAu1szE+lS){dc~io3%FrB4qwAH?eWEhBf|(Atq!cAu1_k$tx@&YQ>8c5fSB;w6qil zb%~0@g5p?f@&BaJ@^k=CwRHY_RYWQqSW#G9&;5!5R1aCuau);%H|}S35W>uMO7L(%TuwDyynsh+$E1 zcW1ePY|WzXX6MGj!pkBkD9w-flCuNM-}&j+Qv82y06$v)g2(SY^spZP?Ix=M2(z?d zIj5w_qM)Ir`@b&tYb>(dpEZ7YA6mi2>wmK#%kSUrAQB64>_6Cn-GBd1$q$Et--y`= zi%E!B3GfPw+6eQChzbhwT3TC)@d{!EBm_mU)>tb6+yCZVutKJY3W*AdiAoBJ2#QOJ zN&s?yuc+_lZEcVE4D83s|F1573;#dl-Emd4ih~!pi~s*l{vGc`TUsNb1TbmogSBC0 z`Sqd0{|1NuM;8A*-`5@sOa7;j|GRVj+Q!Sx7Uyf}iB+%z%>Em{wf~JvEJENJES!c8 zt~PGIUR?jmW^E6ULG;bPbN*kT_`UD{@nQbYoa&d9|9?Ep4zPb4^MAG9&hP$8E&tjc z@)h#qe~A+Q^IxjPx`LiOA(4K%@+}U9Dh$RbD(L&AEsXg4nfeBQ`5E}7>z(~^mn2@J zr0Ee!p9!{@J{20vy(R;6Ds4&R4)zZclb%158+=8fbw$?M#YnGIL4|YI9_P-RkqZW!MtM=ET-+|9tG^%CV-W*0CbOW5UAAecs<&Z96LuJrVgD)bZ7e@t*pH z9U@TAlSkV50TmWbgZvQ2tNoq~`QZr}H5&OBB_^*xh5TE;n-h2k{OLzgGVT0uA}fmm z`N4vM9=G#>^S_H6_OZ_Po5yHhu=$_) zQ5pD(w(odohV=Sr@A=qUdqYEJ8pkv< zK_+o%i;RPDVyhfGf z4#@R9r$BY)P3hyTX0@*mg+t!;d>e41Z+?VdjL+!V7q!`;!WJ)Zd_SkbU&HyLu|`pB zMd)?w^5!;)x+@E(`5emd%9>k#(sZ{N1y1bcJn+}(%M0D5&+l%WKV{CIKWfjZ-b}Xj z;E5c!!#+Kq4^4Bl^5ftWQ`f?2RFbgX^VhLK`F=PDNrt;?*P7clPRBFfQe;(bzd-&j zTMl*oyaLsoL)2*QIIK~5{ADf@1EwUt+fnSF{Q<^=pBWpUwb|&73#UpWnB?1Kl|HryWMb_&5vt*Is+bDV(N+x+t&N)uQBy5jU5IQ40 zUQ0N254(Nxwt!iv0Nt(qCpswTcl|MmjwVmr{Zpj&_~N^`9JagxMb6WLn7a5i3XEKg zBCAfLe^||fKLV?cEc7ql9(}aaCi<7(epwmiaH{khJ}M3mq^`KIx|O`^TV^zBdiRg8lXLi-J(kLPNe>G z=*AA7wu1PzH}S6B-|&o_hcOtryKCn0At!Dr>fTsI%_{y`t{-%nY+sXDXQo>;AX1;m zbO%?6ce}tEcQv;WW%uyU85UlZKE8wZRSn3*s~vblMtzE%l+2J>@KcEH7TbYYGB%y} zSI#}mqIkQPk}20H$@_Y9-gKWD_3;=A`nG>CzA76R*yslAB=&JL{!yZ3IATZF_urlk z3#INB;LiTc`{^e;DKAdN=AApUKFgm2Wv76;*-4G@qo?zmof+zVXfPY4_|(&JDs?1* zM~r_dQSeeyxh_GOtripC=FV8ZbU`F!b2FJW?k!)Vkv7>MxW$}%%F~wp`P_Ej#6J%8 zHa2Wq>XS-a3NWbouR%d&JZE`GTk*1-4mZXnC}hinWpZvh);tOo1YZ$m+MRRz z5ByRd>oxtbjvqbM;vzw#ozFR+BJ^jC(}wh3^-1uV$e+(9&*{9o5`Twx{wF_R^jJqY zox&f}rqMPra$+bL#x}OGk?4+yKl?}=Z7UsQ-#Q7Cb;VfVcUS-VT>{pqCw?14FvqNb z^;qNrL_;{mrhZ7EM17fB|5y9Dn5N9K$kQ`;z1VJjWQ9qgz2W8_m9 z@N2aVTFlYZ4)QF{5Khwa>A1J9LfXUvul6A*;QAl5224NUak_;cVl#6^??ahv#^CF~ zE#Ih9DR}%bTI~-Y6bWk{n9#E=))F*f1b6jG_ARO0t687^K;ZHyPg|8Pp(uXl9*EDj zWlW#~@imDG+7f?I$FBup!!bx|bCd0}oKRT)O|=a?z7rz(HIl$vxj*JgKG{|}%1LsH z&pi=Co>e2wOOlCGnc7QKxMTj;VkeWZ8hw6(q1oe{s8&N`La6FS9)t^L^!}Bs373|*J3V7SW&eqCBjDyJ2Gt#q4;ex!cV}j)r~R> z>c37|#nTp0+uY_~M&dzn4_?H$LF6&fplX=T1k`BkODcv6Hr9t@|Ojz@cf5~u5 z(NEae>aT^2dD@K6kf`F(uwPi&3oSx5s7ryU;6DD>AO@Z`Plq7%ItIhs=z;o>vtK2^ zkd5y9Jrc_cfj_cW$hDQC*+;k>%0Z|*SAWK@0SM_tnRf9bE)zopZY95cDQTS5m(< zmjY^=J5wvbRZzQ!`AJLwk0&ISyU~c-*}W&rQoNMpeHDNEVH(9FQ~rcCJl5!}1{GP@ zISV8?sK1ZuEafS|r#7ALMzJ`l6R05W@#bFDXZ-`gqsb6+8R~L^i$JLs=YR5%+$57P zoBmM;KQp5BEp>tw(Fj%F9KuB!1n^m*RdTW*88jZpp`M&-E9E18^Y?0%MAbYqR5Nxn zx|m90K?xWD`FL_1AkLGoteXw%YqRntuRX#V9lb|Gj?z;AzI@^-&&R9rAuL=L;FpOh zVi;PJq;*HorA4X4RX9SL0{{NDQd0(VpEZR>9qpMQ%{*JbTF3Q(p1IGQA3XD0Nzz-V z@3n9yX*#WS$0p<_(!|~-Sx6UW-?~QnR)1Y(#|b<^n(4?tk?Ol0!oOnKYFlwf5;Joe zvXZW!t74-kOvuX%*NkH7E=s5O7GF~D+)wMICQ>kVfcB%jbjF)!@|S~k2^_3(d)$0h zcMR~0BLKC!ScuP5sd@*iy?inhToui~Z+E%GvBZk{I=k32bdZnrEyvJ8580M^Elz$_ z39tjkm4KK2D?9;gd-zBdNKgqUT%7bI9Ugyj+049Tx~Hf4$h$R;gbjnxx)6_vYtI$4 zZ|I`oL zX;1drMxqzS|G|_z#LxZ}mh+bMB|}wo!QVMGP9>|kxw-aA-A^@`^+}Ccjn9a_uztHS zV!gj5AvYTps}f+wMn@87!gv3{bFd>gdco%*Uar}<_s$bi8BVM}I4<06QFiP5cWK-T zetZ4$gyr^yN+&x#d};L=0uy# zTG8m`k$|b2H}trO9@>@9)#h{YT7P8=j#+)lpGA0ri=n_PlKWhzvGLk0F|W<&Ox@3z z=-Z@{j6luKnCO(4@YZs(Njt*Vdo;R{f2mEXt+crJTXr|+>|0D!Utwsfj7jp1bGg}5 zW9w^J>3jAMf0ecf|j>FEU?mFmh| zkCZhty2lnrixP%cXoSBw1jWA`!QIh4_g^|*+vi)5_ESE-w$HiIr`W~0yYAin9M$h5 z&-t1AZcF*hedl4b^p2ap`z5; zcD=GU)*z%_9^Tv}smB7nC#+RbfhtTI@^l>Jom#*lZ%KYh-&jYQ1NughNT76;|EP3N zrO`O*G++FhT3cxW1UhYhs8|G)0QX6_Ot4?v;r;XGQs{5CK}Wm_3)a4OXl@VHCr-zQ zsA8zJorA_G!nk0As^+%uIK*NFW_-a9%Jf{1@MjPs=N)V7$i)X6#d;lT@MU_tnvBv2?()`}`!$Z9ui1 zQmLul24?A`W9$uFq_Q%*!og`FCd#`Xz2jfH7#&$oC8K9Fw@0xkLq>A=q2mZ@ecbf1 zCt_$2EXj47Id&V{%R>~@9@xG_II0nW(%y!X-l?u;D-vNM&mo<5aar+PlD6x+)@aPI z|9;Gs)-O=11oz|ipr&s_heGlR&&NaYqc&(NRpex5hiFn`bV_B>Ub$&OFUZPXS{U`3 zsKcr+VO94lknAh)v8f$b2vazTwWnWvh=Qtv1PkpxF)?K6nM7P3)<_M}cLp+^48~XD z-R{Hq^#b=xWn0ok4h63FWzA$c+{`WX@8UB}lA46}qWs_ZN}wcY+e(vEHXg9WBB6x_ zvz#H;j*BjRzwmkPz<~p~)+iSSev)YJRTL*bqzk{jz)w^+a@a<%SrU4?yXPLt*Td+~ z9W%BbbWCDT^a->)Z4-?qD1WH^!n`EbCCW1~j-n9o7MavLR|^`K`|dNy3SZ@|IY&rF z)Fz7vd?X1g*)-XGhpI?6^P=9F)ukd04XDVC#6N!%MSX{YH>_+k@Uw*IRKp1dh8IlL zsN%;(23I4bqAX9CgCfgw&!aelK{|=Xd~u_nj%cdZ#zOxke}xxJG-hUocIe%ArW0|H zNTt>OsZT_Z5U%G|yhLYyGLC8=1)29J`||MdM%sS$B77iko7HKyFwEouPE?xbyhcwg zLudk}`aoOCrz71tJv{|p)f#V5XR7oGeTaFgK>ANq=EQHQXIc1@BF^QWP87|Kjlb7zwQk@oDHdZQlSuvfj3bxKX0BIOjVU8_%SVgQzy~_-E%lghXDYjiG3}Cffa2wmecq__Na1I3J^4|d1G_bp zAMIKF5khIkM-&Vzb4bR;VvTCXcDpxx3n$C6N)I67Vw=Yl$^1oA7Z;VraS|epA4Qoc zB$D|snPMOt3A!DRY%+VlrE;(=bIvshx6KMOk@<0*EdQ<51EAHzEv1hQRW}}y7&*<; z>DL}Q%+`r6ENixv=BCn+)g!p)j=>sPVti9NuB=RZp$|`(;<^MBl=tpYt4d#;Lv=*( z)1kP#`q%|qWr=&oo{f}n)X0_VnEb2g2G2r(;l|l&?3LAM_H#N^OlUYEM=?_Zp>DN)TeodKHaD z+D8e3W|LZn$-;r8@7UBKD$N6;tt@h*5ap_nxcC@k#e+$bN+Q_2aHZ9@NS! z&x9dwG?K7I!c630l8C5{rH?OSGKJD9?%*}fpU*w5e=RQ}qDfUY@t)hIq-kW>aTqo| zbs}D(j><$rM6CJY3)X)6+xKHoQ$ysKpZ4foC}Bir&|DB#2}ma~)+=gxqy{#4xD{KNhk<%y%YsT+z9RybU zt`S-nf7}(w>&YN4}M9Hw#RNp@LyoiM*LH? zJyd1lh+HC`pHU)fT)!0;9lxG1`DT5;U8l}~!~|ucBqCOWb&vKuLg`Y|GBXm#^;OL+ z0%%kwW6e^rptM&Aa`#GKJTN`cF?xCNRug1WNpl&T3h0+~ER5;X);6 zyIJUXu=Da|cE5!|%D^Cd72oysdog7FEhfBe#O%c1Qz zuyEH&K_#kIT1uv{B0(8t$;}9vKwR{SzvdX}BXKh5CRtk4d4e6d=pAzq(&x<@BlhDZ z)(5mQUm0UBOGT|;y(+!RC{bq!RJj8X`yCRXg^7Bn&fW}W=3aA|uW1twnnrdm3=9Ni zpXG~@N&)rPzg>e%ixp@T)_0_2WV1RmG7!d|?$L;U=7iG^ZJ|0M&sTNtA;t7n7?1=5 z2+{%qS+t9TIZvrG803V79)4ZuZ{YnYza(pkMuq(V^*%Y9JNroDBNf&9tdy_gBl3Oo z=dWI^vfT7p=RhihoSR%|?K7YW=LnJ{_rT=&^8sJKETVN3l-5VB->hAk&CrV;8oZLf zBUf$=Y#SrX11nlcDJy$#^%ey@e0NeX>7C75B?Z;m=itr6;twX9SEMZ?X%dlJ7L%tu zg{lN9jaihsl(bmx*QMcnlUPO_$%}obt*rw#pSx`MxVaf^Z<5{kaTN}pdjmFb9p%mM z!R@K1A>51J-uj@h)l_VTP6B%*h<~NsKgZiCTWF_zL0y@wwf!vE-3IL5g4q2)TPY($ zT`g1nF4_Y$^S5DGU<@-aN%`q|%xzM3;Fme1s^B>5{tvVEE}Ex}l$;JR`|Vz$rs~Jt z=@-g*+o~=Q`1kpcW$ovAR6vk>mzLjFSg4^~Y4H?iT)`~)P%0qMXcrazWnIF83Y%w^ z_R*bUdKXhzdQd+ORloE+HfCPScj0~J@?cJT*CUbyzk^76FTg5bM7VSALEidnUL6Ioa^s_5Ax??f{`hahTC*jOT zsrujU#Rx;MRN;sW($4Wn1$v(sq?qW%Vwak&<1$*V20W>FZ^ZiGMnHRE0e3)*AG#=` zL1C-3&Rpi|)ktApD)oZoH9pLk0I&vEr0IbqUeH> z=$QGl5j6P?lU-GV#zn>XI(cIyM@kG&B|GVs3?-|~X-gO9-H0e3pTfxatk`5EP`u4n zg5LYu7!b)9?&@%Zz&3UW*H-!}J|ls?Bzk1gyF@Fpo?YE~h%`Y`E|Cc=p3&*J@=(=A2py-c%HvMa zIs!fSi%?rZ1Ch+#((fGu${JXyjsOZp-z%3wgZl7F=RH}<8X8js;TEOceA(Kl*BPRw z=`lcYf~x>r3x)~J#;v*j``Ky8Jc(-zL`hU)Y)6M?kx4}dZhw8uD6|%~GZUy;mM_s! z)hB*+_@Nh!N};b9Dw=LfF08IL85tRIbaK)YVd%Lp>XfLNr}2hj!R9&M6+_F=LH0o! zJTY>qJMY`FcshkVX>UZMafNGoRdsc_V-Gha`IVzTR<$|t*tR563rD6%;=w0HC!hv% zjs>^5=87pp*g_~x#QCVB{1*)klaYCTpRFVr`GfOZ4GoV{GWk7q;%(cIhf5ZSg!Prk zncWe`TPQ3jP%Shp*_l;U)!pBpLM&NcTIx-Hxt2qoL!o+6n;^v=+=x4fn!NydS3e0$ z$!_L^ra}eY6STfjTs6<_?d$t-rHw!NjqvxwhrMRr>3DgSZES3;q#Wejw+vf95ruqU zRjoOhIYIHrLNiY<`Eme>MCn#$H~Uqv-OxO^{N?7z^31yE6(RShaHFBf8aD<_rw@jD(bmwcsYM*$+gI+>_SgZ4`nq73~NhNpI{5_ci)Yg{eaxu38Oeq&6;$q z=|baIk&sk#oy;O|b+%Kk_Ja$FkmHF?SE_Vnje9@eQ+PP4DbiCW>l)+TZh$sjLNFOp zcLNydi3cFpbMln*72Z8-n87#A%gcMg(@&bY$hcgUR{B#|+(!zrUN%yBe2B9I6eCC? zi`46stsBMBpuYDF4?p()*qdKisCL${II$BbG=}iaWr-g5;qfRxHQGXLcmhH|LT%GX z>EpZc%+cy&{lEkeXQf_ThUu=R_HF_j3)SBZ?xn!ex_nl;VQ~R`Q$_izE5St4O(X&$xP3c2JR|p?`s>_4E zuMl&BX5QV)J*_dIrzMk9QMa`SrJ#=pX=s=PO;!Ow#eVfpvQ+Ab#=N67h?*Lp8-%Yh zF15M!YW&H@=B9=+*}{#XVX_lY{%~@-x=}6IeV%B7!DhHFrG(4dm#E?17Q|Rf$o%md zM-R@#39_7p)O!8-If4k#7S)Re1~~?W7xTf93@`iV+`oTcU74Gk8ttxG@|>s8wiH&M zItgcdP}_w=Wzn^#2%i7gTciid;j8=M>0f)97#B^NpN}V!^wH^Jpqo?%k5s9hJt)2A zzkI1k=^)c5&m-om@z<`cg;#)Q6XTp4m%loFUzr|!;gL<^3}dR*p~uyW0W7Hjape?Io4qm@nu*) zivQ&$%{(FwAOC)2q^L7^$qUh`{%I06e!CBx4LLhhGP!R*t~)kG9?nzyzD6QyD05@V zY}<8BOj2nWft68PDEHkAr|Bn$gDYH0GB``325PC>-QA4~Xc&q-e0J(9=$?C@p_%vH zrJ7Yuq996Yg2KBVN&D2(J@Kty$6Mu@CDS#Hl5)Vez>%FA!YOl4-_0p5<^$dO?Bi*h zzp2O?2<-%JCPcfR9Hc+doqKTSV#(#;je)6g_kJ9{)VJxd;oIe}XOBePiy`mt5{-Xp ziQc76f=yPIx+i;zfnYyLjfsgN+O>dkbt^}LPVk)pRhK*U%>$Z>5Y<9JI`JVLy5TP_ z>86qnhB>%am?WErd?)FSj*hybb(=meE#+oW*!8&>C7q_bWdqTi`#4B+>aj2rN`Re_ zCR0}JB@!yHDiGrY6u|DJJz-`&7~@kU!A8ljNF;XcNt07nGJ0m;MbZuFQaai)ZXx&3 z3_J!-ufk^auQrc?+V4Pv=KJeiOxO+pKu>`|o|~DQqoWYhUdg%nc>uAU?`^ymut$9% z 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�`&VXqr3n?y#77o|(vO2m_pK zb61v$Ibk5X1ajPQaI|?Wj0E+k7}^VURths-$ zBpeZ@86MH$Rz5G{`O7jvaL`+x161q%ZeDcnL3cAUgz7Vwwd5wnh^faMnehyw9Sda< zAfA_9@;!Ix+$pj4?lq(K)i#9aI`GhZb5R)>6BNen%o^W*8MELT2Ib#dQg7E93paU% z*z#G;eLKZCXiZjOjK2lR-~gh|t#hYE%63E|i$YYWQ+wb%Eo)og?Xi+lAO@4mMB9?R zJc>34PwA8S(tgA4yjFmvgJe8T_f=JW zTP)|-0su5aQbO%FE?aF)h?1v5;V4w;))8_`;t0wWbD`Dnl|p5vc*u*vb(y)aK`n`f zk@)U?B+AzVo4VXXQ_^MSqbH}1c(}z{)y4DoE}qn^wNIdFobNMh+hyN*PHEOgKT8=u zs7F~o*V)%hk#vOi>Lnh9)9`D(FR4`9^)c>KUFP++zRYWFEk)L^QAm>>+mC%AX%=S2 zpDw4&t$mKors6v`#&mSvk=NwA0Im6$3UPVFfW?+H}L9V5FX_YGV z`*{G4cYQG^jnyRP?1{JlFrW~)YT{%@;Djkrt^x6$i$FZV!9tl4_hyC@+R^CJMctIY zuEZz8NhAW356G1)fWJCQ^~N;vhM%|TGl!doPr<(Vp||?rB;pWzp6S^s9?2HEpvqXTHZ!*(o3J$Yx439{f36@T3*>1X<#VASXTo zz?ff^UE=8(LYWpTsr6{!iD^8Z^7Eupy9L@El?+NP?YUMdVDV1C8%BosqCKHWyWN{B zpuboo01kbGverf{i+R8UhfR5A&4}8LE4!D*hM8)>TaDBE4LtzB2P=z z!Mdr2NJsBxpzB-F^&20${wXR~me1bHwB^o+CS}?=coCtyzxJUo*RkS-bwbOuNFB8c z-TPG~)0##+j68fU@`;WQ3%iu+(X{8fTeyyFYuNukNXq>H>u)%y+#h9HZDjG5AU&;t z!34C$=Jb2d@1f_W?>(3OpXZnvDF9bgp4*%TI58G$O~hf%r&y{^o~}zPPwd?3T@DOg zwM%twV)E=EV8U}?b`ucsL4U~qo{M5=8|6=RGDAax9%Xe9MmBgD0GU&S<&rJtb8oT~)X;N*Sb+Ix?dEBH)+V88W#jFjfC|7wsux1x^775;zp3>Mjg2 zitn^QyB^z)WvE=bo!uV#tkX`SQD_W#{7MTg+zYz(X}&m)etcwtsF=|Ke%{qx-JQqr%!DSD<`+p~zQ{h04LN-ko__uzW*1Kdw>y4(S;2%} zFcWSFl*?#c$$hLiLz`HB>)?=mS0-SdH|4mfn7p9Btm)0w_X@55=V(9xWWJZ4FK8$9 z=TizT9h;3M04v!@a%09Berw@}5kR%#@pJ^zH~=%Zq!B;i5a|u%n&5Q`R?>(y+;{V1RNYp1a1J=oLaT&WTre+Th znqR4(s24saMUK~12vqfvh%H-Z5LY zh)B{w%d5e1#W2Y;@|`5IRaiqa7!G8NRE-I4AMqU|HP(-nHcM~RDq0<6eiEPY(Io>qjpG{7XUmp^}<$miZ4}PT-Rc2m08p|B@ z5Cr**-=789fvNCHE~N0@mD#@I3*LkaM$%;^<2o|tyT?uqw0`fb<1K-8sgc#W@`=gv9ESSbnTdIJH-c#n%`SvqV=ZbXoacCt^jXL?N%V;a{$0hMuco_At2g{*f+z z8iRSYS$3Y8Eshf_Orx uz8$&_LD+o+N&nBHe*ytXuy`^fCMY(0a!hIJW^>>_35PA@Z*n(LPyP?bWb(!U literal 1632 zcmV-m2A}zfP)Z00004b3#c}2nYxW zd?;wAQ}%n!D{R zv$Hchvy0weo4w~d-)!F9PjGHnVxPmcU{5J&P(=!8T(f8OA9j!sQy{?3xjkBgb`*SclvN~8H(}? zTvy{_UoUpFoj`L}XBw6}YGT zv13aWQQB)vBn8O2x{E+7%oa~inGU|}xsHa;4!n7)*=l|n0;VbD!OMIiA)s16FjqkQ za5i7wJvfjcUpaYdA}5@N?Mg8Aj^dY|ND7d3O(P&+knosqDxK{}%AIdL*_cSYQG$R? zrR5n#ymYcLEhpm0n^Gs5x)-ib+Z8Ta&Krl zg7*GYft=6$F?Kl+_mM=QsJ0V?HBMFFMSy`Z3$zNbse*X^xPj3WibmFe*iNTDz)OyY zfsa*_r{dG$<>tSQ7R>+Sp)k*Lytz49`v5i4@{K5|t+@SnTLCpO?S97a zb+%dV70-ar!eX%6j8DB6Fh+7La*TjkUP-gTF``-F%0Nn((=H}*RFjFIWJBx``;8(7 z7hADu0yZ<|JE3d8fvD6Yu_qV_LV0a@RqBbv=mS=U<*`V$kFh#W%pO1t_$n+0ZEx3( zx8qP$Jq9A@v$+Sy2818I_EgOO#m>Z0!18EhiH}(F3YCSw6et7R9TugPPWBleC_=jW z6F~EFbq zj{**q0Pz;<%ZU>?16vywrQ|vDnsUG}mZRD$M0nQiivYwFF(`V#dCLKVXmLZss6wIV zGyz4Kzla5DMd`qQE?_7ZoL|s}9V&_#tnupHQx#arLb`ewpt;un8AH!NLBZ7ene)#7 zuL9&owHyN!Gv6b)L&oq-*dKg78|OK#05_6#?E?bVnw+)vR|d-JiBca8JgD(hhE^WA zVgE!3!YKk3CcYkYPgo3m=fPhZBqsrhgp5TV2V>j8Se@dR Date: Fri, 15 Sep 2023 16:14:13 +0100 Subject: [PATCH 06/71] Add dark mode icons --- assets/marker_darkmode.png | Bin 0 -> 48439 bytes assets/selectedMarker_darkmode.png | Bin 0 -> 55996 bytes lib/interactive_maps_marker.dart | 21 +++++++++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 assets/marker_darkmode.png create mode 100644 assets/selectedMarker_darkmode.png diff --git a/assets/marker_darkmode.png b/assets/marker_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..ff97d3eea6d5d56f4ab0ed81f6918442fe4e0e5e GIT binary patch literal 48439 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 SqStU*f?{s-W6pNBU;hvFgE(3M literal 0 HcmV?d00001 diff --git a/assets/selectedMarker_darkmode.png b/assets/selectedMarker_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..3192438e3a9f2fc0bded7938e4f5b478365926d6 GIT binary patch literal 55996 zcmeFZXH?YL_BL3EiV4i11i=J~xY!vOS(y-63{A`|Y{kw@7nYu3u`m`pqb4ZFBWEXRVs3HM&E7=G zO6=&%VifF3}9nIFko@9v9@)cIK}HyzvqH~iJdVg5bSW=+|JIW%aWJyCup?ODZCTKW28MV?g4h{Y>Gl?kjs6+c&e7g_drV^^ZWC)08xvcC z12-=hFWS|w?b_J=^I=A@g&h6BbbB6~^)L;{>iR!EDyYnh#qkK?_yqpr zqo85N1_Xou+YkREUz{Y~8gCC%z&iNPu>9+N8A(Y6d%UTIHT=RsQTiH-%neB_uLu^) z$;ZXJy)ZdBoQ$ml!NAtYMCQ8K8Q3kCg@rNBz!Yo5Bfx9QX~HMS$B7jb5abl*=jY|* z#~PXN8Jh|U^B4>N{rq*jkt0$f^!zU~F~%Fg82?%ZuYs|M5RZWYr-_k(A*Zo`fC#4{ zzp)S}4-b~tSOgYiEF}2%Xm{)_z)}sY|9w=nDq|RtUx?Sx#KcgT6AaaqQ;<)9pHqaF z--MHgkC$J7$J9{BkVgdB1Dqtmf?#d(+qkY;6Mnz4wm>S5vo^3bgS~PZo0u9nS`*HQ z-@2+M!*bQmjztPh&2k%WhG${nWZ~t#%#Ccx+5(>6e)Vf8?thyAUK;&UkKc1BncVwl zHgP%7Ndse+Yd7RsuFBm}{GY@9`YammUt?_B2kxeY18AJSPh&^ zjL-aj)8fAz<^Pt@en0PQZURI8qspM4f;fIGj{hJ1{57`&-jv{MU~h8O437P82vqnR z3|RO;xmYf!S=bumogFUzy*49rkh%##FZUVle+2aZbTj{ZdiiUW|HsYz4`cg(Z!_Dv z@%u3U=lT7DAly}BhyQk%zkj=pFi1H6$R6zY_wR1F5MCC%>REKc|2o zFE6Koks+3o*MvuySHQ%`#E{4IpJ${D(IbLjyjVdIUIAVq5ir?*eyfakG%`o13G;0q z#=o!sKi0ebi+^)1u!Zfnz+a)4|2Rtc$3L#t#1^Ku2S+-5bFBn}X|9*KepT5eYJ%+Q z(c|nmy|pw^C{=Sao;=HLcK&p|{579*hv=`#UMyb9D|XlDs-Zo4;o>3f>??{pC^Ul= zFOY5B-SPOfOU0bBEEGFla2Vg=zz3aw+TLU?l`~x`F>&}wmXTHXir4w#n5`cMl`+%z z&d9notVGRaDW|GXY_k9(ji+4OFEEE1x8LE}wF9^P2J4k8ROm(7&ON|2;Ll481=IG6 zqb#o|(F^?@I|)vxMKNrfz&L&FO?+R~Tl=X3Mavu!krn zC?+eXw!vQ)Ra4vSb2T~PFGchhd&ul#@1dGO`$T2-y3kt*;fl!uA$6ve-)5FOq$O6H zG#+f_fDW;TTIO03sNvPFsiL`h+6E?zn&D zLvF5MM|=Aa&CVUP@#EGnR#sN3dwR67rve+;f~%4VII$ys6}tXX@qtM#qe4z~sZD+tX^k1z_6*d^L%c6|+O@%*U5d01Q`U*htr6qgb5GhzNvTCJIyyRI zd}O4frK_tTH$FZ-Ej6{qgqU+AQFnHwh?SK!o1dS*Wo~XxI;iT0QkMF}z{7_Rees2p zdGDGQC@`7Fz>0lv%RR&5OOLLw*vSermLw)6nW+ggF6L`&=cN@VCtI}C z`r?n}la;6b-!+_Soo2z`H;dN9U)DxZypS$sY0@%3*RQy;zu_%{i{{4{8O zw$4)T(l*1ab+-%0R_kJ7Vrq!^zFyh`yGNf%HaCb)R2gUU@$nsbeEm!x>~CX?N@Dq= z^wV@|OlHYp0L*9eRd>9yg^ukjwpyAZ<4b=8$vWk}f3N;DeeZb9=g-$?8sj9nnM9o? zroU8HmU^sB5*tqH9L4B+o`!{E(;B=MbB3LroV?6yLOJoLPMk1zE+kuPRHh8hxq^+2 z$#NG9&_BMuX<%wP6jSWJRDVK;4pX)cfb1h@(pTbHSXfXXK0BsXx|$ZIZ7<_j@hRJ} zRZTpWw4b|($FaZceklVp^VYnt#QK0tywwiO%cF2^nFD0+t&NPSv9SUhef|C~o12@e zQ)hh-gZ6xzaal+R(~bD+`k5^OQBn74D)FE9566$glT_%*)1G5Wu?6qov^QYBs=5rX zh15v@bShzueC>8W?vfMk0}tsoXF9Ts*2HI@2l~^)UdzQlIx$-7uVD38`pE zkZx_m>rr)9IunlYcKHGGdVlQM=U*$uXCB`vGAD>(e7+#bwkN=uCwao|dMCSyTh)0s z-G30Ov+lM|X*8JT;5?RpcKrPLGb|}7Nn))j&`SVMumLzk%G4fGI9;-)wz{?^v0gwb zp307iIW@&znRv*rf*n9B&}+F_mYcjfaL{|%Oo~K_d5g5TY+igRzroSBe8Am#x_6_! zzFuA;XRyN`BuYz5OMr8zikA-D?EL!ms}9=T7qmMoU%()qGAwg((|DN|!hnlHoBZ0k$@Iluq03`Tsh`Rjjr?M?xXlFhN8lyjmT)5J5N2U1C)WpR4*W+s1Gxx>Z z7X=t}4rcb&*4K{>_%ffu+5h-TX5JW)-ug7+2BwL`e`2g;ZEC!`z;QgRUUa%hD3?eS z4dDo*eQX~+Gqaw{XhK*Cj-P*SjYxLVYDe&3w!O8_(aycDH@v;Kib;Cj8&(B{ zh2h*(WBRtX>FXQQ-dlQ)9zF6Jj)(a^G%a8-x+g)w8WqrCx1mju>P3d5bZm){HWOE( z!5nVHH&q)2iY;z{Nf*yiGB1}IAdQZqM~@wXnTC&R5p6d-M&xLsW3n7K_jo|AS+Od1x3*l2az>66q0FiLq1s}Zz+9Tk(eE? zRxi)w+G*uJvhx(x;|^Asj0~UQmbQ& zZMYPEo1OkCt@mP1tmkAETkN{dPz!~=4Vow^*LhmE+aye9agYIDG*g^a|rP$ORp zdFQ|r07dVd(aLIi97#HxHr;s}v1pE`Ha0d=VU%S*jy(R8hEZ6-VRjWN-T58s|NEG;g&73pR$()c!=r{Kv?=^oW(&dwoW)6r}BKj-+-9 zZKeSHQXyBt$ZLGBT{$(KCI@YN4Mk>voSdA>)=KQw%CwY})aD#mNjN{)w)!0~(#)!j zs@V0aO5LqhlK1P3jE0ZzM10y(>JkARx94y!8P}O!)dJP`S@;9AC$g37+*iB?`n#1x zDp|}Geem&B(;%k9hdty;$x0o5bAB`_b~B+HU+P&8MqM_sJ;?;Qkd$ZE5F?R4mXf;Z z;NXxG0=dJ57Y)OLe0+*7%LCvsrkqRHDB{N{F#KmnG6^znlpr^;+iZ3t1T86mY-Q0T6=Y!$%u^SU+{1R~D_>My7=^S)~Nb`ND&B0#= z?C1K>KaI~H3?+6Bl9v_!mo`PSn*L*Md-bMd%NEVh6a!-Fsd`;zLEOMi80M#*X{ z_lN#JR88wBUh3FjH$X9Zk2Z*}O~@fAZykxilwAO|>bO#}+Lj@1H&-WAD#S>m<)6L_ z6MFXf&5``!7}>4v(v5Ds_lP9(Dg%0h}|Sn6^j7aW#+WwhS*veyuJF>thEc)_*>)Jmo$y7U=Qg zY>O0W2j=ND*skM*mEKyHG}5EKw&rHTcn2E&vsJ@$;QQw|D&B%f8+p2CFY+tk1-V zxfgUc)9A;i)+Krmi}tO21o~mO!Q|X?SD=6u^Og=haQ5KkpJkEwl9eIvo)S+{vrYM$v4zELZ-U2b`0bpL6nVK%VY+ueQXlPnb!#b2^M-u=`;7 zFI#n-$&RB){TnOj^m?ujGh*Z?(F#77AC&Oetc2CO##>Qr^M9YcWQPVZeEw)+9uCS3P%CgQ&w0=*d;zRJD$;__k z2z-&^UqXK3D5zQN_v{+3wHY}f^83mW&CE^kE!#2E!>|qE*oGR*tgjYXUrlnW4YIzP zW_@L^a{CsmA`@b%5^{8lLxx?My;_Z(J!U!PM>{cQejt(EtLUr-5Y&u-qddV=K>JRD&AD3k;UDay?^BxI_I&?t*5sT*^)|n^*$bb`SX!0{TNfsGr8L>8JLqN%J-LA!=TS}z-fW58B&@SZtW;I} zjV1lJkuLi*cuqNxdhKgC58CvVL>mX>IN4Wv*c)DZNT=7jtMlDDw!6!@I?PJOY{QN# zX;|#kg0@SXo15FYrn|np;K#f`f#tV;ut|u-KBL*j{6>9be^z~qc|=cQPEL!|Fk{W# z@t16dyfzMng3oSOd;WZNOzgQxdx27kHk)(1fzzV;$wLj+4w1zjqUsD_P{(`Nfi>K4cxh95>gyG8=IJR=`?MB(FLc>-TI zH`cu8((s<+u|y(KYwtN>2EYx7dqLstkNb7S+`QoN<1bg-keR)rba88YqS@N&F&nGk z@=4VT#}vK2OaJ)z>f=L0{>6HDfzr;8#$EdC(rMvUHSbJ%7%AZ2D7pysMn&wlO0>Nv{a@hT8+eGhqScXNF-@CCTb;j%8CAu4?6K)bCGICn<-vC zo(zs%f72ug5!C`X*xt?5($&-@VC7oY*e~@n)xhNxlV8B{L+F`F3CS-a;_q6xX-0%U z50Xh%JTM+!j zs|Pd~$Df1T-mcT#eVdDShnl2 zW2HuKbCOuPF}bvB2SvQ>N8Ix&V*xCVed+U`P6>*Y4_wdLubaDz;|Z<{!41q?{8DH3 z)z*#9TB|k<7(5LKU=OZ3p~HmTn?Xu-s28d9+*lw<*pqaebu1zN#0mv`pe&1 zoI34NDZ1$_%xJ15T5mp@=cIM+_2k@KW%){N?M#1!JcEv*(Xxl4Foz@I0^PpRtSeWd zwTRhdCThgsAtX*H1@5o6x>mZi#tjt#9O-k3)p&{3Tx4(Fq^_=8!j&3T#(eZl zOez-Ex4VO@>RLo3I)mg3MCsekhx>mCV2le6cr@!)T+si=eh*kCxfDX;j6T3LYk4qV zb;{VS_2G=IK{)Hu$EiDDY;jOL9ACSu3Jg2Okp;lu+wi9ODxBCGO5HvEqWOa-Ud3gv< zj{4N0$S`#~=d*IDboSC#S5wo+Vdbb0&0!BG7j?SCmTc?x`jx6sLbH|;Z*=6X(Vqk5 zgytp%-3+xbT{nvn@Y(K)kvcJ0l-&^>I~UJc?P|L%lFaX4lZrNK|Zfko;;wxIcSK z3#`!&)~KfIYO29;ySv+V63ZzBwTc!^goD9`Una zIn`IhKWvt5%u43tk0%Y;M?Ag`UNU6(=>dV8X~ABDA!;;HplWxffVP_g?qbCQjCYjr zks)_bhj%Gwi0RCCNCHNlS@NRBW{#b3HZAsoY}N zLH%RFZ2@IAiWHOQ|9WEugu3jD6crODbQCfho|X+UVs*LU>ixGZqvvL3?k!Y&&u$Wt z5S7J^4zmVV%iT8j@Q9Mf(WkV_lny*%(4AWVp4I?SH4FlHXR;hDu@b!H0O2_G2_35T zFk`;txVY1D;%o1m{QYmn#~;aQHRBF`E+y^QwEQ8)AGJb}q>r}xS<3br=arVD1PFL{U5i-e{sTrjt_l~<$PrzpUF{>YIfx7cFCySL80 z=L}JF=1VpgNVyiLPf+PFh5!v>&iQs?$-;N!`|5xZ=^LTYMB@VlW?O5(hUS|#H@#eo z{bP()+@j=ja}?~_()aH+$x-*`qKs6BuxKz;%XXy{B_(cbF9<~{CwqX}2o()b<2(Au zWShOaAM||m&ryvO3%q`2><;T_REBES+eGDCX`=%CBRu})1C8xzT1@Diyq3coJXoy} zOwfXBEBfOO&U?=SgpK=)) zRT;P9M5?gPFFygv=jtu>-ioEyq4IfpAI)vobc1fA4zxY@LA{9TSqSW3RDHcR>|t}< zkN)d$h<`=L5KTGclyy88+O|*z!9BJq7o)xfe%CIDirM359*PG@u;|>NsyA*)i7&-6 zC{y2XCAEz3VO)!KZ921WAVxdA^+00zff|Cqg{LsFY>#uFCtV+*MK0FX!XnNg;CQMH z{*L^eRI%s!iympwFK-+)@?Y)NU_b5Lr~u-l(F5To zr%<2A+}Q!UmqOfG?75SZd0D);;RQRxNcWEMHoxQ-O>LN<0O#hViOv=%6_jv4mdE)V zABa@I2oE58nTPnz(mr2XD(nx(^t|F?{@^$V-Ggknkx(ci=H-{4gOFuBy>RMNhUcW! zK}e7IEIfuS1eMGuaC^=G9}y>w;4<%ZaL_Jf7Z;cCoz(d91;nN_<5ui8uGP0%&+R!X zi$`(S_(2M$vP9=4i1$6}1WD^$(GKrIJjA)jKm9RL8$(jddYcX&%KU}R6c4p;d0bu| zmhAHVsrMX;+mj#xq^<{Y$L4yjE!vcBu8wal0fBv(HmQE8%2+Y9>24lT=t{@HGjcVN zpC^`6D^FN&serU$Jl$K2g}5fuEAt#pS>kqf24$PdKYqOG%yUAZfYF)-0voyH)|tu4 zx3PwIL$6=kl@xrE@G_uguz4^{uNNyQxS%6L+CIGd#3%}DP4~r#71E{j53l{|MaJ%n z7BKXG#xDJ6iiZ<>pI$UuP4b#@h+Xq(a>S@Jqa`PBdkpL^buS0>Jak}p{xqZG)hl?P zquo7X$;X%oFGFD>E8V$>HB?!+3TxH(0=E`fkK&Id$W5#`+`m#bqL_JAgOk?vrJwCf z`Cj7(k3qdAyK-y1K;=DsT?rVoPX};5a>H;Sn^OTyqRRoN93=(t?|jHyPJ`v|ESKW- zyjBK1TUuJY&yF%lk#?39EuY#^mN@$m78gvX>vEdZi(&Yy+>p0mXTLwBhk8+3WJ4C% zCE(7EvLdvRr-cx5Hlv7h33jpYg^u*aFbXsN@$p&{CrR6(u5Y577&!R`BmX$V9D{ub z`Tj0suil-xR$iUD$1#*>Ds^2*)nhWW z63v_u-ZP>u4Hl0UqPJ$|y=cbF2U;{#t|owC=%p+9`B2YoF0yak2{&y10+rzQM&5d@ z%vZ2eUjqt#kX4B&oQBkeE)pmg2$Z1<6p!pgb|h_NfNt%Nb`aP%QJHjTVyxBz? zCRlqLT*Th8CkUI|Cv!KvH;27T8X%)}0ur+N*BoOSP0YJL?CdYv7a zmf+t9r_tobFo8cX{LDBww1#`gAYEl;_b)}4^Kga9&WXn!J8;W`u|2T-^sWc&J9ik8 zcD5JV2~S*}E}Gly1Sy?>z*S#TWU0qtYYcq*UbH&m6_!`m^kQ54xbFqUdll&(hd`z) zOY%O(dors86qXEG#IWhU(yeHuQo`s^zCduqU9d6~>HSom-haqG{{`Oi^#ffzjqe#- zVpmt!$SWgqs*PjmVwaVKH;}&-@YFuMNE!Xfh0a^SmEW^qW)_)ndyR{Eek8ITu-?68 z5h%DBMmfb&DH2852{h9u=B*j#KIf0S=;lwNC*+fqAVTNY53TkItFq+7)INj%643KYrc?ZtNak|%inrS&fZ#Cf{--CTQdv@-iXJk|^A%>lt z$FaLzw(KfUfq=jEm67B&wABS`nWvqHd`q2n=AF$^Be zj-9JiuO&L#+Ck4sH>0&LqpDsWH{6j4MP;<|gC0Ztv3nW7%c63UItkg9#A}D$Ie_#L z^DZN=G6a1kN3UGbn3Aa64N`yaIIg+|>})Fx?UoVkcKnjwN;Nr5$4R3wtp40>>(>ej z6(eV`ujpDVR8mypYcOL|;6I8NK`5TZV8!MK&{Lz}DrQk3h|F+c_uwo3y}|alXiHnG z7K#itLt4)T`PvvY$|S0q=46 zwv4_g&exD$_cCo#`*wX;JMLMGU17LbWg_tQ`7eM4M1U@C!bv!r)hq@b42Gi;@@8xL z$Qd2b*RaePIZV{L5jMVTSl)E^;&YyY_X`7`Csq`kiFp$pacNpaKKB5*DpJFud9tREKaU_O>}k~ebyjNwPD>k8Pvd*o0rnqo1PU) zyRf*3Mwxs9UTpgSb78lr66oVNu-J`X(t$Ci`3q?Da|3|PbJ7Bn^0do;W2Zhg)*P&w zYRwe}JrqqU6pt(|EV8uSMiaaNB{5?_==60?oH((3O_(v!G*bQ~9jkHq)jx zy#@A-_es9)O%0z-m8xw?-?5Rq^K4ZhGg7p^w8J<{{k_9*y@Wsk4aN#|pllwBrHlRG z11AXenP>oaclVGz4+V}KxRsVxIs9&N!bV5AyQ{Z1eL9~!HZpowm{FDI#N`tGgFcX4 z|AP}H;QI_=Kkc*>Og_n;-;T!a(RLYlSZYaND$~sFw4Qajyc+k$Sh{jX#H!b4HUFqq zckJZ266qnGrJdcgw8=7=h=>RR1U=&B|BM0>_bMqajt)JEeWA%#oK~oHFkicxNNkY_ z)6L7>uu}>?la^*}LnU86Xg`{gnm)FQisGV(`{<#BhcmY*lP!xs1N{EqBlzBW`Hv5+n}kBz|;pqotiH(F|E=S>LkR( z)RAN)nnalKc#=f7_1MH@8wk6jizezJ;6Gvq{Qvo^{L~w*g2IIit}y#Z6V_sAu!xtW zij0_-BWmeTQjU#}8(E3kg#zZHI@_U~k+Lt~BK2{#F-AEWT{6Un<*QVwD#_{TPy1vf zveGuf?bHKZipLTiXx^Rp=fkLsxZkuPD?F-4=HTMO=#~Rc(BpOCJN=(7MAzcfx(Fd5 zSsNR6XQ#2s$;pV3X;OW(C@5iS83!(t*Sg=q0n;82%yO6aB*|-%HGUl9uQ2|mP@9V> zbUu3Ko=(5r$i7lNV427uKevhSTBYkN+$+?9L+rAz7b$qVoeAU$14fv6?f#W30GplB zK_#YuOR4}XWy+rhe(EqWG0FcPSyoXz78;kDJg}+W;StSN*&4g7bOsIYRvQOwIf&T_ z2QZuAZ9;IOj;+%qC2VAYn|m%G^uueX<1g)O7YnON05NBeLmFt70ZJjx-ZhX7d3ONR z)nWpwAa0N`+EfE+bhhC}2kg`b?KQrC|Gv6rc$aS3qwDCNuR%9&jwi=^8Ma^UY)`Cw zr)m-z@!lQAFE^N3Vp12S^}9hyNf{e*{R&n4MP!uF$mHH2I(<11U_wf(OD1FE14OZB z=-ZYiohB_U6`iw|=kf_Tj;&Tqb4M4BcXiF*mi9G_dlqF&%=`1&*yN;VXYAp!t`E!K z{2JD3&BPrBq@lgS4ER&oEcmvUU!Z6pp--UpISC0ZY8o0#C^`haxq0q&lH$c^<~`iT`sPLDD!>g+(-Yf>@0qNoUc}tMYazG)tE1>@E0YtQKD=7sxnPA30CF8~#>%}q`B zV$Y@PP`00x&D9hv*o}$oL43#!6+m9pnerDL)7OD4F+DxKq5yr!xsaoKcbz>vdEr<9 z^ZkrqL#5zEoapfdH^I9cDfzjGdl0HAj=F5F4Du+e#cR_-9XY4M%c`kSw0_pKHBVxc-m0p+Rer%97IGmhgxGmZ zxas0SKY0;-R#E4vd0QxmbIEK|akD^3=SiYcQE}-ELn&`_U*DZ;R2Y3~Xl=QW>o6?e zD@fUXI29t*j-K6AR9nJ~G)LDkRj#zJ8xk zFDNMR>Vrd%Vglur^njKh`VM%c6h0080H9(hn#MyiK%#GnavVc?N5OMlO^rl86=-JS zEJo3xY)Se8{dh8peBrJ0^Y5Xzz(YJ1axtx~t>=8pPe;}zLY)?32%xkNE>Zap1AlT~ zvLyw2&Br^-t{OpE>}NG>ir#i>bE6%qTnnYuo>iE7DHyJ?@fjpHK(VR^v%k%JfEUoV zV3_stT-AUbEd8y*g%L=co_|FtJ3l|a94sQ*5>Bd?d_^{b#)e|rczK5=U&?okG%ud4{(&MT>VO7m%cpeOUZ}a zgLC&Ju}|E2NRN8E^h@phO~(V{60Vu#VQqao{I*nu;a$i(j1t_k9&l}3{yIFj61K4! z5I`fN=-@E-?Vby!5}>FC8SSM}3B)p6z~s!|Lg!K8q{K$j79kx(ZC=z;7nIauArdXG zlB%M@ejEfZCogXa$uA=#BT#GZ{Ub?A7modyTWM`-ihKNc@5222h3)d3;I}}kml3}L z@J$*JfYSE(Kz_V{rDrkHbo?1wAEd=WYm%c;VaN;T01&{vd*J$ zp{l>XKPJ#~!e|OQl=#iSX?Pu`V7gKsb+XiNR~X)_+aZy8_kC_|XZgTSs&mlbsEJuW16=M=N2zz|f@6j|`8;ySldZTx zB_$>9?t)W+m1=^rCA5xA!gg1nLLLzl)85~&PmbODjt)65=!{zf?7t0(o>!-{J0~*W zw0)7OxhHiO#8eqW2393a^}Gz^i=m9=w-h9lqO^I-v7xRmIW<*wl+g8z9)lu{g863I z!l}}&&3Y&oJEAIN5hLtN5~BaL_A4yAJBu^Sdh2qTE378>gMZyu`IT^& z%coo~pMogkcIANG+uYm*${;Tk;BF56ALLNW{pMo59j?yOREf56in4~)X zf?3qb`rbz^o)gMkVeT53jN$f$-1qb1^U!K@j~QpT4@kxcDoUQ*M3qS!bWY&hUrj-0 zn0Ms}y-(c)A?L>^LDQ*FC*;-*@VX#O$hbsAMz(zZ{HKNJ4>QHWgJMS>UkIzYNdGh? zDM>m`;p<2TKcub1*ox3-#jv@}YUT55%gIRm@}_1vYmFj0|)aEV#J17{afJ@bD&3L}=D& zw@F&R4N(>4eIF>YL7PqDH~V}7q>yZkB1N~eB#x-NvCqI=wDCl){0ck;*aRgxz&{S! z8pA_j?F}3&niduo5LG6j@XgwRs`eqsz!pMf$bD<|xt;e5_sMlEkavaNY9KjeTeW0m zMGjge4%+Tardj@E8;8Kk@7djx@T7cPQqr&uWV92Z&aWUu>o+I_q7><+d6%V`bQ$CX zC#rDaEgb6p{reg0Lp>5gZ5{BImWGj{cKf=4=9l36#HXQ6<2f&g4cvfZYj5u+15n?0 z(i!g^$&*g9LC{77ws+?FUqeGfgo%zUx$cV+(Fxr>+{N>XPyPKf>zIUPcuu^BKCN{j z89jl&$La2N4~HVTOdHvFze$**+Eum(f(U|9KpJ}f2(h%Yq2 zttSFrUj)wFT2Z{z+YP;CHQpAy1vF)~@r=PX&niK3#S6D2n6M`XU_&* zPx1(dBhk{*(x9b^`V92{6Lg<^1)yg{dO!zf=pGpn0SSGc+ypI>Qu2#ma8f-uTeD-? zw7Wo;8yfC>Xriv+@fmY|P~bm~8e_KVMl^luka}|M-o4u^{KoCFdr4z2%=k_MW`^84 zzv3ZYS6hoJ5ul-juOxMw$Tdi)Lbqn;5R7%gk0(OPrSZaV z^q5MWOCgI#>A#w?^K{I zLI0wD-NS2fdN9SS=b^Sb&iUHxVKou4v0(s?2Obfs>*6vp?qsRgLK41ZuWz|(Qp*bS zc~1y2e?JV^u4_i1z9JT7JMMP*lp0r<67=M2YH5KJN$Tv>zzwnnHw@El#m;=$^V1%( zVV<6zTN*nFWeTt(t1%QHL#NgB7+(R+t5cTIvrSERL8d?$;Q33@@;Q#4OgCABad!Pa z6HbLtRumK#HYdpVBwLdU7LLSQVX&e|sBN8{D|(*du@xgYpAca(2Vi_Cu+s#^yYK^I zO-xJ_fb~EL3-@FE`lR?gh=e^FYOXZ;IFQQEKJepWsAKgr+88v}=*vxZWPKL`+Xha0 zDVq@5W0-XzxatC}U)A_RN5296I&#p#fFcW{N)sveFzG84I= zy~poE!5&lRU#<{ZY+y^NfKyP22o4U0RHV)iIY#rg|G{@n)#Fp7NQSlOKmxSF2MH6;lsKqNZ*g0V$Hr(#*e%-RM- z+SFx=psm0j5?U-kviW4&U)pM$m2=D2V-koHunCdq%06PMYAPIm_3&J^F&{gvA9Y%i z6SLd7BOt}5EThWSmX>&h3$2Y10gO*f5Ii9*c5?lzv^bwJfwRmMoLr{qwYWwG|8xrV zly=_es3_%MdJR6{@+q^>A_@Qx;I4F(cA)I=E6~u9k+-m)(~ck=Ai6j2U-2=yS0)3W z1KPRy;EgCWI(4AdjP~1RX2UB|B%iv$ki7IY4{=~YGC>W)Egc;vmV!uCRnfCufzbIU zAT0bA$Rv;fOJn2f;Jd~qCV>36EH5wD^s-pI=)yWfz^BaK)@Vj)3S1Mm9LeATPdN1bq5xnF}4oRnSyiGD?D) zTb#K-!7^)gW1~VjrJEejiE{wzjbw6hH}YlM8U-Y@=kvz!TOyP}AshpGbR+(5#rJzM ziV)CTi+cv3dMvQg@yFN0GK6YU^(gK~?x1Cyg89A9u1}x-fUE=5#qz8>XS4ft=Ncss zUW}Hvvb4NaUS3YUHP_l(R5fT1jv9G_Z1Ax|P!1-plrwMI^&pjggj9NdVPS)IDK@?r z?uE?P@;87&k3a_|bPITPmYR*bc}As3mH_$A&CRudkr7C{6~lF)Hi+*Bm(~X7R(>q4 zIi@u12TBXSe7b4Gp;=J30jXg#!0jVpHRqFBT!Y_~f&yq1u-J2j2EBzRgml zbaT8ky^`5m8}1(*Tv+}d$e`2w$UaeHR^h@MYiz1E7qR1Ex@sz9n=3Dh4kmq{TX_Km z$M$y9n-|i-&q>7t*;V~T78!J@^i4nN|C9rFM-9N9$qKMs@HCx*L60vSG)u6+pL(1S zZBOh@0|)|AmYkd{13pekm{E5kKs35u$*KAd8zYUCq2V>~>~}8DT-Vv;FoTOL!H4(s z9|PUv0zlN~hvC(8CDzL&dN$$?dj8eG_Er?MC+>9mkWL!EV>NUl3W|s%gCm0DTwHX5 z3PLor9M+5hS7JHzj5MwSdB}NnR{&$c=wmyCW$^fEA7q?|`xGeE1p_PRAob}}clyvC zbPlsC9#Uc1>Cq`lCD6T~!9gp+NCQ96^Q9vmi&p{GteB=Mh{fiyX5=bDosNozQ30^_ zHz=Bm`GmjE%PW9Df;b)^It4HZMhV@YnUrl5U3|yUy85c$p!92_24RM`Feiy>-YjYBTdcpQtZ8P zx8gB|lP8H0ZtBq&rHW*5EpR8tZ;ShB(Lv8qtTTXNfJ46a*wj?7 z3FI10B|#GG&=uOffSOln;>XK|6vm6>f!;w~>SujwlYHAnW=6&r8Bs08QL@3kBy-3w{8I4{eV*kA%_W+*M9tycgoGQ=>y%k< z6(60fbZXB|0SO6iknF>UyA~D}PIjG^fL*;s z8J@d%;z0HHnG>3LP`L}o3^f=t|Aftu#*6PZ_d%P$?;-EpX-HtCx)Hy!;RX6tj?1u` z1j$Z!WJvu@bu-q3)N_O|uf->a=g!Lp(PxkG`Z10+=dWVon{C`s>_W$1;S#aK}%-0t{Ps z(g}JQC?gfp=2lyk?u5UdbIXI1hqLwNp!EZe(PK-5k6~~7u3H1G16F%taUCcw*g9}I zj>9y*-GDP4s4-KucG5}Dgh1U9Wf`sJSCQ?)TyQckKemp;m2(*yrXW-YHH9jbiB3aM zMrf1hE+E`Qbq@NICk+5Dffd@L!qUHaP*lt`)XiAB6>n8Gq{=~i+^@o)%cyB|d(#4f zf(a`tt`Nu}cpk2gj!akb!sSNqfbtfWkaPv|A%L7v7kPPuN*>B-(7*UW_Xq`(IR*^$ zxY4}>{zHJMb*2oxu3{}5uFwq$9vFV0#d%`l9mLbnbdr^ffOlyP=v1;|vhfp}DJY@Y zOqJ~gMFW#plU+TFOr8>r2n=9?7f#$3vLX=KLx&ffP)T#UqfE9s2LwuBc&dNHtsEKR zgf8y}D2;zSCc10gOM(K9COLVO5iz*^+ry2F=syrWaD2~B2#Aj#*gf8SRYEDL2*MTU z?*zsKY6+@N_^MXMA-W4FzLj$hoUbs~i33gmJiiqCYl-JtffuwxcbgKJuHN$31E@Fmj}A6})kvMG zsi}6*iT7}avSK_7stygN<8Ei3E3gJP0in2+5Cpmo7dA&<2q~}};sFjB;UQkSJo5~U zEtQhzXd!viQf4RNRXx)6&%<-j!$qmk(%Sk4K;BN6V|4lMXhQV7l4CV;Baq5VZ7cZT zNFJWcawO{mLx51|Q9FzUHxDTQsiA++3XrzZ4a0ATiyoF;eFRAm1}0?jnN7z{kL=k= zE7}VP4tlEq7hAs_9t7X>@#9qhDd0$eQ(#PhqD`lTo_ERLhsOVR=1FCm$z0s0!noI9*Dj~0_+*28qX1*H&<91jevri>I=d>zwrjti2^3Z30b68mtx(8 zr6sr(jna&e<$xdpmAVOVi4Il2uU-5+>sQH#T3SiTXYzj)s9V4Ixp-_ZeL}}!w;LEL` z4>DUb%`e(29ybb18{&mq35wL-;`*$>5WGrbMXB!NAl0ajjjU@&m%6d(z`hH~>2Rp! z&U+vpJKC5v3Xun3b_|p}%cKyKvvw*h?i``VSVJ~f7Sd0&ekL>Ggrt_vpE$7kmeKc+ z=!K-Ofm#WL6u9?&J9iKoe^29E-eioXDS<7U3s`o{h1ohZ4BA^eR3;AF33t5M(~4rE zY?0`CXkx--&Hxrbs3}RzCrwG>x49*pojDjQm>i8%kI%2g2UeDQs$#~t7->>-b5#)7 zuAf>1vM1Sl4pR#mhM5KCxBOFAVdclgN~+g)%Be7<%9wuBql^OrCUmUZ2%v%P&g3)E_(Cv@vG&MaYlJA) zX|l^?*>eOAMm)A5bC^`spEo~(-yWtxI|p*VmRTDf_i03E!1N(kuS_B?s{a7P%j zuw~gD*4WU`6g+=|<&kZLDzMHn$j#F-MFzB4nzT~CJqyJXj5-~p{-EycG~H_h>lhd? zfNv?_@e7cHFIeDtK+_6&>N5M8dJ&A|@`jhAvvbX)6Zkl|XpJPlpMM17uh>0g3wi3S zIG93zLCcGLP^M8OrRCiZ+cD#U77D)vw+9XOS@? zi3AB_9zpC1Kaa$`N3lz{Sp89ElJnOIlITS6JrK zBI5NG=_`EA#LtH(ed6GYA0Tdq?~wS0bv`>Sx#U)iViZ82KOtd`{d>#soe~kSe@vv- zq&NX=^vPNyaVCTS|*WmJI zDzq~Lcn1L+1DS$KG8 z!xuY%_?{{!D+|j|ZJ%;B9CsN9XaoG9X5WzmZot@a(WV-V*vp`C+qchs9IJlOZ(#aR z7=bh84!K2W%o>2JG@y)*DB;2Z3yinH+>XnuBg6G*XjFwSOF(y2+0hAnI_!)wy$d=6 zTnGYb&^bswKs1tepeWw6XU&?%)Zr&wmH;a(PTCA<~^I4AR(Gu#y@X z+%Exv)N28?FdUEwfY2PYP$vlT;KE}8PXW~wq_@wUE9^U@j4`rM{zg0%z6Sx;9UBqR z+|$Dd7Ihj18G|G<^2?_<#lF2K^V84?}TpaFSx8*tO$N#%F&9giT{ zzOs7om+vrXwg~t@!Sl4sbA(JLL#pi8*ib_P$_fqqDi?l^9$}Ei20sQtWi}zZ)e41{ zg+JH5N-gq|-~x2T$PphwwTrYzK}yyHBr?_NLE&$^%LwohpdJPQyKq|y#kfcI27E9o zhk{{>an6tn)q##Obj?A3Bl*QGkIKDe1kfF5>N6&GQcB3+PQY&oNKf5wgKA)kY0f=JOJi|J;0~G` zCg3Q2g4^=)p?d?340y~6fxKzX$wd$SG)IwW?VNqTy=wK~3kc!Ls(w{Oph3m}NcvJ7 zs$*$$Ie>Tg)8jO^-z7OXcx3LK*W}q^MKLc!!K8rR8Z=~(eZ>U?-Y6!2_8jRr)SSO7 zgdF^9EhSaEGp#uvUV9#J2$RF-7U>RL=;paI2x0HCSA0DC4&Qd`>i)cC)Fix3D6?IS zvX-Aepc>sn!2|d5*#ax$p%sUrWcyS>;W+%t3(Sa`D!E^6{-GT$0U~C3JW1D{Lm7S! zLT%`?2!U!Sa41NrA4e`looLB+&A#d-``{T_!(WsmuYAC`qxWv;!v+@2?YDV#V(2o^ zlXH-5dh8S6n3|__TKt}J#kCY;f>@bUC6R;)wfTY@*UqfJp!XU8zyo9jcQ(Ntq(Sqa zGH4WaMm&gEox{?}Db!|4J#62@BE$W6LvHbguq;`* zD!|m%fb)av;m_>2iajFTv5_W`qU~&c#tSMcwilDlGdldasDAOc^FuX`wIQaLN3Xp& zNx&qci11dzfxaP7;I^Kgcj&&veVTLaVb`)=x!6iwtV>b8s0vM@zaO5yAUYS2UH%b+ z!vsUd2Qq_#9+8FTkf*X7oMUZ|HMYF!TS;Sn+Dkp(uo=Ga3^uK{4;(?J$~zOQwy{jO zNwEu~dlzzJkYR$<6?hIv1U#uYBi)lVb?k}j@szuAnyfFfP29E?9Ps9R1+2fE*5Ses zFrsx}CGf>5)M=-~Z;ye#L$(t<9QHk}ey4R%VNS~Pyj_EnCK+jl1~g2_;rblUy7}4< z3Pyjsi4PEXeKTE>N+tVF!OrEA7&blh1vzM|rcy6V)ukGy%{~M!haPm`yR*0Kv*l78 zWSUV64GLT|z;|4N3`WP?{Sd?H1C7G#Hk6rX66|#=eq3H!-NBOvrUV2Y;sd}O2!P=% zflNV-J6974hl!4D$mbEtiayuoP3~#sLC=T*NgNqYPFRXlOH~oa87If58D9_^w>Dc&1= z>96t0^QT7HP0-pPqhhYsi&|qw*4r{9pbA5Fmyq@a0_$b2=i@|*k?8o-Q z7aw4f{wtm0DekHYG_=%B;L{tvFSWf1Ym2K*D-R-Kac_(*KaSXA`rm^#LvrO}UcL~b z_6Babc41kjp+7%sv=hcVZP%k?keUs_rG{QFhj_I4Gp8&T5cuM9*e}i8Ckz- zB3o(Jm>QI3cv=Fp@L7)sQ}c~O>r&p61jV&`y+KO3jKHBWiFA8*w)XUJgw>ebq$*AX zBUiZ4c-7b9_!HNVRELPn+ZT-LZUzuFx!-|RyTms|8TF7I;hRLKsplBgG! zoy}7-8HBOMKX-JzY&w4v=k+7Tho&W4CUh}d>Y7Y~4y+?hWxureugHbgtcumDLNRSa zI6mrDs7Qqae~&LsS?m9z>MfwEPQ&eC9Ce%#kr4%?WR#MYZcqV{mhO-`G}0Xwij;JR zba$tUfYhPELrNYR>4xw5;oSS(|18$5Su?}&J#Rd@_kMmq257oKTg#`JzobrE>LaFRc0W8 zR|3HgJQU~?!QVf9{+l{lbakdQI$o{1vz4aD6!DVt_ce%7J^clt@J%lZG9i=ST^#4+zPTYRBM(x&ZN#gB(Q*4HA^uMkWP~#o)a;-6`ik)GvBS;OS$S zRE({pxvQl^Q#JdvJtD2X;mCCVKOvr#J-ETO4Hp^v**o`d;WFHszm+S)Sjrh}FLmAY zFU0tl#!q)RG*l(WY&2O?{i*L(ZnXxb41lZo!ta{d*dXc0%E4#Fwt-iD91^z@M6xUw zu^bD8(kdz?7oRyF(_AdFB=ncFPKMXjrs2m}@sG-tbBxIy-_BjJ7SXvgOtRCnOX=oqXlb|FEo~fwn zMHs#~Kb=BIXPXlBMk9s5vD;O5!nwYd%_*a7{6w>QcyO(z-jZ3Dr;$X&Kj-WUtGD!wLrS>FpX&Fk-)imcYkk20Wc4sHO#&Ty#^Z~cIg)|szg z<4e#1(-W^gRM6-x1t-N*3E()k=&?g&6+upSrd?5%Y;;`ogi2H@!)t0n8Rr4T{}U2` zCKoh2nfzZrH>cw#xG5p$cvj62TAstOmweF?!u}%5iVw!^3Kfx&r`j#Cy|4{;w6x#e z;|Ry;gtNrwS(q}O#3%YV$Wsj=C+hX|!lR z+W)%nUyy}i9E#32aK)&tr{t$pRVL@q6{^32{3KcrK7$%+1Cah=?f4*W5%&K2&s&2> z7!PHVu7=~&t($_EkG{!L9J}(%IwuHFe*VDQKIE#_ zt11LA&35`iJ{tSd^DhGRZ@_OO>3rI2U8T`^U4{tW=^)>1E1OD=-lylh zDnIn6*anN69$9rgm_nb74X6KiAX8>B%mVcl&}@X-iLVf4oHToR@`m5*b|!MZ^UsR1 zB(WGIuzCS67Red!oz~6tX5?`=ZgxZEOYc{@a$aX~7lqH_;F>7@yC$gV`A)urh!9|D zd@tYBXx7w^YsL)CnpIRvht`_<=jO!x8TFT?6mkvv!X2+4ZSM9be(B_uHpkNf|E)j% z>*&NcabE*cWQcq&5iI5GZaBHD_zVz;%_(I#rd?lX7v)ZxO|6Pb` zsfr(qr#kTHzka0@T7HvWOnU^q z;=}nv)yhTicO32x;2Zvg`AY;t1AVKjIWmmqar~?BS)22#Bx-Ffe>x{WLM~>fO3BGU zl6{?lI~|sZp1h)@3&<{05Dh<#ce-LS&OAP^d;b>i%Qsx*4(_$3R#SwPmC}kzLww_K z(mX|C`UrSbJ@9?1rDrqYD#@HtIozDjhf{k;(2J8T?t)67sgcne4Trp#^wH7WFKQF8 zB`@O*_q)LMeEKu;uiv}tv~|?TGtZ=hzze!j&8cgj zV|hDPSGg*3Xmrm~a0ZY&)6S?vck7OM+5r zjp9t7nc*NWMM_qqm7|Mf*z8>g#C-bofVYc6`r-TB&d~jVY+LT%=PHrNFaE)Y>eDEp zuz2S8`xn9Z8#0>>lV}O>p(Ix+h@eua&V#g^F3&`2v5e0*6cs_3lh}`lU}rZ1MTjd!*3c@Lv*UyNB9W@V1%8?|ko3^^!cZmb zvLn&xC{0_GbHK#$%XJFt|5?JY=f2hk2lI!pARBwgflUpj|2e8r9_dsKLO}XH*T}tDFRa*v(=6s=6mot2pr8jO zMfEH7v;cjNkfM)~vHM2Bs5&~aR@R+l>t0KJnFC!?YH55*Zi+zORJP_QSP$%j)X0sr z#^xv7JlvT|!*PaQLmLUIO`;b6;k!&wC@vu(D^q6m#?b7`;z(p%(k$<4}{=NLC*-pS^xj~_<4?(XCqx% zdBmA3+3}48xz6@2Bs;fOuaG{w=R$R=DY&e+l!uts%AdCzPDLk$ z?Q;RRg;bnxzO0*hpXxC$Pmk$)H9vhNg4A7AUU-YQ6g=+RUWJ)R4Jf?}CCF{L-t4e9 zaCevZkrS}hnP4xud22;Q0>iJ0ZO4fRBJAf_({|n@E7|G~s6$5*izhcD|2~N4h?NXv zgXDM{V%ZW*9zB@;OMi^L3o0$)br%-;bf7F&(t#3 z@t?|o1cKW!6dKP@!oW5K>yW1*s`ADPO5vY!4-cl^m@yOlHh4>u+tajD$zl(4vK=#kr%H5d?k3=U{)}+Rk z$tCWrR8zyFG+awbII#cxFZeu*dBjNH)%)VrB5o^f98a_3fB{qac^kqqBv~9Ci<)>R zEF#j~MioEehp~5d7O%)pFHICGw2iWSS8&!-*)80AQC$i7s2r|2FYahZ)$ZM4RJc+v zd#1LD;?4e>4sURxRiXSdy2=d?3n*SkJ+@L1r$VeBU*Ov12I806p|40#%^D?m!`1(N zOWC)?g81)lCYE|(h2Sh5PLr8#vM#it?qpJ3`aA$_Y_J@GETYiqg*-3}5_1RB6&j3U zQ%9^&R2upX&r-)IWucN84*L8pY+}iSIgJyxP)ExgLjkl*H|_W{BHHC1z*|5n7{t)k zt*j>Nl|&4E8r1&e$e;I4@9xID&x;_TS?NFj(D{I^_nD@sN96kdBA3|l#A=dnfqn#q z-W>)HL%F8i6A6sf3(;y2mJcs;Z3{a{LO2>5A3yu;2GO9?E%G}8$@4)2C2*!~S}(|5 zX7cgVmz3}Mlp=R`{*?wc>}C8+-k%e&o=id9l4^qww5>7XSM;UejX+xzG2atNga$r0 zLMkNvg|?M@t?U-VCHC{xHAIULck+8{w!(`&e`MGWzB^{0f;m}|acOz2fi-l@y7rkJ@xXj~ zUp6Sb(ZzXPoaGwZM8g}kTFC04PblAP(A#6R22DaJfFS@!4DfY=|#EqEt$Xq{h*BiKb zuNuo0Ms5h7MGviJX!$l(R6%b6G86!iMM!msvs0ZjWfr&L^=BDV7Mu}LfGnzMcfB`1xj9TREXb{6v9F$Z1wCsys5xWHZKTw$QX z;f*-wURZxGwV>$F$e@K52#MlFnOWc*S1>zKl(U+CD?f%Jp=7s$-j`F;1P1s6pgKR$ z%)IWtf5wGM=$J@RjuXdsp9d5V95+~&s|)2ZGk_-Wf2)z)wKR*dkK@Ro@BUf>(!1@d zuq^pW*vr7dVr?80LNT#E=EIgk3|bREsT3!kwA_VU>~JttaW9*u9@JdAJv)op`f=3+ ztO;sD4Q%RgPvWpkIx30du7AGDRq<7GQZS68+ zhTo)*{}phs=>&*&0(k3=H``s#7@{3pR5#s;QN6)JP4&R(YXcRD7cZXuSC~*{@bDVT z1LNb&Y)V~rjIozAZ|C=RnwfZ4rHl9AZ+JvC)u%%?gtY_>-WKirQI4HDZU;<XN9I*FhTS4L{O`9EyX#oNGIykBZTcxR2vgm@8(O)63xa@1=)+_HAq7A$}`3pN9J|mPOQk8 zE&C)O3%c>wB>)EzspNJ{yspjwY7L&JFew#O*LxN&2ytviGx(thCF|3aX^N+Zmt|b^ zEahGAXw+W4kaxaawR4{M^5y9|sX?2YB6Cde)~cps<;9Vkdlk946Uh?;14av53VMJB z0u&cVY5#{#3ZsO#?oWD1|?XJtG!$uG6 zr$T}*cT0JBLq2z~|HyUPScuO;L0G2;=zE`9r7k=Sl}exMJ}aQ1!cHo91KAEJRHSjF zy&n8I*4~n$L1`KBBy;3*g!S{0St3&`BVJ`K=}?S=C&wxF0DGJSVS1;Ow>LQ9^>~H5 zJZ*68`vK$cD?GWpLwe{jpaz9OrW=C;U#s#WC7F3MIcIdAwd*LGT`t82m8fW?ylk!L zymI#G`rIGtG;o7_U7FrC2vKy_=&HA|IWJ;M7v2dFm)YmOMmGDo!$Uy7_zF|*s1$@p zRGNf4b~6wQ=Ag7$=}q(w+O~Hu%U4@`Ia*~DIqYV%XuOyOy3szqW@z5CPljJgQ(jh& zFde4xe2OL|Nh5O#$3uH>ad6q~ZN5Q|DW|{rmSQ84o)ReP*Q2B(Dh-E-u|vX?nxO{H?0 z)RftUGDC_}5xG0s(i$6W9DSMq;c4qq%r#HprEm8%JXPMhF*97B6macVRMb@;vG#RRJ(^o4hhI!jn9ces*$2J?@CFwQUUC3ht zOi<5@)A%iVZ}dh^cx~0R&!^gZkVX@REB##)EtiRHi- zh?U2+5=d923ffkxb4`{0#U4-XP#(%T1m;|NMhBbfyAa@hT4IB4T#9`7yJA{D5ls;D z-T)U&n5iI@Xi2a1QW5v&+qx8c1=%N5?ZmXow&GlivUzS0{T&*fW@Uc298dR2$eCB1t5t+rw@OcbTAB zTvBX#)ZR>d9ZkDg5@m_B(*_o_Dt_MKBuA<&(Hx+~S{imF%w4RqSlwF2$a( zorUKb8Sav+OF$BJZKq@1b9Q-^e(#sLUZzp*{W%u%mipwswZ!s+_+*&_7$fy_1$++K z5AWFhad-%VB6ng)`en@3om$@n{RpO?j#n6}IV{uyn_JIz<~*^HNO|mn*M7MNNn3m^ z`qIed%T}Sq?kgNpgPqq&I7L!c^n1jWViy}@9Ejaj-G?oPV%5I! zQK=24cQCHTN?#_7F^d{s1I+Bljz!JXoR<-Rya#9-;3q=!Rj9GR1&E0s-_{|P* zTIApWV`NNhE!-P;8FCE`y1-y;xBemG5T1WlWy1Pb@?8W@HP%JBuWK}F+28mkbdAfx zF_YnM*|h85BiwYmgNgdP}2%khj zMp|3Rqvi)Fv+@>)oFvU9Yumw+PQ?lD>zLH}IQjy-LCI9Mc5N+YZ=4eozo_Fs43bcL zTH|(VTDM{>6ngsay&J!aR4RA|evktp51M2S{Q2jtvUEs?VBik~H-gV+;D2mlcPjq@T9PGk7vkbero zM^CTSnZK4rtt@Iq{uz#5_R)19*DlS6A`{^r(UIZHGgoj3i5jKEmwONjUPdqBo|o6K z@~=(N1izs{bV`&ZVY($@mpUA!8zR_>0&UrCPj-B|F!c5R^1NMuj3IyR;;RJuHsE4fXDZW7mgP#4m6H2Gb{dZ2QalY4cR$#FmI|D2qsG9pc2XQwDHv@>8PFpqMpholXjE`Y4z z27UZxtO&dV=xDZYf&j)>cj{{uoN}b18jwY65?t ztB|E*D~3;{)dNVx08EEA94Q$Yup57n2+S95A0BwfQPA^DRM)V z3d*l>UC`(%_(b6V^e(8FLD>S%9;s5N@W<;~S-3NiC?t8yN#uucjgRP-T5&(1=;S~1 zlGXd4#X*|!xb+owoI@7#vfQm}MAcc|?9u8_nf72+%Y0By&Ih+9ty>OowNMphhPv6x zfH;GD!YM3#`hjDf(G@)~5Dp-dzQt|Lh+r<5?FH-wvePkG2{bqWJi~DN8Ukh~ak!al zWwmaq+w*a?nQL-Hx4-!3!7iX37HTa{zv7SrMe?(Q_37um5)QrE&2>~<$$FA?dqEpE zyE%U?J*}nvhE=ZHt?qE+Y|YuSn)IA1N{YthAvTyhb1rTJc{f6QgZUOi`}FdI@vU- zyG0D~C1RttTk7%CGaB3mMJm#CDC76$hF9!W(6MT0en$7^l#Ea{3hvwtg3#gBIWGsM z94nom))_#zA(CIfcmhBb;#5#co~ppI19O$vv4wjs8VfXkm{x;ks%cA8qz4|3;=n_uaiT{Tf5WV0T?2Sn$oGckm9nbLBn$9&5gvFhi~NrlDMn~YlO*YM38>mjFm z4e*}&n$Stfbes2FkKUWB%-gXTUXu_R)>OR$J4TJMRE5G8P_bPw-SF2kvRO*_s!vdQNI#Y@|aI7pL(Gd8Mkyd{@x z&ch{jW-)DVsL&d<;3pEC^nz-`9oRKEOhiWK^I{zojOie$IJ>F)D}3m%++r`4vY0Y# z<;Gk|>W9t%dVXS+2K`RYmIuMwEPEySWAFw65e(u_06grSodraL_IP6Bwrgt;MHtk* zUS7Nc<0bNx8B{8<(-DKGjR9cOv{`sO+}#5^<`lt0waz@}i}yq1w225}W8xI4on#mS z8O=xrqnT4koE7Pv6=<}KyArYYwH&9^8B4LB|AbDtzPZp=gS@rhpT5gGhvi4T~Q7|84-0apm< z%%l@+`Hq$V*#Lz*AlTgU6AOR(Ys2i#$VxR3f;ma8_IGzRv4Xd*(uX{KaaJw)TWP$j zQ;v~9C<8N~z?VP6uPK!Gd^sy)wX|Jh_;l+}*p(J&&FQVEx{IW!6`u`17VB>(67K@ zw;S#6Tb4nT)7B;l%?89_;!RF8Y@dRd>A@5Nw2Rn; zl_$}XD zODHYI6?0qVGkZ2khXUSc&|tp3m%*+_B*y++%37TmF$~@##Xp6f6%Na*6K|(jJ!M_s z!&rq$&9`s~+JBTgq*Yl~UK^`c0>++yY3q}5XC3#`oAQF69Qf;9Iw=B+9RSy>|9Qa< zh)uxt2BIWg`2U3W9}cfCkqk?Jz%1|WmC~%{T$d~NNI?GR=~&SAXc;p78OG+lR(SpG z%`elW)?XvMyhLiGLNiwslJavfof~r5{a$sVb2~18jvv~ten1Cwad23VKP7`I14t5x zF2g_?P#&=3B{v5#Jdn@O_1F-H*a*fM+#FiU<93IkjCo)We3R0$vd6t4%is65{^Q`W zE(8c6F>K66L2ZlCJvWZn#N#;}J=ZEQpFcrPw(RKn7futfOpC0M-2BXSSb3{SRs&V8 z+nwduhp(9w)yUhZ|k!Bu9s1ex<5YM_K$r^@hnGiGK?93Dx^|wV9N15vCTEYfB8Iz z7++m&By3IK$eIk_#k246yS(`i&N;-ZV61BI6J6`+%7^uR7~`4b6v4FPCik@_nV1WLt2+K1BJufnhRpnS zi`C|O&x7J|uLaL6?<8u70M3rjFU$TR>* z6+ps-NS>8*1h5OC*F@StZEsB=xHItnii}xH>_C3FLSzr%Sz4tVXMggB93^{E1xue^IX)^m>{LF@bzQU~mJ5OeaDakY3dCp9Br`XCYj z5g-_kP~}nGcbWvE{r6ZXh4Y|S1C&!lNdZPBrNP>+8$`j7 z#Aj-+TV7Vywru=iIr`vN=OYbirFJtJ)`jYtNoN*G9b20M`sL%vVb58uYUGMeAy+ht zKXgsrnYOM`m_TYR4{KjCRCfl;tOEf zk*E}4I-;mv`L(pYogL7p=$F(>Pfyqyu{?vA45+P~LPF!ea#xW)fRj^>Iuk(;-U~ad zV3G*!J5NQ4+;?1-WP@e(4gRDQkRb+_o%VaX45#YlFTci#{sU-^%YEgId-)EiufVYR z+W)aO2uhc^s(8?!E^1}{U=TC{?<>2^8EPte24Ia`+&SOoPbP(hbFWEkMbg3QAk zAb3N!5N|bpSa@xB1w++1SqKq*MJ1WO8CEbX`1|uDOLeSvD5R${4y~T}11A6hehHL4 z$7P<0gN2hVZ_mXp+{EeS611XDQUp?gk^8P?o83K;0m09!VkDGblbE~&?StUpU@~EU zU3)vadf-OEd4!75d6Gmrq4nGrX@HCj5+$TVe1+LviF@1Ii2MWcCKi%;dfF1UOy{#= z6FBge<3|})%QLhitV(6Q5y;HmOpWk0pL0O8#i&UfG?u&0eqtJ`gWQDau=gz(qC+yv zlASCRS1Zz5u>U7=_v8Qs6li(C&qygM7$Cqfdg&VO0+)1Ut<89i3dqs89dEQo$Yo;I zyog}d12SD>gZe9&AOw=J(CI2mP0)*wVH-Jpqri3Ds)>il+b+wg4U#LBh70-Dc2NuUmsWW4o)1zJmXp zOG@RrLog$v9&OohcZyj%-foZ%I7e#|-Kr)0-S=D>uLpKMeXE!V-m++0cN^%%BsyqT z^%2R*3adC!5?V-We5Hv!hWZCM0Z3HY#j$m}woWkY6h`2HEDXE*#6xKTY-!9)!d7Yp zF>=Zz@3~JJE9=gt8ZCcZybz5vqHCO2*F6~%Tz)$4?X-B{-&y!DymO3N^>T=ymdB)8 zg=(j#++*vNJikknLT<`{3&@BTFXS7PIfR6ZntxFm2+@cHpsDFosDnVi6@qupI(uEE zI3Yk_JfkMi&?b0y$ghGOTaO+pD=$v4honX=up-cbrY_0sJ1pbF|BesJ!PDJHN}_aI z>7INr5;>w;K5*u091&5LrkpoFXuk1XquhceE{Y+vbuMgluE6`o13(2o1Fs0`O3;A? z`9+``(a%TFfNB7U%49)C0I+%ZJM`(489-lI9Xujd7y{q`Y(>s6o-%xUMkQ05hYe!v++!E&qJbDb<(ED z`%{>8h^09HVx0KD|MY<38KQxc1)3djdm!v;6?j4SsyIZ>QQnO<3{$q3270ORNEsre zz}zka9}Fo={MPE?B;^vvdMvKE0*3`?q@`HA7dhzaVYYO$h}a#KA=H_-X8>)93e1}4h1WXD3b$tj6`$ziL)JcY$m!VYqi9)^Gm z4=N_Wr{NN^N_^Wv=iEnwXct0031k>xnJz6Yjl58E{yk?7mI^Xd^`Kr01&%Oae}QZG zFDM7nwnKJkkhdU2ebD$vsPWd7xxb}*MqixSbk5GA55x}Ndj!#Ukq+?f&8-gF(`lX_ zPBtFBWAeSo%?1rKJXA-#w@v%_p?vplGib?P*^Z5}a;}sb3e{<{be{YLRZ7P}6}3e_ zy2NLKcR)5cq-*G76In-zLv1ZJ#8-=e2Z#GP07DonrylFG$7pNgz~DgTd{roJLMo4> zco4b6+e_`xhDrx;{|lk6_Z?vT5X>NRJ>cIonzj5BP~@O>qydqjEe~7*OiM8V4Rr`9u`t}p~&OZVK3&s(9>t_Dwa=|50w+P&0oqLKEVQ$6zKnL zM%xRi!NBj5hZqm41{pt=5U(N>*9B59U~ZOR${oDLNC%JpBe#{Tq%k&13W`C5YhU7ba{QhSQ&9;MA&Qu#WdR|#_~ zx-JnxvYvF7j_+Yp@(CYAq`{(-^%b73?b#&lFAEil3`N$|g>-vr(2dh-NL`0_^$-{w zC{!l2ArapS#TBS)^j|_EF;6!)O_2VBZvci0@b+-_pcA)Tvy#QS8V!tXKJuXEnH9uC zLqS3X64-96Y^j+y)V64Wy$1@hI<-z_pe_oj0Z&aH-tsIwH{o>KT3aXr3@HdXr|c}- zoQ%&6Snp&xVyQK50h$;V zA_e2s%#z?ltw1pu#})3wHD_v`Xf zStOCN>hzxuAcg=nU-nw$dDsQXJH1UJRn}+2l@!!Gr2<~x@)alZKa3j%1q*bAcdMV ztPy^eK?SG;Vr+o&g4KkIHs}_%*L+ISlh-j0h@B4bO^9Y2plYhdNy3@*;%{n@jx_wdm z6Y4;djN!$YiM{-qAyjtG@!j&5GcJD+|CMk64 zS4Xshy6cF$jKa?CE)8d~+8j$v3QAJ1SS#)C`3i9A%|fh?P7riKD|pyf|G`Fy^tufPC|TClfhAJom0vQ%#S!(k9dRgkIO9w9N8JV(>cS}bKQ52hM^TU z9#SA9BkmiOAX)gn%>VmAOr4RPg`G>cLUG@%?I%UHEiQ&J7-$Q_*qg#Nbedk*la~{` zrPdme@w~PM9jJbu0{Prwjybi`jUz-y6sVrxwgg>Imi5m1X30mFr#Y~gzHsV*%Oo|X zb_I{0idpJ~4F{Gdjn^fy_dn2T{n2qnn&xbm4eT8WX z8Dr%|wqnT;o$RiS!w?1v*duZwx9~PsJlGxLcw4`vQoBcJ`hH-d?o&dTNQM0<-4O<-O%JU0w&nC<$$W}tXD>m$Sbo(Vo&ci6A;|N1+2Y{rFqJY^n)Y<5b1kCc zW8H)CAChEY+jR}J;zA5>KTy>+-CI5g$ScfDdp1JlG(hlo@mzhPj(MoCurQ1J-kMbv z7{eDsVqDp-%K(bZX(NB2TLBHLDG^K(AVCK80LBaPdbE646h1d)e0@OKegk1|Dd*FWIX zYxsAg{nc#FjGd}n$9pGT_9@2=QRDOj>qIl12G@!mQIQQ+%9%kY9JOZTF}j^~d#pP$WugnTTE<5e2rY?RV}%?Mx*`KMsL$o6#;L7Lwv1X4m+tojl8S>t<6=@U?9DRd#_- zmy2FPqaL)LV0B^G2yd7!mSv)^!8ULNLT(43-RJ7=FktH4d7R^iA9BY+P}7>$+FkdfM#v?TAuz z+=4=&e$cbj;75p={UeV?h9{WN@u`nYgdR7Zhfe&(!X*h&Wdzt^+C@jxt;k)lNRmuM zVV2yC@2*pX!U25;s~i(=bF+I3fYa}n`4tPojoqFr63ur%t>tD_3JsC8;=diXMZ7NH z-E@GatL5nRPUpPlQ`1BOR%d--^=x?m*wA|kMYZM`lSTHPm8C;5((*nXp=}xuc~mH6 zXx)sTGH)2HL^y2is3Bg!Sq?%pu~Y?g+HeTCWj?#v_hT>wuy1^vxnNleveLR!m`1s% zSq{_U`^auqu|Y=;dJskiZrUb8v}qqHh-zP`KB?zmx77K_X8+_MX6xg7F(DmYo*}Qb zp9V`H(ku~-&wzU|ndS2Iqgy!2M-q+utCK7zQ#yq#i*re;wjzfnthKE5q#+k;-p}l_ zmup9SeAB!oC`nCV5EI!KUnLwBn}&bw+Eci{0Ihz?OAf8kd%n+~a>u1-UHLXwQReL6 z@Er){DwcxlyuD(?FGywY_Q8t~J+L1SJ=t9_%Rh}q0BpAEf}`C&qn*rO{x#P!&VlR1KiS~e%y@vENe)j}Otq}((- zm>-61+ld`Ah)Y)eO_J30K09vuDADw}tW1BXX?W@4j>#4^B)o``U}9zMfFw5^5_j!k z!lnml+ARJvyz#PX`db=X6BD|AV)s~2cm!Vj#L*hD_&LqIs?=c^${Z-|Zuux9C$-P! zT1Q!RGXHeP?YW{cr%zkGiG#PX=nLAF&&o;P)Hmb&L#}tU9T%Lis&i1vQL84f8A+F+{Cynh;>D)FDrQUDm*3VrFgo z{w=w)AW&MSdyE-2gaha_~FNfl_E6ZJ>0#9lWgF7qgu&pB7U8OmtI9Yl!Js0%D z;3D+4CUlb@1^fH;dEZ&xzL&H0{#ZXWad+HuGvzf48J&5;0F~lw?fg4MUlO$X)-pVq&>(=6-3LJ5DN2Pd-Cm z7`smkvpz|@f4voG*dG_QU+=)^K6Q-5bK_&G$doK24RPV6xpGoplDU6XgTYx6HEO+R zHF@;ab5W9Zai`N(*QMMUREZ&LHG*UkxOCt*J32a86J+%?H5Dn!?^?-pj*gCsi>1NM zupEtqLkMb%<8-jJ_@_ zUi~q9{L6#ksAgkV?^NXMppsHK;qt>1!&ldFqKa+k5A1cT`rPc!rh`%UtpAm&DO91( zRH3FzWcpsidsiah1Feu^WO*p7N<|&drH&GCr)<|mIc38@Bog#fLA8JGdk9s$?4(Il zTr^CxZilrj)Ube_V?01PyR=4795)GM^FEk$ey+*Yhd4i|kJ{_oKqwVNJKG0ZsN$p1MwTJx5P19V9G^iG@(b{dF5|T(4 z8ZY3#XZ<-How2vKCk^ya%R}xFK{gu;i<>&bdi=UY3hjIrx@0jsozhw(ST$Oj8?j+; zfArvLjWCM*=qqqD!#xUEoufLxP)&m&`+j9_H4z?r={pW0}d7Y$LW$A$h*xfzB8%4J=(GYi1`6 z{IFcZ&cQ(i{1AfR)@H}?DB7liW$qm@&#g3%nPHe+P~yC<`}f<&B_$>IAiTE&q2Laf zTb#ygYdFelu~riV1D|6wV#Bu>yFa9NJr&jtca6atgQrL`R|LktQYNi?T~Fq;Z&X-{ z?@H0heRz$5UkOfaY^r)lT&0LkyTtfWxoCTOnp7tH>{oeena1q#VXseLjfGcf%v0zc zW)9IPc}!W>L}VoMyn?n#HO#W5j_Q!C){X>$)y1{7ND!fih!UQjk*R5x87_#8x?s}k zKsOPfF4A#U98Yc>E!ozHF#BWrNkxkOkPF*NiCm+AS!6#J4wN=NuMzC}IG zx*zkaW+^)A7Qu~<;n~43t1XFnO|JthN}*Gs!5AD)+N*F6Q};Z6Y7yQz*y*Djb1X6n zpKK&>E=x|3Y#fW+n|M*kL~LSIRMw8!{CNa?q zLtWoXyQ~%)4uj~$pqiamP#^_(4G;&xY*Nfuv66SGtGiC+9oX4QDIV6$HqG=ECPdhJ zYC3+)&H3plJ`2EqziQLBr&(5znhe@v$Ngju$WPdG?t9TwR_}iBtsS@ZYAzm zXA#O28*$OUZPLB|3z>{*9PPREF`3Lq0ZR%}_}R zS|iOr%R@dVa!oQNly+3=EF6zh*)u0S1H(IG=r4eyooRDAq&51ahJAAX^yPt;eMKfe zns9Nh=tm-LuesRtR5F|9l)4G#KbglT~grvOgY<$c5!~}dzO_nP*16=d_V`>#&DG6 z1$O)&53vBRshPaqZSUzGB{jv-#r1=?t*;McnS6X7gB>FK6(~R6Xp_dcHF)!gNKc2o zecEDY<9KM~;DU~YO zZ4UI8geFDC?UJQnI@lw~J}&+W=uE##u+7TJvjD-4P|31It=i1`K^wksq)Nh-hxW-Z z@f+{3C39`kxxw&+HIcvQ7Zrxz^;dv0li8M1pN5oFx7*{;2%%{@T^hDr35J7@Wi<4H zfh%@lJ1}cH5$iX@JMEeX#u6?7D|3q0?;?a%T$P>~JIg(QJf*2KeaJ8)xcU}?H0gB3 z5OpS6K;^R5qiteo_3qPyiXc)@Qe~H(euK+i?xw5SA}^_Dsbb=8$4=Juv>-GDjKI#e z_*P_;l{;F<-Pl5w&b<#yZ=6w{+|=9ND{zY1K6J$ijJk%2@RPDhmvEaY z{HO5;EtO$nnqlqSkd}4W_;@X5cj65<@p^eVmxh*B7zE_-B{IwaS>3#M!YDUZm;qPf z`W?ah5ck5%gT(Y`Z#@dE1_T=LKOLmp|ExQLZs27#48s~*drFOcPZ8VZ%QounkTHGI zGE!PX+cj(__-k8J!a>c{pFX zIQ}+#CDyM)y+NFRuR+zR@{azf`_02Ip{#qxjp#W$*u-yJy89>f8|L<~flzY>{yylK zb)443+f_3`iH)hlZPA~Sc4o)e%vJIU?VrYOuG*^h(gvnmic$gX(ZYAdy>+fmy_#j# ztK&cTYS&~%%zq`gv%GGZPGtXw4_Jg)e8eL34hwsp#mTShp3a<34C?G~N>27DJ}{nC zeC(DZq!_qPoH@p=GwKarp*F|681m1xBg34!xoudYOG9^z6oNnkZ6kHRQD zR^A}rfEdJjK6AStNd$>7BsH9QdQ~YphdIdSe%Bm`;Ib7rl9jTSw3b}xo9AZ_iwWJC z&WWx$_r|q5!<-0`%y;Nu`#F4G?YDYKfjiV1$Std9=@&`b8=FOBW%<8 zh~XbcH(;8>ww%3wZDguoFW}sO3pAijCDX@RzUtX+`8WgDieTNn+=2EmI_o0d^z`b= z@`176B5ny0SqQFULxLvba(Vt8#LBWA3olS!tdy6q90s72p!z9fAewNmwknCso&wR8UEt8Hj}HkKUK zWH(%`^S`FTA^@~NImu!3R*Gie&q@qIcd8}v7rl^972LN@%yK3SO9|FsSF;RzeI+LA z;-ey0#RgT)k0EUrLfY%f9Os*5Z>8xUhT;>F8Nk-;UEd)WDf@Vw?fq*?f>;H0!KVXG z*5{AO{ehDsa8P%1S73jHn=sMoPsUm=--rq!=$ogaVUHK$IUT^n`M{s~}%u7~@ z(cLJzIm}e1SU)yx=7mbhhKHz?abvjhmpu5aW&GM9DoQW#^K)=zd~%VsJ)S(yudL89 z?G`@XK7C8sEjIrDwRa`nRCZDSk)+<>w5&}M@jR? zpCzlRX+PVQ{n%g9Auv@`B*%Tfi7uJcd*DN)Y-cU&54>YctLTWI0f8Y@ZOi82qY&=2 zb!g_VHD1%v%+a4;&&N=R=H1De4WHsiG)ANStCaT%)qPI8KUI1e7O$mmZ6fP2a^qEt z3Tb%STqM8aJL`{tGM?id|FvGZTH`c-ZTsAJ@$EWM%vkE}RQT@ZF5WNSN82SkWG_E_ z7qh+P(IuOPQ~3#6We$XwnGzN*dV#{F+hl>P{LWw)=7-Q0sDICe7lyvRBInv0~M z=dMZqvk`;@vz_Am>|P(=^irqSVf0D$O-u7qN@e8dW>^!TOq0=o+vTOR0;SqP0N-wumjtlsN5MMo{I0c@IT=mo)|Z1U~N)# zWbw%>U6qi|b0K*r38$;YOI2>QOSLaF$&gzl228Ky$O~GK-Nu54n zpno2xGo`KUb7$^ebssuyqS#cFs<&p`PO-Rj?2NAWZ=bnhXIe${&)w1|W?BOvQ(eq!DvtR7BcPN+e z$yb8B$L88Py$I#!-KUn^$BXCH`h?`3N!FZck=6*lBc!1d*65+XpS-U^OY&J}fBLKD zDAmR~hr?=>P0DKz9+cSew6dU7(&_9e_INY;*?VWtov9Z8rl-ti&oeZmmPDGZK*`BN zy@DA_sn;^>=qjQ|h?j+$#v5HJvhO{9&Q3y)xb4w_=oF<8<}!ht%5VCLCKq0Fn`8t@ z665W@`FA9THzuDPDM+#Y_%d+8dP4d9<><>1g_lx$d)&1j7#00kS{TZpsbc8{&^@U$ zqx5)hLYi3*b#oRSmRuli(>frgn`2;bY^$D=%9=GdUw?Jz6bMg=OSO#-7q&<+GoOFB zJB6uYcwdFbgJ_{Q57otX=fsRk6CHNW7E8&^%;cct9Y**lIW8yb+9~wFE>AdmM6W2` zDl}drr@)^2-mI`}G{oBDjoanNU!NZbIPWneu6$3WgzAt5GHi6ti`r?y@mS=z?QHxF zg=hPD!KWY_E#|um(Q7(qOUn0Z_OI}{01Qe#Y8J@}HP6Srzb_pBA(yv_aExnx+DF?T zW`*SNhYe;5%7zKM=PuuGbQ5MJ^1CKw_GTX1Wl~jeqpdN1LygL{%-*nvl$OQeekr-8 zOwPGSIi3@4X8_0&H=}~})PAu~k2O>1X0TkQ>zL`G7fb=C=v4+D+A{Eae!{7*%-9v2 z9zFavQs|#aOH8tfc$njQYtJ(wm5ZJaPtBU!w%dCd?|kWIEHb;DMew=TG(~NGUH?Z0 zQN*SilJY7d_yi&_PU<4 zLgz!GSjPnHoYF`6+qxVT9|PEbBBco5y`vow<}?(UQXc-$DEM)LUYL=@XFA!AY3|3g z_qWl1K>sa1LxLAvpCQA+(EXI*LPCMe9M|#JjZ>k`<3;9NtW&AVIa?~OZ%TbtQR$&I z;SlNG__(&0?`vwmpB1k_graUF=VrZwn=Yl@5m3n}I9&bABj7N*0q3$rnpddeA6z&T zw#{3UcmPws2I#S_&nCv}M_okdTYEX8@fW+Qv9l+M(N~@F*0>+s&S96OSo!6^DVJ>z zZ+tE>D_o;b6P#_BxOcuaQXxN?_kM_*p|aP+a`^lT^IL6+pg!Bl`X16qd^0JbZ$n+MOB}cUkaEvT9i%V1UF!?s=fWH#powl>J?hcBT8!9iL;eEkKcKeaYXliwf@O!^$RT1g}#Tr zF}NnJnSERBH}FMEVQpJ(9Rl(lWPNp9+?y=)xNoRthvk~rIq zyr!C?Z<>uYseRL%FDHvAor_AiZ>QSYR%u|ecfG&%=_Pf%GMCU(=n`ZksGB9HVVHIBtO}iCIPgXajk!~Kth{rOD6kVlqp=#iUdH~z=oW* zqFin^bijl3J-Q@1t8>dY!?t)ywYdEDXP*-Wo)|fiQfmFID;A<{gYnpRDtr&9|4_xOJ-Q^ zYFJg|hZygXq6Gnwez_+!J$iZ78l@gvI;GVkRAw9PUhN$|QqHN2IiK(f?XmrG^Lgzl zmThUoO=*A z_yNe)niVZ3&Go=xw153;oyDQ$Oc#AXjxui&JD1Gct(DxZMef$#-+fAJUjL->+KY92 zgqU5$YZktRMcxQjWjR@tv3txwOT`^NkiR4SH;{d*UmryS(KR&GV>Y(TpQiaVRv!q=j*91Gsd=ZV|&JKldSeBHV$ zDfHc@FqSoGG}4e=2u*g!eY3~8)Z~X;1BX7ZzG)bFW3Jcyeq+X0bB&A$LdJ$@^G$}# zP*PmdO7DoP23v$+u0kqgsR8f*zG#6vmxukiTxam9>DNkS5b%Ae` zS6Ee`3g#yBnqPUE9wwkQW<>8ani=Xg8ZA91e`Gi5<}uQ#lM{h&yvLeapBoMByzjuj zd*{Znh#O5=QYWNh#oyzLz$MtFD>^hKGtZOsTb}P z$w-mp!Wsl1bGyuooclkNDiwKErL`2f6ad7JUW2MdBPe#MlCuE}pBh?CLEE%xaRpGIW&71fJ zn!gu~es0KPckFO|x@+!BW+Azdx*=w-{4W3etCK|2;k}YgyJGIR(VM`vCr=RhRx+|W z$GR+UbWlLy+~D<#FE+KlVH5VUN|*X^=-d-EX3n<)78m+iYW6OvNN%5Hr9_w>`bSb- z3cyJ3*wOO%f~C=5g?~c5cU$Hy*~VH9Tb8$Gia@m625PWWeXM^$a1YxQ6L6KTvp6wD zIG%iHXE<8xvV(ba#!WQjq+YDA_x8Xm~9hlie@~#I|$em&PD3^GH zT;tW7t&wU`tff(Vt@X>bHCXyE3aw;6kDmTFlXnvI{%N%PQSp}1(p{%Bo0Blp&afiZ zQcT-}>f9D~wUHE)<1yTDy_~)_`49t!9L>u&kh@K8a^c-2HFA5U0*`t>$$vJ?G-xb7 zyMX9x5;Rn@k|&X8+dT{uo9>l55!%blI@!j-S{B1ISUEpqt5TvJwa#i4tWxE6-#Mdi zA6mV)j!jtd_hB1U;i%7c4Xd;5Z8+i0rLNampQ7|6#$rfHor}ZXJlKuCbT^NlSJQcD z0AiJ1j$Q+2{X$P;O{MJgYqGfoJiCsS)8+DN@1IVltZa}1ovN-SbnRT|ruEsq!)fs6 zVs?h96!HUfkIm4HT3?Jy<{=C&mj_}-d(kJbuDrfVZsT2cQ$+mG#_r}Gza2xKW zPf@%2H7$>s0tv~&p@q%4;S+)t_WQ?~>R-F>Go{2OrWn4XNd+LaAc!&G@Ho_=RYgS{Qh!(Sg!tUL?0H2f* zBUM>8!reEY1fCLv4eeJqa5Mc`aKb`>l94%w>QXJxb4pTnyE}7!Q_ztU63-+#Vvi*o zYa5ERjVtI;7JI^NJb-?9h5qPmzQp!B+*!0b7A8v4wbu+2v&KJ1B+gOpr&hP^{gjtT z)0cmtTg0bid7 zr%c%X`BLMW)@WZ~q)hr@ZWW2!^iLWN==pm~bPpOe2|Qiv?KCIRx?#mlfLRtMp5jpT z+KrlTzR!Hr&`FzkaYEVkz_UA}T*DtaWMcM*!<(AAi%p#Dpx?C_3mJRE8@;I7sqAqh zvv>9WKgpydU7=W`6}hAuslse1W|hahw_hSR3za|YtTgr~DN6u#Da zSmyzVyNW1zi*^q(zPMuZ_37Lyc1=shZ0J{itwy7!W9joQ&pm|&X=XDysqde-1{2Nf zqfTuWjH!^cF6F=7TYStZ3FzdcS>1f3_PQ96h3`y!y@{58`Sirtp-zeXHmZPs$p|$q zx;e+~BypSQbMc^W^tR}%W=8*zm)~dIS2aOHkj;peU_it$Omk*l=K48a-s$O4Sv&fd zKFS+)yr0J#=Oq(j$DGZEZAoTNbfOqB zbvXc2pXcnvtOp`W!pW3t@9QIT_~L>bFTnjS`2}bau*k^at!gLqWfCNrIn}D`gy+8$ zGQLYDU>J=V=tylt(aigj&23FOTK)1pFJ(Mc;rf-lCuA!YX;@0-I~^0OU>l=ZBB&(2 z;7jAieCTiMS7Wh*P{2oXEO!ObxUINfFbUYxzc^AQFl-Xy1hcv-#5AO;QfH>Yoqd{Q zmU4r<6J$;V8)(w%IyFTe88TR-R9%-4ms;X`1VdrP0JnBZOc&ywrr`3;P4cns?Fl@tpw~8Z=1tW`S+=<>%x>A-8 z-!0$|A|)Ez_)=zAwYsk5YY#_0a9+}Ko@|Va* z)2Z2XT)3Mr$<@w)H3g!zDXJQ|AMU0}P$ks8&oU7TgzOK&Bl_a_+pf$+muzgS&ww~w zRQTxdHMTT{Np9Cn-+HYbkT;wU%Pb?f^C16Ise;XCW0M;V7*T^Oitm8fqz;?08P z?u3$#9X{1R48P;~vCbpMk0FYLmhpGXp^45EeNK~77gZbTAZ_Fq{3Phk?IO1|(U#+u zJeGD#@ot#Nx(oi=o}Ccc#BksZpo}LNT;IeT)_706{{s1EbPf#@?!LS|!gXF9W#4|u zOCnfu>7$;&Mz!tW3#|4CK6uTLCFV}Pj-H+=%%atc}+3i;eSiDb1*ein{QnHQCl))=l*VO{&rDiUHrLWXU3ct&ukFhLJI zUVgxtB3l=oC2>0_Y z1fi7tI^2D$HmVH;OWcCoX|Var6>hjI7z?>&Hxr46^Zu77mZvNM`YpGWFrh`2h&8Mh zp=5%4)z{*0gXB-pWov4;RURC_+)XKb=+1}dgk!|&q!K^2_n0#Z=f6%Q||x9 zI;{JcFmPH>SF}k`O$q=9_80fk2@M_Z!+j`$W6xCA^}ChvVCn++YwC_quY2;!!1EN5 zC6SdlxfbO{!#^n3g#6q%nF>!`dUP+`-0m!j?H=G>SUS^KhzKWP0yeMt%HWgX!QSt= z2$kL|LVOypwN=}GSLznL+Qm?>qf)`Z?)+a*T1d=)R;V!6&}X%-&^wgmiymqC7k`=C z*VHe2ZFP*Ggh>hO9;9zCvb?u$1oMeRFqy8Xp<|>O#UQ0ZzEL_MMu}m>tf9-pirM4u z^r0ZuQ%?2U)=Xrr!DAOkE%q$ST{T&jc1lE+Z%@qX^m9DyG?6i;C`Hp19hH`# zJGMZN`b*JC!%)WB9m~H1+6`y*zEEwT-ooD`i{8{! zB6$1FWB#oOTiu1O*zjqguGc3LhY_A_c{Vd^+NcQchWDtPkVEB03W;OI#5?(P67Ao_ z7aSa=fq0Oz;O{yI=n;R%LJ^7=1c>|dG=zfbyYS#in6mwvHQ=2~`^o+`l6K;)mF+|=%C-|5{)V6&4u6%i9&??zpUq9zf^jm;+crG4pROq}#k@am0dX;$b)I>?HsT&&m z+aYjk7!Ce*NczHs@kZ5TW8`?LTWHnm{tA;e6ehSKr*K2m8>k$bmc>(79gnu-5!na# zC9tkl5xw6@y_b?rjk)#&ShVopp6zDB(($s;9-C+?9kxF*6+&4yiKLqz0hYULLHo(Y z<~p%AL@^U+LAIJo$rCZp;a4cif6f{IkKs%gnfBtw`1oO+<0v%T&|2Ee?kL1byqrA&S>BbkBt2iZM~;HP<&W0Tu{ygWPf zm;^aSL&e87FF%JY$@28nf(MV(uSYQ%yArL>Zj+-fo6hK2rcB#eP83zj-`SurANH4c z58y}5Y4p`f_Z-Fx4?0i>v@PXXn5!V^LJ@09i#Mi37P;Vrl7M5v=})sCrUB?9Y23M9 zBIR350iflPK##S3ed<1^6PH@_Op-Y@ruPzx0)BMvXUwH58vw*0**NqgPM0uqH(%t3 zGVY$0FmTSDLQynWc!GN!#}+O{{ROk?x&R29Ay5$>_=n6wu)>}!kCqwqB>9m6{)-!7 z3Td>c{U~ypPptpk_QrY+m78YCpH5)IoxqNqVE>mB#wIXuLcp>U!v1yw;z-7i%lZcPxtO40XFhmA*fc4a4M)DFq(QKHq@KORMJhZ0VmF`F#M;W$|UJCN^I z5uWZ5g~Fg0g+W8{yXk*}nxo-H4x^6cY-{QDa*C7CHcVau87CIva91O)TFLI7qJE+E zax`2kJ_oIy@SFskG_Vh$()(HX=sRBvJypVev zA~E%4p$I$&0mG~;~L^Hu_q9R-rY^JLGwbrK*6$uA2(FJnySmj zp}tWkEwbn^e6kk9xZF%&fwy>5J8DR|#x&gki@4N9SR^C`zU<>7I&zx{&Vcdr>CTjB zn|M3d1q z3QXN}O*xdPVg=-lR)i#&{@ny7aDKJRBm;XHlM0Y{Md`pFK%M26AMg(uQ%$yFM^ga_ z$uMLIMr^0J3}4fEl@hvFvN0+&>r1&kC25=}2Ug-WmeU=`^L;;ZS`y{jcIa>JA}eC? zlE-M0N+IR?_~l-uPGp@z+uVG2FIeFsUrL<=C;cm9Q)dee0_ypEKB6~$^9d)%+`%cl z!Y2uL2+$0`qzy+<;JEk<(`poOYN*h?$Mo$u5iy4qAQoc9EN;IUynN|+)*3W|U+gGm zIdx=JKsQ?e*f5@jcXPRH(VM`Mo!R8|>HKG20A&tvsaal4eKM*;VQl|_*A(hLkSTT5f<}5tHeI=ZgDIKC+2xf~5k_CbWNt<}&>S z=V}pdszjuQq>XSEo6zB0Mr=g@zU+gZ!vM3H6$aN7D#C+u>52I3K+?eA-pB`ocWUps z0XTy!%-reCqi+Vk;Zdi*JkCxevX>1b@}8Zr4l(fJM>rH;%ua|g)o7;x89UohUxNzs zuMr3meU&=FBubqs^fjl2Z|{RTFOMU~J7_N+B>6tlii(SX5IK`}qvr29jSAcclH4lJ zpM_xs1J7??UgHILH9Dol{%MYWPs-)_i@E1=;{Oi+^M<0MRGf{>*}IZ-KYVfM{sP8i zlIkVdw*?yYk2|8W!Yc_QPBp%N{aSEtO84A zLY623hUA5kbYWrl_W*l0WH|?bO+*C8hxf&mp$g5C%4AC22a_OKAP_jS@4A5eto}8? zb-m1H;jJnVE*XjBU>T3;9SC8TpFNhH@RC3E1W@M-MTb#CjWr><=yOa%XZuABeLLqD zN_RArwj;dqu|d^C{wbtR5MuC3yOb@5<3mYeND%n-*z+_Q4&o$<#vRD7aq#)BRN3Ue z27mcPv)@YLFQ2H3<7>Bo&%Dz94dm;lQT-CuqyDj28(%^7)P#R{9P;Gu3ep!{FE{l0 zI}GVin2$+C$e4#pi4z+14~Y=-wA24ZrsZeVJNM@u26A{j;=J71I?D|tMwcK=QhPdh}{ux#WRc}#)F^P!?+TbuwZ zF)np^kA969#L10piH7-NciE)9VpB~$4=nPEVQ7bAhzgzkJV5o0zGDV{X66o^;x+&+Ak8o$lj@HbnxWOk;8c}i4 zWjvl5%gOa~tjJ-ymKAz&$gXIJvMy8%aeSUc6=cJX&@`b$ih9~B<&&yLT!?h}4566V zAx{dDtt90R)j#sj*9e;fkefunA@8H`%OjJCxJRn-Dx7PG@My*E?n~Y=IG?9!kj$zT zrID5NMOC?_3kqC z<8*oDz2PA>B~+S}WRS!+-Qhe10fk}h{CJE_)D|v~vupUKZ7#Z_$|<@E|L4t)S*Pmz zjPZ-Dw%lKgTN@A2PMc#O+Ct0l90V4 z%fdfESW}i8wb~8KV{b(O1+JbT2JHY2*v!#+L=;DlHiSs>@}BVhJt?|H*(6D&d%Dr@ z7k~iV!u?fDN|l9TtzuKt+M8?YfZFwo-~HD^sFbPnEpN~+KcK9@m%dKAVDj(qz%$;c z=6<+u0HD@4QON#u8BY&tF)&*We0>&k^!klDmJjCkG)K!$x!X%t35@)H-?sc5Z|c+} z$OgV2re29qw?a)mgU8B&ZtDkvcii*-=(CvZUfE%AOYR@Hoc!A@QTWpJf4hYjq%3Wy zKsZ*pAbP*SVNJP*WZwX=Ip5uVTTG2SdN$~!FkqZ`Y4(|G4~DNZ_JGd<1nqb!ZFdah z;-6ZI>!=H=mFmp4M&6R$G+QSQ5FN|CMo(K_DFe~k0j;9p z+V6fRXh*sX034WNjc;C*Uv8^ertlq=&DBn&^>EP2fXNGqPA5o%G0(D$J>ST1IcoN+ zCuJFLR+W>+VJ8b`t7l%Zr5qAc%uM1agN|=&eG>pLdKQ6rBEX8=X5vH&jeCDfYLR-K z8PWiMz0nP&_n-}UO*6yOt~t)G?<~MxF(j=(k;p29_AB`prRAvPK%lc5mAMJYcAE8zh zjxAx*uV36(DcP#oRGuEuXv+QG^)izQ(MtRx6-MOyvL)_gHa`;5m$lm1r(#VBQtM+GU*cJ>Rd9VViCFEAy@ zkx*9*h(S?ogo(jiQx?Q({Hid{5v#5z8KK(_CHlGSrUH@#b`0nuKf% z=y3myg*YD874@V_xEuo#zP0y11VEhIP~ngSh8TgXwcl<-oFAs*+)lll3ig8MvlD^~0%t!d{$z~&-q=;N`1B%NAO&L6C>Y`%@cTP+_b*6T#Y zu_H_PZ|5y>!zi8(B5n0&%DF#7Rb&T-38B)`1!pmw_>AG&Yof$GUp$jk5_xA3e&o_?I%wi;`~Fg|F)4op%#40{bh9|YJ0KcE$Ci#j6#qN~gl^xK;}s+g zjVMzoq#GMUry08jg`XAnOR_C@vHBy~`b42VKbB#j_j^(zS-g~Q8p_op!3ezXO$YT0 z)G{BX5l*jzeFNlVg6-{}>UHc^e9{dd4yH~kI%X$mK(zdezNck9=F>-hS?aHy~q4`va3HXtkuBIOR6{khxVKy**{AWq$5 zaco~j3VWA8N;M1vtobA3r|zm;&p8`zT|L_@tfL6N1#7KJjTieUlH`k zKTkIFVBvcLP^R(WDPQu8Fy#*?{yP1&4-^R>$6pc9?#JSxu?lF>&(IKnMxY#(L5t<5 zq@6p9Pqk6QH2a(3-~d1Hsr`l<5bKvfJ$*D78rQx>m-nsDv_JAV=#3gtJ<6XX+75X_ z&4f99eekEOYfeN1k%0D02R>Oh6q*)@mxc|f;8>~uR;_bG{)n`{9=$qHbS*(pZLRZB zBn_cH{`(4id4e<=A^DI#C17p$dsHo!qVw~Z+EZ*W82c41$`pPODk#IDQUL%Z9ptkrgr#v*P7(k~x zm>dqMC5kxd|7fVcK^!l!NAch+`K{vTp8u^``vl+%b{KB?Kp56-17Ddyg}FT2iZkYw zYw*qCaGe-^cE|PNowTxoJ}=3y~FsCz^}pZxb5~;dRM3t3JQ{qyEu4L zrye4xS&5ozAHMJLQc8Q37fGK71-5yKYp5IBSSZ!PCuPad9UnP zKJ&ctUwC#i{_IOjiADQqVZJ^si{_jsZoImpOA6*dU3~GRelYXSomqI|#qOfI`&j~4 z&HJ}ApetPP6+pt?i_u3d%e{|4vBiUjbx=3J>l^jlU}kp0@IcKvt>aE^vXePUzO(T> z3nf61(5HN`(_f*^sJMr*zIkQ2uV_E-rq*jQNjh(x+HM9d8bl1dS?K*omF(nW9^E#w@~{g1>FifpN!iylk3q`)OtD1`_^-Iqd3u1H#x+;tdUu2# z{Mkk$?Lu74Q=PZtw504j9kp!FF15>Y({eTwrXYxKL7Nn*S##l=_=t4V^_U{`hR^s%-iSYv)MfN|n&e)WE z;vH3;Pk-I!<60G$sNa2uFJ+pae*b2iPH0-XEbWsIZ3nvP2OfdmJ-rOH6u1$B3&HT0 z4Y*bee_4To0)FH%luPil8bkXJKMW`z;b#X9jqsP12<_nK|0nr>pZp(*lj5aS{4Qf{ TLE_n}C~{9Jo>53WX6*Yv*>ODs literal 0 HcmV?d00001 diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index ce5f6a0..8d143b1 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -71,6 +71,8 @@ class InteractiveMapsMarker extends StatefulWidget { Uint8List? markerIcon; Uint8List? markerIconSelected; + Uint8List? markerIconDark; + Uint8List? markerIconSelectedDark; @override InteractiveMapsMarkerState createState() { @@ -215,7 +217,7 @@ class InteractiveMapsMarkerState extends State { } } - /* Future getBytesFromAsset(String path, int width) async { + /* Future getBytesFromAsset(String path, int width) async { ByteData data = await rootBundle.load(path); ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width); @@ -230,12 +232,22 @@ class InteractiveMapsMarkerState extends State { int current = widget.items[index].id; Set _markers = Set(); - if (widget.markerIcon == null) + if (widget.markerIcon == null && + Theme.of(context).brightness != Brightness.dark) widget.markerIcon = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/marker.png', 100); - if (widget.markerIconSelected == null) + else + widget.markerIcon = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker_darkmode.png', 100); + + if (widget.markerIconSelected == null && + Theme.of(context).brightness != Brightness.dark) widget.markerIconSelected = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/marker_selected.png', 100); + else + widget.markerIconSelected = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/selectedMarker_darkmode.png', + 100); widget.items.forEach((item) async { _markers.add( Marker( @@ -256,7 +268,8 @@ class InteractiveMapsMarkerState extends State { : BitmapDescriptor.hueRed), // */ icon: item.id == current - ? BitmapDescriptor.fromBytes(widget.markerIconSelected as Uint8List) + ? BitmapDescriptor.fromBytes( + widget.markerIconSelected as Uint8List) : BitmapDescriptor.fromBytes(widget.markerIcon as Uint8List), ), ); From 95b44dc4e2030ccae80012ffa120ce5f27f030ce Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 16:27:16 +0100 Subject: [PATCH 07/71] fix remounted widget bug --- lib/interactive_maps_marker.dart | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 8d143b1..0d54e77 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -232,20 +232,17 @@ class InteractiveMapsMarkerState extends State { int current = widget.items[index].id; Set _markers = Set(); - if (widget.markerIcon == null && - Theme.of(context).brightness != Brightness.dark) + if (widget.markerIcon == null) widget.markerIcon = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/marker.png', 100); - else - widget.markerIcon = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/marker_darkmode.png', 100); - - if (widget.markerIconSelected == null && - Theme.of(context).brightness != Brightness.dark) + if (widget.markerIconSelected == null) widget.markerIconSelected = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/marker_selected.png', 100); - else - widget.markerIconSelected = await getBytesFromAsset( + if (widget.markerIconDark == null) + widget.markerIconDark = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker_darkmode.png', 100); + if (widget.markerIconSelectedDark == null) + widget.markerIconSelectedDark = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/selectedMarker_darkmode.png', 100); widget.items.forEach((item) async { @@ -269,8 +266,13 @@ class InteractiveMapsMarkerState extends State { // */ icon: item.id == current ? BitmapDescriptor.fromBytes( - widget.markerIconSelected as Uint8List) - : BitmapDescriptor.fromBytes(widget.markerIcon as Uint8List), + Theme.of(context).brightness != Brightness.dark + ? widget.markerIconSelected as Uint8List + : widget.markerIconSelectedDark as Uint8List) + : BitmapDescriptor.fromBytes( + Theme.of(context).brightness != Brightness.dark + ? widget.markerIcon as Uint8List + : widget.markerIconDark as Uint8List), ), ); }); From 4b7c2fc77f339301b495cbfeecccec4f82b54607 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 16:50:08 +0100 Subject: [PATCH 08/71] Try to center camera on current position --- lib/interactive_maps_marker.dart | 18 +- pubspec.lock | 346 +++++++++++++++++++++++++++++++ pubspec.yaml | 1 + 3 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 pubspec.lock diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 0d54e77..cbaf787 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -9,6 +9,7 @@ import 'package:flutter/services.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:interactive_maps_marker/interactive_maps_controller.dart'; export 'package:interactive_maps_marker/interactive_maps_controller.dart'; +import 'package:geolocator/geolocator.dart'; import './utils.dart'; @@ -88,6 +89,7 @@ class InteractiveMapsMarkerState extends State { Completer _controller = Completer(); GoogleMapController? mapController; PageController pageController = PageController(viewportFraction: 0.9); + LatLng? _initialPosition; Set markers = {}; int currentIndex = 0; @@ -95,6 +97,8 @@ class InteractiveMapsMarkerState extends State { @override void initState() { + _getUserLocation(); + rebuildMarkers(currentIndex); super.initState(); } @@ -105,6 +109,16 @@ class InteractiveMapsMarkerState extends State { super.didChangeDependencies(); } + void _getUserLocation() async { + var position = await GeolocatorPlatform.instance.getCurrentPosition( + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.bestForNavigation)); + + setState(() { + _initialPosition = LatLng(position.latitude, position.longitude); + }); + } + @override Widget build(BuildContext context) { return StreamBuilder( @@ -158,7 +172,9 @@ class InteractiveMapsMarkerState extends State { ); }, initialCameraPosition: CameraPosition( - target: widget.center, + target: _initialPosition != null + ? _initialPosition as LatLng + : widget.center, zoom: widget.zoom, ), ); diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..6a8b314 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,346 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" + source: hosted + version: "1.17.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + url: "https://pub.dev" + source: hosted + version: "2.0.16" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: "9f1c9a70dd25fc9d9574ff17ba714cf3bc7894258efd7d1ce0debfafe2f8e1d8" + url: "https://pub.dev" + source: hosted + version: "10.0.1" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: db75aacf93fcdc2c0ba20066019da30e3fde7b102cf579d4bd4ea542e8a8cf17 + url: "https://pub.dev" + source: hosted + version: "4.2.4" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: "36527c555f4c425f7d8fa8c7c07d67b78e3ff7590d40448051959e1860c1cfb4" + url: "https://pub.dev" + source: hosted + version: "2.2.7" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: b2a0c09d2cfe8ad6a8cc44fada5c1092c49286e8c30922914dfe2b23799a682f + url: "https://pub.dev" + source: hosted + version: "4.0.8" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: f68a122da48fcfff68bbc9846bb0b74ef651afe84a1b1f6ec20939de4d6860e1 + url: "https://pub.dev" + source: hosted + version: "2.1.6" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "463045515b08bd83f73e014359c4ad063b902eb3899952cfb784497ae6c6583b" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + google_maps: + dependency: transitive + description: + name: google_maps + sha256: "555d5d736339b0478e821167ac521c810d7b51c3b2734e6802a9f046b64ea37a" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + google_maps_flutter: + dependency: "direct main" + description: + name: google_maps_flutter + sha256: d4914cb38b3dcb62c39c085d968d434de0f8050f00f4d9f5ba4a7c7e004934cb + url: "https://pub.dev" + source: hosted + version: "2.5.0" + google_maps_flutter_android: + dependency: transitive + description: + name: google_maps_flutter_android + sha256: e6cb018169e49332f88d23b1d2119b09e8ab4e7d3a1b889a1b7b3fd113e034ba + url: "https://pub.dev" + source: hosted + version: "2.5.1" + google_maps_flutter_ios: + dependency: transitive + description: + name: google_maps_flutter_ios + sha256: "2a595c9789070786c654e9772ec0d1bb759ae37d2dd776291af5398531274e06" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + google_maps_flutter_platform_interface: + dependency: transitive + description: + name: google_maps_flutter_platform_interface + sha256: a3e9e6896501e566d902c6c69f010834d410ef4b7b5c18b90c77e871c86b7907 + url: "https://pub.dev" + source: hosted + version: "2.4.1" + google_maps_flutter_web: + dependency: transitive + description: + name: google_maps_flutter_web + sha256: "05067c5aa762ebee44b7ef4902a311ed8cf891ef655e2798bae063aa3050c8d9" + url: "https://pub.dev" + source: hosted + version: "0.5.4+1" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + js_wrapping: + dependency: transitive + description: + name: js_wrapping + sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c + url: "https://pub.dev" + source: hosted + version: "0.7.4" + matcher: + dependency: transitive + description: + name: matcher + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" + source: hosted + version: "0.12.15" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + url: "https://pub.dev" + source: hosted + version: "2.1.6" + sanitize_html: + dependency: transitive + description: + name: sanitize_html + sha256: "0a445f19bbaa196f5a4f93461aa066b94e6e025622eb1e9bc77872a5e25233a5" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" + source: hosted + version: "0.5.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" +sdks: + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index fd97c81..5e42796 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: flutter: sdk: flutter + geolocator: ^10.0.1 google_maps_flutter: ^2.2.5 dev_dependencies: From 165146d97a351ed7468629ddeb63a7fb84c79970 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 17:16:48 +0100 Subject: [PATCH 09/71] add request permission for position --- lib/interactive_maps_marker.dart | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index cbaf787..4169f45 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -108,8 +108,38 @@ class InteractiveMapsMarkerState extends State { rebuildMarkers(currentIndex); super.didChangeDependencies(); } + Future _handleLocationPermission() async { + bool serviceEnabled; + LocationPermission permission; + serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + 'Location services are disabled. Please enable the services'))); + return false; + } + permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Location permissions are denied'))); + return false; + } + } + if (permission == LocationPermission.deniedForever) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + 'Location permissions are permanently denied, we cannot request permissions.'))); + return false; + } + return true; + } void _getUserLocation() async { + final hasPermission = await _handleLocationPermission(); + + if (!hasPermission) return; var position = await GeolocatorPlatform.instance.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.bestForNavigation)); From ae35b74c2e3f8010ee1e9f4586e153346312b11e Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 17:24:28 +0100 Subject: [PATCH 10/71] try to use directly initial position instead of ternary operator --- lib/interactive_maps_marker.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 4169f45..b1389ea 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -108,7 +108,8 @@ class InteractiveMapsMarkerState extends State { rebuildMarkers(currentIndex); super.didChangeDependencies(); } - Future _handleLocationPermission() async { + + Future _handleLocationPermission() async { bool serviceEnabled; LocationPermission permission; @@ -136,6 +137,7 @@ class InteractiveMapsMarkerState extends State { } return true; } + void _getUserLocation() async { final hasPermission = await _handleLocationPermission(); @@ -202,9 +204,7 @@ class InteractiveMapsMarkerState extends State { ); }, initialCameraPosition: CameraPosition( - target: _initialPosition != null - ? _initialPosition as LatLng - : widget.center, + target: _initialPosition as LatLng, zoom: widget.zoom, ), ); From dce9daf20a5dd3aadac8d7c4d9b0796f596a6e90 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 15 Sep 2023 17:40:48 +0100 Subject: [PATCH 11/71] second try to use directly initial position instead of ternary operator --- lib/interactive_maps_marker.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index b1389ea..d3c7b60 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -158,7 +158,18 @@ class InteractiveMapsMarkerState extends State { builder: (context, snapshot) { return Stack( children: [ - _buildMap(), + _initialPosition != null + ? _buildMap() + : Container( + child: Center( + child: Text( + 'loading map..', + style: TextStyle( + fontFamily: 'Avenir-Medium', + color: Colors.grey[400]), + ), + ), + ), Align( alignment: widget.contentAlignment, child: Padding( From bf0e1979aa7bc3ca6498a452b2b38c5ee67cfcf2 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 21:20:50 +0100 Subject: [PATCH 12/71] stream value try to fix --- lib/interactive_maps_marker.dart | 1 + pubspec.lock | 8 ++++++++ pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index d3c7b60..83a798c 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -154,6 +154,7 @@ class InteractiveMapsMarkerState extends State { @override Widget build(BuildContext context) { return StreamBuilder( + stream: null, initialData: 0, builder: (context, snapshot) { return Stack( diff --git a/pubspec.lock b/pubspec.lock index 6a8b314..72b0358 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + fluster: + dependency: "direct main" + description: + name: fluster + sha256: "3807f5d088b7798f0416b8578498046338af98bb4fb922a70e2810b8293963f6" + url: "https://pub.dev" + source: hosted + version: "1.2.0" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 5e42796..10966c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter geolocator: ^10.0.1 google_maps_flutter: ^2.2.5 - + fluster: any dev_dependencies: flutter_test: sdk: flutter From ea340d43164e3cce1f5f34066b19c8a0509fdc69 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 22:44:29 +0100 Subject: [PATCH 13/71] try clustering markers --- lib/interactive_maps_marker.dart | 89 +++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 83a798c..3254555 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -3,6 +3,7 @@ library interactive_maps_marker; // interactive_marker_list import 'dart:async'; import 'dart:ui' as ui; +import 'package:fluster/fluster.dart'; import "package:flutter/material.dart"; import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; @@ -13,6 +14,37 @@ import 'package:geolocator/geolocator.dart'; import './utils.dart'; +class MapMarker extends Clusterable { + final String id; + final LatLng position; + final BitmapDescriptor icon; + MapMarker({ + required this.id, + required this.position, + required this.icon, + isCluster = false, + clusterId, + pointsSize, + childMarkerId, + }) : super( + markerId: id, + latitude: position.latitude, + longitude: position.longitude, + isCluster: isCluster, + clusterId: clusterId, + pointsSize: pointsSize, + childMarkerId: childMarkerId, + ); + Marker toMarker() => Marker( + markerId: MarkerId(id), + position: LatLng( + position.latitude, + position.longitude, + ), + icon: icon, + ); +} + class MarkerItem { int id; double latitude; @@ -90,15 +122,56 @@ class InteractiveMapsMarkerState extends State { GoogleMapController? mapController; PageController pageController = PageController(viewportFraction: 0.9); LatLng? _initialPosition; - - Set markers = {}; + final List markers = []; + final List markerLocations = [ + LatLng(41.147125, -8.611249), + LatLng(41.145599, -8.610691), + ]; + late List googleMarkers; +/* Set markers = {}; + */ int currentIndex = 0; ValueNotifier selectedMarker = ValueNotifier(0); @override void initState() { _getUserLocation(); - + for (LatLng markerLocation in markerLocations) { + markers.add( + MapMarker( + id: markerLocations.indexOf(markerLocation).toString(), + position: markerLocation, + icon: BitmapDescriptor.hueGreen as BitmapDescriptor, + ), + ); + } + final Fluster fluster = Fluster( + minZoom: 7, // The min zoom at clusters will show + maxZoom: 15, // The max zoom at clusters will show + radius: 150, // Cluster radius in pixels + extent: 2048, // Tile extent. Radius is calculated with it. + nodeSize: 64, // Size of the KD-tree leaf node. + points: markers, // The list of markers created before + createCluster: ( + // Create cluster marker + BaseCluster cluster, + double lng, + double lat, + ) => + MapMarker( + id: cluster.id.toString(), + position: LatLng(lat, lng), + icon: BitmapDescriptor.hueRed as BitmapDescriptor, + isCluster: cluster.isCluster, + clusterId: cluster.id, + pointsSize: cluster.pointsSize, + childMarkerId: cluster.childMarkerId, + ), + ); + final List googleMarkers = fluster + .clusters([-180, -85, 180, 85], 10) + .map((cluster) => cluster.toMarker()) + .toList(); rebuildMarkers(currentIndex); super.initState(); } @@ -153,7 +226,7 @@ class InteractiveMapsMarkerState extends State { @override Widget build(BuildContext context) { - return StreamBuilder( + return StreamBuilder( stream: null, initialData: 0, builder: (context, snapshot) { @@ -202,7 +275,7 @@ class InteractiveMapsMarkerState extends State { print('Values changed'); return GoogleMap( zoomControlsEnabled: false, - markers: markers, + markers: googleMarkers.toSet(), myLocationEnabled: true, myLocationButtonEnabled: false, onMapCreated: (GoogleMapController controller) async { @@ -255,7 +328,7 @@ class InteractiveMapsMarkerState extends State { widget.onLastItem!(); } rebuildMarkers(index); - Marker marker = markers.elementAt(index); + Marker marker = markers.elementAt(index).toMarker(); mapController ?.animateCamera( @@ -303,7 +376,7 @@ class InteractiveMapsMarkerState extends State { widget.markerIconSelectedDark = await getBytesFromAsset( 'packages/interactive_maps_marker/assets/selectedMarker_darkmode.png', 100); - widget.items.forEach((item) async { +/* widget.items.forEach((item) async { _markers.add( Marker( markerId: MarkerId(item.id.toString()), @@ -337,7 +410,7 @@ class InteractiveMapsMarkerState extends State { setState(() { markers = _markers; - }); + }); */ // selectedMarker.value = current; selectedMarker.value = current; // selectedMarker.notifyListeners(); From e944a62209af139e76d841cc0be389ea7a1dd8f8 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 22:52:47 +0100 Subject: [PATCH 14/71] fix colors hue --- lib/interactive_maps_marker.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 3254555..4b7762d 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -141,7 +141,7 @@ class InteractiveMapsMarkerState extends State { MapMarker( id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, - icon: BitmapDescriptor.hueGreen as BitmapDescriptor, + icon: BitmapDescriptor.defaultMarker , ), ); } @@ -161,7 +161,7 @@ class InteractiveMapsMarkerState extends State { MapMarker( id: cluster.id.toString(), position: LatLng(lat, lng), - icon: BitmapDescriptor.hueRed as BitmapDescriptor, + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), isCluster: cluster.isCluster, clusterId: cluster.id, pointsSize: cluster.pointsSize, From d583ad1e58c9030b75df9dc79e25acb561c69d6d Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 23:04:52 +0100 Subject: [PATCH 15/71] try to cluster still --- lib/interactive_maps_marker.dart | 58 +++++++++++++++++--------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 4b7762d..bc15802 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -122,7 +122,7 @@ class InteractiveMapsMarkerState extends State { GoogleMapController? mapController; PageController pageController = PageController(viewportFraction: 0.9); LatLng? _initialPosition; - final List markers = []; + final List markers = []; final List markerLocations = [ LatLng(41.147125, -8.611249), LatLng(41.145599, -8.610691), @@ -141,37 +141,39 @@ class InteractiveMapsMarkerState extends State { MapMarker( id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, - icon: BitmapDescriptor.defaultMarker , + icon: BitmapDescriptor.defaultMarker, ), ); } final Fluster fluster = Fluster( - minZoom: 7, // The min zoom at clusters will show - maxZoom: 15, // The max zoom at clusters will show - radius: 150, // Cluster radius in pixels - extent: 2048, // Tile extent. Radius is calculated with it. - nodeSize: 64, // Size of the KD-tree leaf node. - points: markers, // The list of markers created before - createCluster: ( - // Create cluster marker - BaseCluster cluster, - double lng, - double lat, - ) => - MapMarker( - id: cluster.id.toString(), - position: LatLng(lat, lng), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), - isCluster: cluster.isCluster, - clusterId: cluster.id, - pointsSize: cluster.pointsSize, - childMarkerId: cluster.childMarkerId, - ), - ); + minZoom: 10, + maxZoom: 15, + radius: 150, + extent: 4096, + nodeSize: 64, + points: markers, + createCluster: + (BaseCluster? cluster, double? longitude, double? latitude) { + if (cluster == null) { + return MapMarker( + id: "", + position: LatLng(0, 0), + icon: BitmapDescriptor.defaultMarker); + } + return MapMarker( + id: cluster.id.toString(), + position: LatLng(latitude!, longitude!), + icon: BitmapDescriptor.defaultMarker, + isCluster: cluster.isCluster, + clusterId: cluster.id, + pointsSize: cluster.pointsSize, + childMarkerId: cluster.childMarkerId, + ); + }); final List googleMarkers = fluster - .clusters([-180, -85, 180, 85], 10) - .map((cluster) => cluster.toMarker()) - .toList(); + .clusters([-180, -85, 180, 85], 10) + .map((cluster) => cluster.toMarker()) + .toList(); rebuildMarkers(currentIndex); super.initState(); } @@ -275,7 +277,7 @@ class InteractiveMapsMarkerState extends State { print('Values changed'); return GoogleMap( zoomControlsEnabled: false, - markers: googleMarkers.toSet(), + markers: googleMarkers.toSet(), myLocationEnabled: true, myLocationButtonEnabled: false, onMapCreated: (GoogleMapController controller) async { From ce82946757c7eeb269132e42f8dd11a051322dce Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 23:11:35 +0100 Subject: [PATCH 16/71] try to cluster still 2 --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index bc15802..fc7b268 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -127,7 +127,7 @@ class InteractiveMapsMarkerState extends State { LatLng(41.147125, -8.611249), LatLng(41.145599, -8.610691), ]; - late List googleMarkers; + List googleMarkers = []; /* Set markers = {}; */ int currentIndex = 0; From 3b67725f787a64eca5df010e9c342c5a1ea0bc0c Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 23:21:58 +0100 Subject: [PATCH 17/71] try to cluster still 3 --- lib/interactive_maps_marker.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index fc7b268..894655a 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -141,7 +141,7 @@ class InteractiveMapsMarkerState extends State { MapMarker( id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, - icon: BitmapDescriptor.defaultMarker, + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), ), ); } @@ -158,12 +158,12 @@ class InteractiveMapsMarkerState extends State { return MapMarker( id: "", position: LatLng(0, 0), - icon: BitmapDescriptor.defaultMarker); + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue)); } return MapMarker( id: cluster.id.toString(), position: LatLng(latitude!, longitude!), - icon: BitmapDescriptor.defaultMarker, + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), isCluster: cluster.isCluster, clusterId: cluster.id, pointsSize: cluster.pointsSize, From f1e2b476c979549ee7c079fd4c8febdc08e3b136 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 23:35:20 +0100 Subject: [PATCH 18/71] try to cluster still 4 --- example/lib/main.dart | 9 ----- example/lib/stateful_example.dart | 60 ------------------------------- lib/interactive_maps_marker.dart | 9 ++--- 3 files changed, 2 insertions(+), 76 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 30273ed..88916a8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -44,15 +44,6 @@ class MyHomePage extends StatelessWidget { ); }, ), - ElevatedButton( - child: Text('Stateful Widget Example'), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => StatefulExample()), - ); - }, - ), ElevatedButton( child: Text('Advanced Usage'), onPressed: () { diff --git a/example/lib/stateful_example.dart b/example/lib/stateful_example.dart index ad138a3..e69de29 100644 --- a/example/lib/stateful_example.dart +++ b/example/lib/stateful_example.dart @@ -1,60 +0,0 @@ -import 'package:example/simple_usage.dart'; -import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:interactive_maps_marker/interactive_maps_marker.dart'; - -class StatefulExample extends StatefulWidget { - @override - _StatefulExampleState createState() => _StatefulExampleState(); -} - -class _StatefulExampleState extends State { - List markers = []; - InteractiveMapsController controller = InteractiveMapsController(); - - @override - void initState() { - super.initState(); -// Fake delay for simulating a network request - Future.delayed(Duration(seconds: 2)).then((value) { - setState(() { - markers.add(MarkerItem(id: 1, latitude: 31.4673274, longitude: 74.2637687)); - markers.add(MarkerItem(id: 2, latitude: 31.4718461, longitude: 74.3531591)); - controller.reset(index: 0); - }); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Stateful Usage'), - actions: [ - IconButton( - icon: Icon(Icons.add), - onPressed: () { - setState(() { - markers.add(MarkerItem(id: 3, latitude: 31.5325107, longitude: 74.3610325)); - markers.add(MarkerItem(id: 4, latitude: 31.4668809, longitude: 74.31354)); - controller.reset(index: 0); - }); - }, - ) - ], - ), - body: InteractiveMapsMarker( - items: markers, - controller: controller, - center: LatLng(31.4906504, 74.319872), - itemContent: (context, index) { - MarkerItem item = markers[index]; - return BottomTile(item: item); - }, - onLastItem: () { - print('Last Item'); - }, - ), - ); - } -} diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 894655a..d4914f4 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -154,14 +154,9 @@ class InteractiveMapsMarkerState extends State { points: markers, createCluster: (BaseCluster? cluster, double? longitude, double? latitude) { - if (cluster == null) { - return MapMarker( - id: "", - position: LatLng(0, 0), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue)); - } + return MapMarker( - id: cluster.id.toString(), + id: cluster!.id.toString(), position: LatLng(latitude!, longitude!), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), isCluster: cluster.isCluster, From 6af89023a41f875e961a07939466a3cb1878c18c Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 23:43:48 +0100 Subject: [PATCH 19/71] try to cluster still 5 --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index d4914f4..570ec15 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -165,7 +165,7 @@ class InteractiveMapsMarkerState extends State { childMarkerId: cluster.childMarkerId, ); }); - final List googleMarkers = fluster + googleMarkers = fluster .clusters([-180, -85, 180, 85], 10) .map((cluster) => cluster.toMarker()) .toList(); From aed4fa7add4a3491225b2d571e3293a26ec9008b Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 19 Sep 2023 23:53:41 +0100 Subject: [PATCH 20/71] try to cluster still 6 --- lib/interactive_maps_marker.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 570ec15..5c4a94f 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -124,8 +124,11 @@ class InteractiveMapsMarkerState extends State { LatLng? _initialPosition; final List markers = []; final List markerLocations = [ - LatLng(41.147125, -8.611249), - LatLng(41.145599, -8.610691), + LatLng(40.729053, -73.987142), + LatLng(40.732130, -73.983891), + LatLng(40.732327, -73.984414), + LatLng(40.735525, -73.992725), + ]; List googleMarkers = []; /* Set markers = {}; @@ -146,8 +149,8 @@ class InteractiveMapsMarkerState extends State { ); } final Fluster fluster = Fluster( - minZoom: 10, - maxZoom: 15, + minZoom: 0, + maxZoom: 21, radius: 150, extent: 4096, nodeSize: 64, @@ -166,7 +169,7 @@ class InteractiveMapsMarkerState extends State { ); }); googleMarkers = fluster - .clusters([-180, -85, 180, 85], 10) + .clusters([-180, -85, 180, 85], 12) .map((cluster) => cluster.toMarker()) .toList(); rebuildMarkers(currentIndex); From 925d2a82cde15e90737344b1c9735be4a88fec76 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:05:04 +0100 Subject: [PATCH 21/71] try to cluster still 7 --- lib/interactive_maps_marker.dart | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 5c4a94f..31ad683 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -124,11 +124,9 @@ class InteractiveMapsMarkerState extends State { LatLng? _initialPosition; final List markers = []; final List markerLocations = [ - LatLng(40.729053, -73.987142), - LatLng(40.732130, -73.983891), - LatLng(40.732327, -73.984414), - LatLng(40.735525, -73.992725), - + LatLng(36.837446, 10.177410), + LatLng(36.813458, 10.133916), + LatLng(36.860832, 10.253826), ]; List googleMarkers = []; /* Set markers = {}; @@ -144,7 +142,10 @@ class InteractiveMapsMarkerState extends State { MapMarker( id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), + icon: BitmapDescriptor.fromBytes( + Theme.of(context).brightness != Brightness.dark + ? widget.markerIconSelected as Uint8List + : widget.markerIconSelectedDark as Uint8List), ), ); } @@ -157,18 +158,20 @@ class InteractiveMapsMarkerState extends State { points: markers, createCluster: (BaseCluster? cluster, double? longitude, double? latitude) { - return MapMarker( id: cluster!.id.toString(), position: LatLng(latitude!, longitude!), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange), + icon: BitmapDescriptor.fromBytes( + Theme.of(context).brightness != Brightness.dark + ? widget.markerIcon as Uint8List + : widget.markerIconDark as Uint8List), isCluster: cluster.isCluster, clusterId: cluster.id, pointsSize: cluster.pointsSize, childMarkerId: cluster.childMarkerId, ); }); - googleMarkers = fluster + googleMarkers = fluster .clusters([-180, -85, 180, 85], 12) .map((cluster) => cluster.toMarker()) .toList(); From 7f2866f01b235c6cbdeca9841a01ac2bfda6cc59 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:14:00 +0100 Subject: [PATCH 22/71] try to cluster still 8 --- lib/interactive_maps_marker.dart | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 31ad683..bed2515 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -143,12 +143,19 @@ class InteractiveMapsMarkerState extends State { id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, icon: BitmapDescriptor.fromBytes( - Theme.of(context).brightness != Brightness.dark - ? widget.markerIconSelected as Uint8List - : widget.markerIconSelectedDark as Uint8List), + Theme.of(context).brightness != Brightness.dark + ? widget.markerIconSelected as Uint8List + : widget.markerIconSelectedDark as Uint8List), ), ); } + + rebuildMarkers(currentIndex); + super.initState(); + } + + @override + void didChangeDependencies() { final Fluster fluster = Fluster( minZoom: 0, maxZoom: 21, @@ -162,9 +169,9 @@ class InteractiveMapsMarkerState extends State { id: cluster!.id.toString(), position: LatLng(latitude!, longitude!), icon: BitmapDescriptor.fromBytes( - Theme.of(context).brightness != Brightness.dark - ? widget.markerIcon as Uint8List - : widget.markerIconDark as Uint8List), + Theme.of(context).brightness != Brightness.dark + ? widget.markerIcon as Uint8List + : widget.markerIconDark as Uint8List), isCluster: cluster.isCluster, clusterId: cluster.id, pointsSize: cluster.pointsSize, @@ -176,12 +183,6 @@ class InteractiveMapsMarkerState extends State { .map((cluster) => cluster.toMarker()) .toList(); rebuildMarkers(currentIndex); - super.initState(); - } - - @override - void didChangeDependencies() { - rebuildMarkers(currentIndex); super.didChangeDependencies(); } From f74841b916358900dc10391e284f280a13a99e89 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:24:42 +0100 Subject: [PATCH 23/71] try to cluster still 9 --- lib/interactive_maps_marker.dart | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index bed2515..271c09b 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -143,19 +143,10 @@ class InteractiveMapsMarkerState extends State { id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, icon: BitmapDescriptor.fromBytes( - Theme.of(context).brightness != Brightness.dark - ? widget.markerIconSelected as Uint8List - : widget.markerIconSelectedDark as Uint8List), + widget.markerIconSelected as Uint8List), ), ); } - - rebuildMarkers(currentIndex); - super.initState(); - } - - @override - void didChangeDependencies() { final Fluster fluster = Fluster( minZoom: 0, maxZoom: 21, @@ -168,10 +159,7 @@ class InteractiveMapsMarkerState extends State { return MapMarker( id: cluster!.id.toString(), position: LatLng(latitude!, longitude!), - icon: BitmapDescriptor.fromBytes( - Theme.of(context).brightness != Brightness.dark - ? widget.markerIcon as Uint8List - : widget.markerIconDark as Uint8List), + icon: BitmapDescriptor.fromBytes(widget.markerIcon as Uint8List), isCluster: cluster.isCluster, clusterId: cluster.id, pointsSize: cluster.pointsSize, @@ -183,6 +171,12 @@ class InteractiveMapsMarkerState extends State { .map((cluster) => cluster.toMarker()) .toList(); rebuildMarkers(currentIndex); + super.initState(); + } + + @override + void didChangeDependencies() { + rebuildMarkers(currentIndex); super.didChangeDependencies(); } From 11643dd35849409852b3a3dce91d673e32f52e59 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:29:54 +0100 Subject: [PATCH 24/71] try to cluster still 10 --- lib/interactive_maps_marker.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 271c09b..5a0a350 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -137,6 +137,8 @@ class InteractiveMapsMarkerState extends State { @override void initState() { _getUserLocation(); + rebuildMarkers(currentIndex); + for (LatLng markerLocation in markerLocations) { markers.add( MapMarker( @@ -170,7 +172,6 @@ class InteractiveMapsMarkerState extends State { .clusters([-180, -85, 180, 85], 12) .map((cluster) => cluster.toMarker()) .toList(); - rebuildMarkers(currentIndex); super.initState(); } From fdd7a75498d16be82f6460a31ae52a2445fcf9a0 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:35:04 +0100 Subject: [PATCH 25/71] try to cluster still 11 --- lib/interactive_maps_marker.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 5a0a350..c510596 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -135,9 +135,9 @@ class InteractiveMapsMarkerState extends State { ValueNotifier selectedMarker = ValueNotifier(0); @override - void initState() { + void initState() async { _getUserLocation(); - rebuildMarkers(currentIndex); + await rebuildMarkers(currentIndex); for (LatLng markerLocation in markerLocations) { markers.add( From bce9f826a0b0937c015383734a9cbfbdb2c664ec Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:40:35 +0100 Subject: [PATCH 26/71] try to cluster still 12 change to images --- lib/interactive_maps_marker.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index c510596..3f5ea97 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -135,17 +135,14 @@ class InteractiveMapsMarkerState extends State { ValueNotifier selectedMarker = ValueNotifier(0); @override - void initState() async { + void initState() { _getUserLocation(); - await rebuildMarkers(currentIndex); - for (LatLng markerLocation in markerLocations) { markers.add( MapMarker( id: markerLocations.indexOf(markerLocation).toString(), position: markerLocation, - icon: BitmapDescriptor.fromBytes( - widget.markerIconSelected as Uint8List), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), ), ); } @@ -161,7 +158,8 @@ class InteractiveMapsMarkerState extends State { return MapMarker( id: cluster!.id.toString(), position: LatLng(latitude!, longitude!), - icon: BitmapDescriptor.fromBytes(widget.markerIcon as Uint8List), + icon: BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueOrange), isCluster: cluster.isCluster, clusterId: cluster.id, pointsSize: cluster.pointsSize, @@ -172,6 +170,7 @@ class InteractiveMapsMarkerState extends State { .clusters([-180, -85, 180, 85], 12) .map((cluster) => cluster.toMarker()) .toList(); + rebuildMarkers(currentIndex); super.initState(); } From 0de1b8724107fc19d87b87b1b81de34521e7ffe0 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 00:59:56 +0100 Subject: [PATCH 27/71] try to cluster still 13 change to images --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 3f5ea97..8ba77c5 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -167,7 +167,7 @@ class InteractiveMapsMarkerState extends State { ); }); googleMarkers = fluster - .clusters([-180, -85, 180, 85], 12) + .clusters([7.597046,35.726447,12.716675,37.874853], 12) .map((cluster) => cluster.toMarker()) .toList(); rebuildMarkers(currentIndex); From c09680060161351097debb533c1ff6e61ceb5931 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 01:11:21 +0100 Subject: [PATCH 28/71] try to cluster still 14 change to images --- lib/interactive_maps_marker.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 8ba77c5..f601466 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -149,9 +149,9 @@ class InteractiveMapsMarkerState extends State { final Fluster fluster = Fluster( minZoom: 0, maxZoom: 21, - radius: 150, - extent: 4096, - nodeSize: 64, + radius: 150 ~/ 2, + extent: 2048, + nodeSize: 32, points: markers, createCluster: (BaseCluster? cluster, double? longitude, double? latitude) { From 2220db8db0a7f803b6ab1be47aad9c57249772e5 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 15:15:40 +0100 Subject: [PATCH 29/71] try to cluster still 15 change to images --- lib/helpers/map_helper.dart | 168 +++++++++++++++++++++++++++++ lib/helpers/map_marker.dart | 41 ++++++++ lib/interactive_maps_marker.dart | 175 ++++++++++++++++++------------- 3 files changed, 314 insertions(+), 70 deletions(-) create mode 100644 lib/helpers/map_helper.dart create mode 100644 lib/helpers/map_marker.dart diff --git a/lib/helpers/map_helper.dart b/lib/helpers/map_helper.dart new file mode 100644 index 0000000..395b86a --- /dev/null +++ b/lib/helpers/map_helper.dart @@ -0,0 +1,168 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:fluster/fluster.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:flutter_google_maps_clusters/helpers/map_marker.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +/// In here we are encapsulating all the logic required to get marker icons from url images +/// and to show clusters using the [Fluster] package. +class MapHelper { + /// If there is a cached file and it's not old returns the cached marker image file + /// else it will download the image and save it on the temp dir and return that file. + /// + /// This mechanism is possible using the [DefaultCacheManager] package and is useful + /// to improve load times on the next map loads, the first time will always take more + /// time to download the file and set the marker image. + /// + /// You can resize the marker image by providing a [targetWidth]. + static Future getMarkerImageFromUrl( + String url, { + int? targetWidth, + }) async { + final File markerImageFile = await DefaultCacheManager().getSingleFile(url); + + Uint8List markerImageBytes = await markerImageFile.readAsBytes(); + + if (targetWidth != null) { + markerImageBytes = await _resizeImageBytes( + markerImageBytes, + targetWidth, + ); + } + + return BitmapDescriptor.fromBytes(markerImageBytes); + } + + /// Draw a [clusterColor] circle with the [clusterSize] text inside that is [width] wide. + /// + /// Then it will convert the canvas to an image and generate the [BitmapDescriptor] + /// to be used on the cluster marker icons. + static Future _getClusterMarker( + int clusterSize, + Color clusterColor, + Color textColor, + int width, + ) async { + final PictureRecorder pictureRecorder = PictureRecorder(); + final Canvas canvas = Canvas(pictureRecorder); + final Paint paint = Paint()..color = clusterColor; + final TextPainter textPainter = TextPainter( + textDirection: TextDirection.ltr, + ); + + final double radius = width / 2; + + canvas.drawCircle( + Offset(radius, radius), + radius, + paint, + ); + + textPainter.text = TextSpan( + text: clusterSize.toString(), + style: TextStyle( + fontSize: radius - 5, + fontWeight: FontWeight.bold, + color: textColor, + ), + ); + + textPainter.layout(); + textPainter.paint( + canvas, + Offset(radius - textPainter.width / 2, radius - textPainter.height / 2), + ); + + final image = await pictureRecorder.endRecording().toImage( + radius.toInt() * 2, + radius.toInt() * 2, + ); + final data = await image.toByteData(format: ImageByteFormat.png); + + return BitmapDescriptor.fromBytes(data!.buffer.asUint8List()); + } + + /// Resizes the given [imageBytes] with the [targetWidth]. + /// + /// We don't want the marker image to be too big so we might need to resize the image. + static Future _resizeImageBytes( + Uint8List imageBytes, + int targetWidth, + ) async { + final Codec imageCodec = await instantiateImageCodec( + imageBytes, + targetWidth: targetWidth, + ); + + final FrameInfo frameInfo = await imageCodec.getNextFrame(); + + final data = await frameInfo.image.toByteData(format: ImageByteFormat.png); + + return data!.buffer.asUint8List(); + } + + /// Inits the cluster manager with all the [MapMarker] to be displayed on the map. + /// Here we're also setting up the cluster marker itself, also with an [clusterImageUrl]. + /// + /// For more info about customizing your clustering logic check the [Fluster] constructor. + static Future> initClusterManager( + List markers, + int minZoom, + int maxZoom, + ) async { + return Fluster( + minZoom: minZoom, + maxZoom: maxZoom, + radius: 150, + extent: 2048, + nodeSize: 64, + points: markers, + createCluster: ( + BaseCluster? cluster, + double? lng, + double? lat, + ) => + MapMarker( + id: cluster!.id.toString(), + position: LatLng(lat!, lng!), + isCluster: cluster.isCluster, + clusterId: cluster.id, + pointsSize: cluster.pointsSize, + childMarkerId: cluster.childMarkerId, + ), + ); + } + + /// Gets a list of markers and clusters that reside within the visible bounding box for + /// the given [currentZoom]. For more info check [Fluster.clusters]. + static Future> getClusterMarkers( + Fluster? clusterManager, + double currentZoom, + Color clusterColor, + Color clusterTextColor, + int clusterWidth, + ) { + if (clusterManager == null) return Future.value([]); + + return Future.wait(clusterManager.clusters( + [-180, -85, 180, 85], + currentZoom.toInt(), + ).map((mapMarker) async { + if (mapMarker.isCluster!) { + mapMarker.icon = await _getClusterMarker( + mapMarker.pointsSize!, + clusterColor, + clusterTextColor, + clusterWidth, + ); + } + + return mapMarker.toMarker(); + }).toList()); + } +} diff --git a/lib/helpers/map_marker.dart b/lib/helpers/map_marker.dart new file mode 100644 index 0000000..da94145 --- /dev/null +++ b/lib/helpers/map_marker.dart @@ -0,0 +1,41 @@ +import 'package:fluster/fluster.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +/// [Fluster] can only handle markers that conform to the [Clusterable] abstract class. +/// +/// You can customize this class by adding more parameters that might be needed for +/// your use case. For instance, you can pass an onTap callback or add an +/// [InfoWindow] to your marker here, then you can use the [toMarker] method to convert +/// this to a proper [Marker] that the [GoogleMap] can read. +class MapMarker extends Clusterable { + final String id; + final LatLng position; + BitmapDescriptor? icon; + + MapMarker({ + required this.id, + required this.position, + this.icon, + isCluster = false, + clusterId, + pointsSize, + childMarkerId, + }) : super( + markerId: id, + latitude: position.latitude, + longitude: position.longitude, + isCluster: isCluster, + clusterId: clusterId, + pointsSize: pointsSize, + childMarkerId: childMarkerId, + ); + + Marker toMarker() => Marker( + markerId: MarkerId(isCluster! ? 'cl_$id' : id), + position: LatLng( + position.latitude, + position.longitude, + ), + icon: icon!, + ); +} diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index f601466..8d05583 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -13,37 +13,8 @@ export 'package:interactive_maps_marker/interactive_maps_controller.dart'; import 'package:geolocator/geolocator.dart'; import './utils.dart'; - -class MapMarker extends Clusterable { - final String id; - final LatLng position; - final BitmapDescriptor icon; - MapMarker({ - required this.id, - required this.position, - required this.icon, - isCluster = false, - clusterId, - pointsSize, - childMarkerId, - }) : super( - markerId: id, - latitude: position.latitude, - longitude: position.longitude, - isCluster: isCluster, - clusterId: clusterId, - pointsSize: pointsSize, - childMarkerId: childMarkerId, - ); - Marker toMarker() => Marker( - markerId: MarkerId(id), - position: LatLng( - position.latitude, - position.longitude, - ), - icon: icon, - ); -} +import 'helpers/map_helper.dart'; +import 'helpers/map_marker.dart'; class MarkerItem { int id; @@ -122,54 +93,63 @@ class InteractiveMapsMarkerState extends State { GoogleMapController? mapController; PageController pageController = PageController(viewportFraction: 0.9); LatLng? _initialPosition; - final List markers = []; - final List markerLocations = [ - LatLng(36.837446, 10.177410), - LatLng(36.813458, 10.133916), - LatLng(36.860832, 10.253826), - ]; + List googleMarkers = []; /* Set markers = {}; */ int currentIndex = 0; ValueNotifier selectedMarker = ValueNotifier(0); + /// Set of displayed markers and cluster markers on the map + final Set _markers = Set(); + + /// Minimum zoom at which the markers will cluster + final int _minClusterZoom = 0; + + /// Maximum zoom at which the markers will cluster + final int _maxClusterZoom = 19; + + /// [Fluster] instance used to manage the clusters + Fluster? _clusterManager; + + /// Current map zoom. Initial zoom will be 15, street level + double _currentZoom = 15; + + /// Map loading flag + bool _isMapLoading = true; + + /// Markers loading flag + bool _areMarkersLoading = true; + + /// Url image used on normal markers + final String _markerImageUrl = + 'https://img.icons8.com/office/80/000000/marker.png'; + + /// Color of the cluster circle + final Color _clusterColor = Colors.blue; + + /// Color of the cluster text + final Color _clusterTextColor = Colors.white; + final List markers = []; + + /// Example marker coordinates + final List _markerLocations = [ + LatLng(41.147125, -8.611249), + LatLng(41.145599, -8.610691), + LatLng(41.145645, -8.614761), + LatLng(41.146775, -8.614913), + LatLng(41.146982, -8.615682), + LatLng(41.140558, -8.611530), + LatLng(41.138393, -8.608642), + LatLng(41.137860, -8.609211), + LatLng(41.138344, -8.611236), + LatLng(41.139813, -8.609381), + ]; + @override void initState() { _getUserLocation(); - for (LatLng markerLocation in markerLocations) { - markers.add( - MapMarker( - id: markerLocations.indexOf(markerLocation).toString(), - position: markerLocation, - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), - ), - ); - } - final Fluster fluster = Fluster( - minZoom: 0, - maxZoom: 21, - radius: 150 ~/ 2, - extent: 2048, - nodeSize: 32, - points: markers, - createCluster: - (BaseCluster? cluster, double? longitude, double? latitude) { - return MapMarker( - id: cluster!.id.toString(), - position: LatLng(latitude!, longitude!), - icon: BitmapDescriptor.defaultMarkerWithHue( - BitmapDescriptor.hueOrange), - isCluster: cluster.isCluster, - clusterId: cluster.id, - pointsSize: cluster.pointsSize, - childMarkerId: cluster.childMarkerId, - ); - }); - googleMarkers = fluster - .clusters([7.597046,35.726447,12.716675,37.874853], 12) - .map((cluster) => cluster.toMarker()) - .toList(); + rebuildMarkers(currentIndex); super.initState(); } @@ -222,6 +202,59 @@ class InteractiveMapsMarkerState extends State { }); } + /// Inits [Fluster] and all the markers with network images and updates the loading state. + void _initMarkers() async { + + for (LatLng markerLocation in _markerLocations) { + final BitmapDescriptor markerImage = + await MapHelper.getMarkerImageFromUrl(_markerImageUrl); + + markers.add( + MapMarker( + id: _markerLocations.indexOf(markerLocation).toString(), + position: markerLocation, + icon: markerImage, + ), + ); + } + + _clusterManager = (await MapHelper.initClusterManager( + markers, + _minClusterZoom, + _maxClusterZoom, + )) as Fluster?; + + await _updateMarkers(); + } + + Future _updateMarkers([double? updatedZoom]) async { + if (_clusterManager == null || updatedZoom == _currentZoom) return; + + if (updatedZoom != null) { + _currentZoom = updatedZoom; + } + + setState(() { + _areMarkersLoading = true; + }); + + final updatedMarkers = await MapHelper.getClusterMarkers( + _clusterManager, + _currentZoom, + _clusterColor, + _clusterTextColor, + 80, + ); + + _markers + ..clear() + ..addAll(updatedMarkers); + + setState(() { + _areMarkersLoading = false; + }); + } + @override Widget build(BuildContext context) { return StreamBuilder( @@ -285,11 +318,13 @@ class InteractiveMapsMarkerState extends State { : "assets/json/mapstyle_dark.json", ), ); + _initMarkers(); }, initialCameraPosition: CameraPosition( target: _initialPosition as LatLng, zoom: widget.zoom, ), + onCameraMove: (position) => _updateMarkers(position.zoom), ); }, ), From 950103a468ca9ce391d3b05d4f7b659a5f695b7f Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 15:22:30 +0100 Subject: [PATCH 30/71] try to cluster still 16 change to images --- lib/helpers/map_helper.dart | 3 +- pubspec.lock | 144 ++++++++++++++++++++++++++++++++++++ pubspec.yaml | 1 + 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/lib/helpers/map_helper.dart b/lib/helpers/map_helper.dart index 395b86a..e4e8199 100644 --- a/lib/helpers/map_helper.dart +++ b/lib/helpers/map_helper.dart @@ -6,9 +6,10 @@ import 'dart:ui'; import 'package:fluster/fluster.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; -import 'package:flutter_google_maps_clusters/helpers/map_marker.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'map_marker.dart'; + /// In here we are encapsulating all the logic required to get marker icons from url images /// and to show clusters using the [Fluster] package. class MapHelper { diff --git a/pubspec.lock b/pubspec.lock index 72b0358..21bfd78 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -65,6 +65,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" fluster: dependency: "direct main" description: @@ -78,6 +94,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" + url: "https://pub.dev" + source: hosted + version: "3.3.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -200,6 +224,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.15.4" + http: + dependency: transitive + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" js: dependency: transitive description: @@ -248,6 +288,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + url: "https://pub.dev" + source: hosted + version: "3.1.2" plugin_platform_interface: dependency: transitive description: @@ -256,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.6" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" sanitize_html: dependency: transitive description: @@ -277,6 +381,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" stack_trace: dependency: transitive description: @@ -309,6 +429,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" term_glyph: dependency: transitive description: @@ -349,6 +477,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + win32: + dependency: transitive + description: + name: win32 + sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067 + url: "https://pub.dev" + source: hosted + version: "5.0.8" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + url: "https://pub.dev" + source: hosted + version: "1.0.3" sdks: dart: ">=3.0.0 <4.0.0" flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 10966c0..ad5d978 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: sdk: flutter geolocator: ^10.0.1 google_maps_flutter: ^2.2.5 + flutter_cache_manager: ^3.0.2 fluster: any dev_dependencies: flutter_test: From 42a13662444503eb0bd1ebd8ca54928a21c33383 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 15:59:48 +0100 Subject: [PATCH 31/71] try to cluster still 17 change to images --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 8d05583..0c48c46 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -306,7 +306,7 @@ class InteractiveMapsMarkerState extends State { print('Values changed'); return GoogleMap( zoomControlsEnabled: false, - markers: googleMarkers.toSet(), + markers: _markers, myLocationEnabled: true, myLocationButtonEnabled: false, onMapCreated: (GoogleMapController controller) async { From af1fdfdf58c7ea37f4f245564215d6330fcd4b69 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 20 Sep 2023 16:21:31 +0100 Subject: [PATCH 32/71] feature basic clustring markers --- lib/interactive_maps_marker.dart | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 0c48c46..8854db0 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -130,20 +130,20 @@ class InteractiveMapsMarkerState extends State { /// Color of the cluster text final Color _clusterTextColor = Colors.white; - final List markers = []; + final List markers = []; /// Example marker coordinates final List _markerLocations = [ - LatLng(41.147125, -8.611249), - LatLng(41.145599, -8.610691), - LatLng(41.145645, -8.614761), - LatLng(41.146775, -8.614913), - LatLng(41.146982, -8.615682), - LatLng(41.140558, -8.611530), - LatLng(41.138393, -8.608642), - LatLng(41.137860, -8.609211), - LatLng(41.138344, -8.611236), - LatLng(41.139813, -8.609381), + LatLng(36.860832, 10.253826), + LatLng(36.837446, 10.177410), + LatLng(36.813458, 10.133916), + LatLng(36.80324799649396, 10.178795859199756), + LatLng(36.84867719670467, 10.173551048840771), + LatLng(36.83304905048471, 10.23132067865196), + LatLng(36.85052619143521, 10.27096510507772), + LatLng(36.453724423821065, 10.741209233267941), + LatLng(36.44834969677488, 10.736808809719832), + LatLng(36.419724494437965, 10.665472609479401), ]; @override @@ -204,7 +204,6 @@ class InteractiveMapsMarkerState extends State { /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { - for (LatLng markerLocation in _markerLocations) { final BitmapDescriptor markerImage = await MapHelper.getMarkerImageFromUrl(_markerImageUrl); @@ -324,7 +323,7 @@ class InteractiveMapsMarkerState extends State { target: _initialPosition as LatLng, zoom: widget.zoom, ), - onCameraMove: (position) => _updateMarkers(position.zoom), + onCameraMove: (position) => _updateMarkers(position.zoom), ); }, ), From 7aa194a451d51284beccd627c0821919b4ac4e8f Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 21 Sep 2023 16:54:05 +0100 Subject: [PATCH 33/71] markers clustering feature and darkmode --- lib/interactive_maps_marker.dart | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 8854db0..0a99bec 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -122,8 +122,11 @@ class InteractiveMapsMarkerState extends State { bool _areMarkersLoading = true; /// Url image used on normal markers - final String _markerImageUrl = - 'https://img.icons8.com/office/80/000000/marker.png'; + /// Url image used on normal markers + final String _markerImageUrl = 'https://i.ibb.co/jZmy40R/marker.png'; + + final String _markerImageDarkUrl = + 'https://i.ibb.co/TTnV65k/marker-darkmode.png'; /// Color of the cluster circle final Color _clusterColor = Colors.blue; @@ -205,9 +208,12 @@ class InteractiveMapsMarkerState extends State { /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { for (LatLng markerLocation in _markerLocations) { - final BitmapDescriptor markerImage = - await MapHelper.getMarkerImageFromUrl(_markerImageUrl); - + final BitmapDescriptor markerImage = + await MapHelper.getMarkerImageFromUrl( + Theme.of(context).brightness == Brightness.dark + ? _markerImageDarkUrl + : _markerImageUrl, + targetWidth: 80); markers.add( MapMarker( id: _markerLocations.indexOf(markerLocation).toString(), @@ -264,16 +270,7 @@ class InteractiveMapsMarkerState extends State { children: [ _initialPosition != null ? _buildMap() - : Container( - child: Center( - child: Text( - 'loading map..', - style: TextStyle( - fontFamily: 'Avenir-Medium', - color: Colors.grey[400]), - ), - ), - ), + : Center(child: CircularProgressIndicator()), Align( alignment: widget.contentAlignment, child: Padding( From bf4abd435558a703df0c179b5b15ea812f6a2a1e Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 21 Sep 2023 17:08:51 +0100 Subject: [PATCH 34/71] change cluster color --- lib/interactive_maps_marker.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 0a99bec..494b2fd 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -122,14 +122,14 @@ class InteractiveMapsMarkerState extends State { bool _areMarkersLoading = true; /// Url image used on normal markers - /// Url image used on normal markers + /// Url image used on normal markers final String _markerImageUrl = 'https://i.ibb.co/jZmy40R/marker.png'; final String _markerImageDarkUrl = 'https://i.ibb.co/TTnV65k/marker-darkmode.png'; /// Color of the cluster circle - final Color _clusterColor = Colors.blue; + final Color _clusterColor = Color(0xFFff5f5f); /// Color of the cluster text final Color _clusterTextColor = Colors.white; @@ -208,7 +208,7 @@ class InteractiveMapsMarkerState extends State { /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { for (LatLng markerLocation in _markerLocations) { - final BitmapDescriptor markerImage = + final BitmapDescriptor markerImage = await MapHelper.getMarkerImageFromUrl( Theme.of(context).brightness == Brightness.dark ? _markerImageDarkUrl From 540aa8e499c04d7db3fb6d2fb4db4372a49f79af Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Mon, 25 Sep 2023 20:04:11 +0100 Subject: [PATCH 35/71] feature add on tap marker --- lib/helpers/map_marker.dart | 29 +++++++++++++++-------------- lib/interactive_maps_marker.dart | 10 ++++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lib/helpers/map_marker.dart b/lib/helpers/map_marker.dart index da94145..6e5db4a 100644 --- a/lib/helpers/map_marker.dart +++ b/lib/helpers/map_marker.dart @@ -1,21 +1,17 @@ import 'package:fluster/fluster.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; -/// [Fluster] can only handle markers that conform to the [Clusterable] abstract class. -/// -/// You can customize this class by adding more parameters that might be needed for -/// your use case. For instance, you can pass an onTap callback or add an -/// [InfoWindow] to your marker here, then you can use the [toMarker] method to convert -/// this to a proper [Marker] that the [GoogleMap] can read. class MapMarker extends Clusterable { final String id; final LatLng position; BitmapDescriptor? icon; + final Function()? onTap; // Add onTap callback MapMarker({ required this.id, required this.position, this.icon, + this.onTap, // Initialize onTap property isCluster = false, clusterId, pointsSize, @@ -30,12 +26,17 @@ class MapMarker extends Clusterable { childMarkerId: childMarkerId, ); - Marker toMarker() => Marker( - markerId: MarkerId(isCluster! ? 'cl_$id' : id), - position: LatLng( - position.latitude, - position.longitude, - ), - icon: icon!, - ); + Marker toMarker() { + final marker = Marker( + markerId: MarkerId(isCluster! ? 'cl_$id' : id), + position: LatLng( + position.latitude, + position.longitude, + ), + icon: icon!, + onTap: onTap, // Set the onTap callback for the Marker + ); + + return marker; + } } diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 494b2fd..79940d9 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -216,6 +216,16 @@ class InteractiveMapsMarkerState extends State { targetWidth: 80); markers.add( MapMarker( + onTap: () { + int tappedIndex = + widget.items.indexWhere((element) => element.id == _markerLocations.indexOf(markerLocation)); + pageController.animateToPage( + tappedIndex, + duration: Duration(milliseconds: 300), + curve: Curves.bounceInOut, + ); + _pageChanged(tappedIndex); + }, id: _markerLocations.indexOf(markerLocation).toString(), position: markerLocation, icon: markerImage, From 6677a02f021a580a55ccd6c4d36791fb4c930c93 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Mon, 25 Sep 2023 20:14:44 +0100 Subject: [PATCH 36/71] feature add my location button for the map --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 79940d9..236dff7 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -314,7 +314,7 @@ class InteractiveMapsMarkerState extends State { zoomControlsEnabled: false, markers: _markers, myLocationEnabled: true, - myLocationButtonEnabled: false, + myLocationButtonEnabled: true, onMapCreated: (GoogleMapController controller) async { mapController = controller; await mapController?.setMapStyle( From 36fb0bfce0593097e64df9a56df807812156ec72 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Mon, 25 Sep 2023 20:21:44 +0100 Subject: [PATCH 37/71] fix padding for mylocation button --- lib/interactive_maps_marker.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 236dff7..7061a42 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -217,8 +217,8 @@ class InteractiveMapsMarkerState extends State { markers.add( MapMarker( onTap: () { - int tappedIndex = - widget.items.indexWhere((element) => element.id == _markerLocations.indexOf(markerLocation)); + int tappedIndex = widget.items.indexWhere((element) => + element.id == _markerLocations.indexOf(markerLocation)); pageController.animateToPage( tappedIndex, duration: Duration(milliseconds: 300), @@ -315,6 +315,9 @@ class InteractiveMapsMarkerState extends State { markers: _markers, myLocationEnabled: true, myLocationButtonEnabled: true, + padding: EdgeInsets.only( + top: 40.0, + ), onMapCreated: (GoogleMapController controller) async { mapController = controller; await mapController?.setMapStyle( From acb7ac052a19429b464d031382d131d8a0ce8d45 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 16:45:13 +0100 Subject: [PATCH 38/71] change markers to be recieved from the properties --- lib/interactive_maps_marker.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 7061a42..69011e2 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -18,11 +18,9 @@ import 'helpers/map_marker.dart'; class MarkerItem { int id; - double latitude; - double longitude; + LatLng location; - MarkerItem( - {required this.id, required this.latitude, required this.longitude}); + MarkerItem({required this.id, required this.location}); } class InteractiveMapsMarker extends StatefulWidget { From 7836b51f3c9fe02a3304e0cda1340f67a62b662b Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 16:55:24 +0100 Subject: [PATCH 39/71] Debuging/change markers to be recieved from the properties --- lib/interactive_maps_marker.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 69011e2..ddc145e 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -150,7 +150,9 @@ class InteractiveMapsMarkerState extends State { @override void initState() { _getUserLocation(); - + var markerLocationsNew = + widget.items.map((location) => location).cast().toList(); + print(markerLocationsNew); rebuildMarkers(currentIndex); super.initState(); } From 4436d3a0fa37fc5581f20a90ce9b88386e1e5985 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 16:58:28 +0100 Subject: [PATCH 40/71] Debuging2/change markers to be recieved from the properties --- lib/interactive_maps_marker.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index ddc145e..7c6359e 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -150,9 +150,7 @@ class InteractiveMapsMarkerState extends State { @override void initState() { _getUserLocation(); - var markerLocationsNew = - widget.items.map((location) => location).cast().toList(); - print(markerLocationsNew); + rebuildMarkers(currentIndex); super.initState(); } @@ -207,7 +205,8 @@ class InteractiveMapsMarkerState extends State { /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { - for (LatLng markerLocation in _markerLocations) { + for (LatLng markerLocation + in widget.items.map((e) => e.location).toList()) { final BitmapDescriptor markerImage = await MapHelper.getMarkerImageFromUrl( Theme.of(context).brightness == Brightness.dark @@ -218,7 +217,7 @@ class InteractiveMapsMarkerState extends State { MapMarker( onTap: () { int tappedIndex = widget.items.indexWhere((element) => - element.id == _markerLocations.indexOf(markerLocation)); + element.id == widget.items.map((e) => e.location).toList().indexOf(markerLocation)); pageController.animateToPage( tappedIndex, duration: Duration(milliseconds: 300), @@ -226,7 +225,7 @@ class InteractiveMapsMarkerState extends State { ); _pageChanged(tappedIndex); }, - id: _markerLocations.indexOf(markerLocation).toString(), + id: widget.items.map((e) => e.location).toList().indexOf(markerLocation).toString(), position: markerLocation, icon: markerImage, ), From 4b0b373595ae54a5fbcddf45d6692bdb52564893 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 19:23:30 +0100 Subject: [PATCH 41/71] feature/change markers to appear depanding on zoom level --- lib/interactive_maps_marker.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 7c6359e..33eca3f 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -132,6 +132,8 @@ class InteractiveMapsMarkerState extends State { /// Color of the cluster text final Color _clusterTextColor = Colors.white; final List markers = []; + late List newMarkerPostions = + widget.items.map((e) => e.location).toList(); /// Example marker coordinates final List _markerLocations = [ @@ -205,8 +207,7 @@ class InteractiveMapsMarkerState extends State { /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { - for (LatLng markerLocation - in widget.items.map((e) => e.location).toList()) { + for (LatLng markerLocation in newMarkerPostions) { final BitmapDescriptor markerImage = await MapHelper.getMarkerImageFromUrl( Theme.of(context).brightness == Brightness.dark @@ -216,8 +217,7 @@ class InteractiveMapsMarkerState extends State { markers.add( MapMarker( onTap: () { - int tappedIndex = widget.items.indexWhere((element) => - element.id == widget.items.map((e) => e.location).toList().indexOf(markerLocation)); + int tappedIndex = newMarkerPostions.indexOf(markerLocation); pageController.animateToPage( tappedIndex, duration: Duration(milliseconds: 300), @@ -225,7 +225,7 @@ class InteractiveMapsMarkerState extends State { ); _pageChanged(tappedIndex); }, - id: widget.items.map((e) => e.location).toList().indexOf(markerLocation).toString(), + id: newMarkerPostions.indexOf(markerLocation).toString(), position: markerLocation, icon: markerImage, ), @@ -332,7 +332,10 @@ class InteractiveMapsMarkerState extends State { target: _initialPosition as LatLng, zoom: widget.zoom, ), - onCameraMove: (position) => _updateMarkers(position.zoom), + onCameraMove: (position) => { + _updateMarkers(position.zoom), + if (position.zoom > 10) {newMarkerPostions = _markerLocations} + }, ); }, ), From 356ba3d91d3f578994050c256b791de12c4e1f9b Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 19:31:16 +0100 Subject: [PATCH 42/71] feature/change markers to appear depanding on zoom level 2 --- lib/interactive_maps_marker.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 33eca3f..19b41ae 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -334,7 +334,8 @@ class InteractiveMapsMarkerState extends State { ), onCameraMove: (position) => { _updateMarkers(position.zoom), - if (position.zoom > 10) {newMarkerPostions = _markerLocations} + if (position.zoom > 10) + {newMarkerPostions = _markerLocations, _initMarkers()} }, ); }, From 8e345a622014676020eedd5a6ee4c2023ed8b7e5 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 19:37:50 +0100 Subject: [PATCH 43/71] feature/change markers to appear depanding on zoom level 3 --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 19b41ae..44e8508 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -335,7 +335,7 @@ class InteractiveMapsMarkerState extends State { onCameraMove: (position) => { _updateMarkers(position.zoom), if (position.zoom > 10) - {newMarkerPostions = _markerLocations, _initMarkers()} + {newMarkerPostions = _markerLocations, _updateMarkers()} }, ); }, From 7ae8a5c2a6ab930232522cf651d1a15e2315197a Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 28 Sep 2023 19:45:34 +0100 Subject: [PATCH 44/71] feature/change markers to appear depanding on zoom level 4 --- lib/interactive_maps_marker.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 44e8508..10804bb 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -334,8 +334,6 @@ class InteractiveMapsMarkerState extends State { ), onCameraMove: (position) => { _updateMarkers(position.zoom), - if (position.zoom > 10) - {newMarkerPostions = _markerLocations, _updateMarkers()} }, ); }, From e07e8c23daccbfee61221ee940bb23456dc2b085 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sun, 1 Oct 2023 19:55:46 +0100 Subject: [PATCH 45/71] add intial position parameter to map widget --- lib/interactive_maps_marker.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 10804bb..4513114 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -37,7 +37,7 @@ class InteractiveMapsMarker extends StatefulWidget { final IndexedWidgetBuilder? itemBuilder; final EdgeInsetsGeometry itemPadding; final Alignment contentAlignment; - + final LatLng? initialPositionFromlist; InteractiveMapsController? controller; VoidCallback? onLastItem; @@ -54,6 +54,7 @@ class InteractiveMapsMarker extends StatefulWidget { this.contentAlignment = Alignment.bottomCenter, this.controller, this.onLastItem, + this.initialPositionFromlist, }) { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); @@ -329,7 +330,8 @@ class InteractiveMapsMarkerState extends State { _initMarkers(); }, initialCameraPosition: CameraPosition( - target: _initialPosition as LatLng, + target: + widget.initialPositionFromlist ?? _initialPosition as LatLng, zoom: widget.zoom, ), onCameraMove: (position) => { From ec7137fb5f29de4574de59de04b63fc224caff65 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sun, 1 Oct 2023 21:19:52 +0100 Subject: [PATCH 46/71] add intial position parameter to map widget update --- lib/interactive_maps_marker.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 4513114..cff1e44 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -330,8 +330,9 @@ class InteractiveMapsMarkerState extends State { _initMarkers(); }, initialCameraPosition: CameraPosition( - target: - widget.initialPositionFromlist ?? _initialPosition as LatLng, + target: widget.initialPositionFromlist != null + ? _initialPosition as LatLng + : widget.initialPositionFromlist as LatLng, zoom: widget.zoom, ), onCameraMove: (position) => { From dce9e1a321a6fa73d251f2c7950acda237417a93 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sun, 1 Oct 2023 21:25:46 +0100 Subject: [PATCH 47/71] add intial position parameter to map widget update2 --- lib/interactive_maps_marker.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index cff1e44..147e145 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -331,8 +331,8 @@ class InteractiveMapsMarkerState extends State { }, initialCameraPosition: CameraPosition( target: widget.initialPositionFromlist != null - ? _initialPosition as LatLng - : widget.initialPositionFromlist as LatLng, + ? widget.initialPositionFromlist as LatLng + : _initialPosition as LatLng, zoom: widget.zoom, ), onCameraMove: (position) => { From 44f32b8269305cbfaee96f3b5c2ccb401e78be2d Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sun, 1 Oct 2023 21:43:09 +0100 Subject: [PATCH 48/71] add geocoding to get city from current position --- lib/interactive_maps_marker.dart | 7 ++++++- pubspec.lock | 32 ++++++++++++++++++++++++++++++++ pubspec.yaml | 2 ++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 147e145..5f656a4 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -7,6 +7,7 @@ import 'package:fluster/fluster.dart'; import "package:flutter/material.dart"; import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; +import 'package:geocoding/geocoding.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:interactive_maps_marker/interactive_maps_controller.dart'; export 'package:interactive_maps_marker/interactive_maps_controller.dart'; @@ -200,7 +201,11 @@ class InteractiveMapsMarkerState extends State { var position = await GeolocatorPlatform.instance.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.bestForNavigation)); - + List placemarks = await placemarkFromCoordinates( + position.latitude, + position.longitude, + ); + print(placemarks[0]); setState(() { _initialPosition = LatLng(position.latitude, position.longitude); }); diff --git a/pubspec.lock b/pubspec.lock index 21bfd78..a8bd9d5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -120,6 +120,38 @@ packages: description: flutter source: sdk version: "0.0.0" + geocoding: + dependency: "direct main" + description: + name: geocoding + sha256: b34c0501bbbaf3190b85bef3078b27cf66c28a8915c6d3af50d67f356aa7da31 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + geocoding_android: + dependency: transitive + description: + name: geocoding_android + sha256: "609db1d71bc364dd9d0616f72a41c01e0c74f3a3807efb85e0d5a67e57baf50f" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + geocoding_ios: + dependency: transitive + description: + name: geocoding_ios + sha256: c85495ce8fb34e4fbd2dd8fc5f79263d622d9f88c4af948c965daf6b27a7f3a1 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + geocoding_platform_interface: + dependency: transitive + description: + name: geocoding_platform_interface + sha256: "8848605d307d844d89937cdb4b8ad7dfa880552078f310fa24d8a460f6dddab4" + url: "https://pub.dev" + source: hosted + version: "2.0.1" geolocator: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ad5d978..a9f4938 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,8 @@ dependencies: google_maps_flutter: ^2.2.5 flutter_cache_manager: ^3.0.2 fluster: any + geocoding: ^2.1.0 + dev_dependencies: flutter_test: sdk: flutter From f7ea1b64fa1e342729197269870cc14f01a56020 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sun, 1 Oct 2023 22:08:23 +0100 Subject: [PATCH 49/71] filtering dynamicaly with localitys --- lib/interactive_maps_marker.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 5f656a4..54c9d85 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -20,8 +20,8 @@ import 'helpers/map_marker.dart'; class MarkerItem { int id; LatLng location; - - MarkerItem({required this.id, required this.location}); + String ville; + MarkerItem({required this.id, required this.location, required this.ville}); } class InteractiveMapsMarker extends StatefulWidget { @@ -134,8 +134,10 @@ class InteractiveMapsMarkerState extends State { /// Color of the cluster text final Color _clusterTextColor = Colors.white; final List markers = []; + + List NewFilteredMarkerPositions = []; late List newMarkerPostions = - widget.items.map((e) => e.location).toList(); + NewFilteredMarkerPositions.map((e) => e.location).toList(); /// Example marker coordinates final List _markerLocations = [ @@ -205,9 +207,13 @@ class InteractiveMapsMarkerState extends State { position.latitude, position.longitude, ); + print(placemarks[0]); setState(() { _initialPosition = LatLng(position.latitude, position.longitude); + NewFilteredMarkerPositions = widget.items + .where((element) => element.ville == placemarks[0].locality) + .toList(); }); } From ea2eb83fe8102fab4168254aeac5351a19293523 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sun, 1 Oct 2023 22:22:33 +0100 Subject: [PATCH 50/71] update filtering dynamicaly with localitys --- lib/interactive_maps_marker.dart | 41 ++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 54c9d85..bc651d8 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -39,24 +39,25 @@ class InteractiveMapsMarker extends StatefulWidget { final EdgeInsetsGeometry itemPadding; final Alignment contentAlignment; final LatLng? initialPositionFromlist; + final String? filteredCity; InteractiveMapsController? controller; VoidCallback? onLastItem; - InteractiveMapsMarker({ - required this.items, - this.itemBuilder, - this.center = const LatLng(0.0, 0.0), - this.itemContent, - this.itemHeight = 116, - this.zoom = 12.0, - this.zoomFocus = 15.0, - this.zoomKeepOnTap = false, - this.itemPadding = const EdgeInsets.only(bottom: 80.0), - this.contentAlignment = Alignment.bottomCenter, - this.controller, - this.onLastItem, - this.initialPositionFromlist, - }) { + InteractiveMapsMarker( + {required this.items, + this.itemBuilder, + this.center = const LatLng(0.0, 0.0), + this.itemContent, + this.itemHeight = 116, + this.zoom = 12.0, + this.zoomFocus = 15.0, + this.zoomKeepOnTap = false, + this.itemPadding = const EdgeInsets.only(bottom: 80.0), + this.contentAlignment = Alignment.bottomCenter, + this.controller, + this.onLastItem, + this.initialPositionFromlist, + this.filteredCity}) { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } @@ -211,9 +212,13 @@ class InteractiveMapsMarkerState extends State { print(placemarks[0]); setState(() { _initialPosition = LatLng(position.latitude, position.longitude); - NewFilteredMarkerPositions = widget.items - .where((element) => element.ville == placemarks[0].locality) - .toList(); + NewFilteredMarkerPositions = widget.filteredCity != null + ? widget.items + .where((element) => element.ville == widget.filteredCity) + .toList() + : widget.items + .where((element) => element.ville == placemarks[0].locality) + .toList(); }); } From 22744d982567b04fcabc4335bc1e4e1e0bca8b63 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 4 Oct 2023 00:38:25 +0100 Subject: [PATCH 51/71] change image assets from folder --- lib/helpers/map_helper.dart | 11 ++++++----- lib/interactive_maps_marker.dart | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/helpers/map_helper.dart b/lib/helpers/map_helper.dart index e4e8199..5ecb981 100644 --- a/lib/helpers/map_helper.dart +++ b/lib/helpers/map_helper.dart @@ -5,6 +5,7 @@ import 'dart:ui'; import 'package:fluster/fluster.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; @@ -21,13 +22,13 @@ class MapHelper { /// time to download the file and set the marker image. /// /// You can resize the marker image by providing a [targetWidth]. - static Future getMarkerImageFromUrl( - String url, { + static Future getMarkerImageFromAsset( + String assetPath, { int? targetWidth, }) async { - final File markerImageFile = await DefaultCacheManager().getSingleFile(url); - - Uint8List markerImageBytes = await markerImageFile.readAsBytes(); + // Load the image asset from the project folder + final ByteData data = await rootBundle.load(assetPath); + Uint8List markerImageBytes = data.buffer.asUint8List(); if (targetWidth != null) { markerImageBytes = await _resizeImageBytes( diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index bc651d8..772cd27 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -124,10 +124,10 @@ class InteractiveMapsMarkerState extends State { /// Url image used on normal markers /// Url image used on normal markers - final String _markerImageUrl = 'https://i.ibb.co/jZmy40R/marker.png'; + final String _markerImageUrl = 'assets/marker.png'; final String _markerImageDarkUrl = - 'https://i.ibb.co/TTnV65k/marker-darkmode.png'; + 'assets/marker_darkmode.png'; /// Color of the cluster circle final Color _clusterColor = Color(0xFFff5f5f); @@ -226,7 +226,7 @@ class InteractiveMapsMarkerState extends State { void _initMarkers() async { for (LatLng markerLocation in newMarkerPostions) { final BitmapDescriptor markerImage = - await MapHelper.getMarkerImageFromUrl( + await MapHelper.getMarkerImageFromAsset( Theme.of(context).brightness == Brightness.dark ? _markerImageDarkUrl : _markerImageUrl, From ad117989a03afb605eb4afbac38a8c1b49f4314c Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 4 Oct 2023 00:49:50 +0100 Subject: [PATCH 52/71] change image assets from folder 2 --- pubspec.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index a9f4938..4d282c7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,4 +22,6 @@ dev_dependencies: flutter: assets: - - assets/ \ No newline at end of file + - assets/ + - assets/marker.png + - assets/marker_darkmode.png \ No newline at end of file From 33f305c69dd941e9414a4297cf5057978204c65e Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 4 Oct 2023 01:00:25 +0100 Subject: [PATCH 53/71] change image assets from folder 3 --- lib/interactive_maps_marker.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 772cd27..30e24d6 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -124,10 +124,10 @@ class InteractiveMapsMarkerState extends State { /// Url image used on normal markers /// Url image used on normal markers - final String _markerImageUrl = 'assets/marker.png'; + final String _markerImageUrl = 'packages/interactive_maps_marker/assets/marker.png'; final String _markerImageDarkUrl = - 'assets/marker_darkmode.png'; + 'packages/interactive_maps_marker/assets/marker_darkmode.png'; /// Color of the cluster circle final Color _clusterColor = Color(0xFFff5f5f); From 00ea1baf0b3a6eb10f28a13c2af8f72bf0758c56 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 4 Oct 2023 02:36:52 +0100 Subject: [PATCH 54/71] handle restaurant list filter outside the package --- lib/interactive_maps_marker.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 30e24d6..8661d6a 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -136,9 +136,8 @@ class InteractiveMapsMarkerState extends State { final Color _clusterTextColor = Colors.white; final List markers = []; - List NewFilteredMarkerPositions = []; late List newMarkerPostions = - NewFilteredMarkerPositions.map((e) => e.location).toList(); + widget.items.map((e) => e.location).toList(); /// Example marker coordinates final List _markerLocations = [ @@ -156,8 +155,8 @@ class InteractiveMapsMarkerState extends State { @override void initState() { - _getUserLocation(); - +/* _getUserLocation(); + */ rebuildMarkers(currentIndex); super.initState(); } @@ -168,7 +167,7 @@ class InteractiveMapsMarkerState extends State { super.didChangeDependencies(); } - Future _handleLocationPermission() async { +/* Future _handleLocationPermission() async { bool serviceEnabled; LocationPermission permission; @@ -195,9 +194,9 @@ class InteractiveMapsMarkerState extends State { return false; } return true; - } + } */ - void _getUserLocation() async { +/* void _getUserLocation() async { final hasPermission = await _handleLocationPermission(); if (!hasPermission) return; @@ -220,7 +219,7 @@ class InteractiveMapsMarkerState extends State { .where((element) => element.ville == placemarks[0].locality) .toList(); }); - } + } */ /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { From 6f94471d340d9834bf07274e617aa016a6d9f38c Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 4 Oct 2023 02:50:16 +0100 Subject: [PATCH 55/71] handle restaurant list filter outside the package 5 --- lib/interactive_maps_marker.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 8661d6a..760aee4 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -124,7 +124,8 @@ class InteractiveMapsMarkerState extends State { /// Url image used on normal markers /// Url image used on normal markers - final String _markerImageUrl = 'packages/interactive_maps_marker/assets/marker.png'; + final String _markerImageUrl = + 'packages/interactive_maps_marker/assets/marker.png'; final String _markerImageDarkUrl = 'packages/interactive_maps_marker/assets/marker_darkmode.png'; @@ -293,9 +294,7 @@ class InteractiveMapsMarkerState extends State { builder: (context, snapshot) { return Stack( children: [ - _initialPosition != null - ? _buildMap() - : Center(child: CircularProgressIndicator()), + _buildMap(), Align( alignment: widget.contentAlignment, child: Padding( From 0b36a13aef1fa09ffaa4687f25b189c6ff736b36 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 5 Oct 2023 13:52:31 +0100 Subject: [PATCH 56/71] fixed dynamic filtering --- lib/interactive_maps_marker.dart | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 760aee4..823d883 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -137,34 +137,25 @@ class InteractiveMapsMarkerState extends State { final Color _clusterTextColor = Colors.white; final List markers = []; - late List newMarkerPostions = - widget.items.map((e) => e.location).toList(); - - /// Example marker coordinates - final List _markerLocations = [ - LatLng(36.860832, 10.253826), - LatLng(36.837446, 10.177410), - LatLng(36.813458, 10.133916), - LatLng(36.80324799649396, 10.178795859199756), - LatLng(36.84867719670467, 10.173551048840771), - LatLng(36.83304905048471, 10.23132067865196), - LatLng(36.85052619143521, 10.27096510507772), - LatLng(36.453724423821065, 10.741209233267941), - LatLng(36.44834969677488, 10.736808809719832), - LatLng(36.419724494437965, 10.665472609479401), - ]; + late List newMarkerPostions = []; @override void initState() { /* _getUserLocation(); */ - rebuildMarkers(currentIndex); + newMarkerPostions = widget.items.map((e) => e.location).toList(); + print("toul el lista initial" + widget.items.toString()); + + print("toul el lista" + newMarkerPostions.toString()); +/* rebuildMarkers(currentIndex); + */ super.initState(); } @override void didChangeDependencies() { - rebuildMarkers(currentIndex); +/* rebuildMarkers(currentIndex); + */ super.didChangeDependencies(); } @@ -346,7 +337,7 @@ class InteractiveMapsMarkerState extends State { initialCameraPosition: CameraPosition( target: widget.initialPositionFromlist != null ? widget.initialPositionFromlist as LatLng - : _initialPosition as LatLng, + : widget.initialPositionFromlist as LatLng, zoom: widget.zoom, ), onCameraMove: (position) => { From 7a9317a7eb964f6df32b3aa6cb79aa53362e77cd Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 6 Oct 2023 17:11:12 +0100 Subject: [PATCH 57/71] delete unused dependancies --- pubspec.lock | 530 --------------------------------------------------- pubspec.yaml | 3 +- 2 files changed, 1 insertion(+), 532 deletions(-) delete mode 100644 pubspec.lock diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index a8bd9d5..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,530 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" - url: "https://pub.dev" - source: hosted - version: "1.17.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - fluster: - dependency: "direct main" - description: - name: fluster - sha256: "3807f5d088b7798f0416b8578498046338af98bb4fb922a70e2810b8293963f6" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: "direct main" - description: - name: flutter_cache_manager - sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" - url: "https://pub.dev" - source: hosted - version: "3.3.1" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c - url: "https://pub.dev" - source: hosted - version: "2.0.16" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - geocoding: - dependency: "direct main" - description: - name: geocoding - sha256: b34c0501bbbaf3190b85bef3078b27cf66c28a8915c6d3af50d67f356aa7da31 - url: "https://pub.dev" - source: hosted - version: "2.1.0" - geocoding_android: - dependency: transitive - description: - name: geocoding_android - sha256: "609db1d71bc364dd9d0616f72a41c01e0c74f3a3807efb85e0d5a67e57baf50f" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - geocoding_ios: - dependency: transitive - description: - name: geocoding_ios - sha256: c85495ce8fb34e4fbd2dd8fc5f79263d622d9f88c4af948c965daf6b27a7f3a1 - url: "https://pub.dev" - source: hosted - version: "2.1.0" - geocoding_platform_interface: - dependency: transitive - description: - name: geocoding_platform_interface - sha256: "8848605d307d844d89937cdb4b8ad7dfa880552078f310fa24d8a460f6dddab4" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - geolocator: - dependency: "direct main" - description: - name: geolocator - sha256: "9f1c9a70dd25fc9d9574ff17ba714cf3bc7894258efd7d1ce0debfafe2f8e1d8" - url: "https://pub.dev" - source: hosted - version: "10.0.1" - geolocator_android: - dependency: transitive - description: - name: geolocator_android - sha256: db75aacf93fcdc2c0ba20066019da30e3fde7b102cf579d4bd4ea542e8a8cf17 - url: "https://pub.dev" - source: hosted - version: "4.2.4" - geolocator_apple: - dependency: transitive - description: - name: geolocator_apple - sha256: "36527c555f4c425f7d8fa8c7c07d67b78e3ff7590d40448051959e1860c1cfb4" - url: "https://pub.dev" - source: hosted - version: "2.2.7" - geolocator_platform_interface: - dependency: transitive - description: - name: geolocator_platform_interface - sha256: b2a0c09d2cfe8ad6a8cc44fada5c1092c49286e8c30922914dfe2b23799a682f - url: "https://pub.dev" - source: hosted - version: "4.0.8" - geolocator_web: - dependency: transitive - description: - name: geolocator_web - sha256: f68a122da48fcfff68bbc9846bb0b74ef651afe84a1b1f6ec20939de4d6860e1 - url: "https://pub.dev" - source: hosted - version: "2.1.6" - geolocator_windows: - dependency: transitive - description: - name: geolocator_windows - sha256: "463045515b08bd83f73e014359c4ad063b902eb3899952cfb784497ae6c6583b" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - google_maps: - dependency: transitive - description: - name: google_maps - sha256: "555d5d736339b0478e821167ac521c810d7b51c3b2734e6802a9f046b64ea37a" - url: "https://pub.dev" - source: hosted - version: "6.3.0" - google_maps_flutter: - dependency: "direct main" - description: - name: google_maps_flutter - sha256: d4914cb38b3dcb62c39c085d968d434de0f8050f00f4d9f5ba4a7c7e004934cb - url: "https://pub.dev" - source: hosted - version: "2.5.0" - google_maps_flutter_android: - dependency: transitive - description: - name: google_maps_flutter_android - sha256: e6cb018169e49332f88d23b1d2119b09e8ab4e7d3a1b889a1b7b3fd113e034ba - url: "https://pub.dev" - source: hosted - version: "2.5.1" - google_maps_flutter_ios: - dependency: transitive - description: - name: google_maps_flutter_ios - sha256: "2a595c9789070786c654e9772ec0d1bb759ae37d2dd776291af5398531274e06" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - google_maps_flutter_platform_interface: - dependency: transitive - description: - name: google_maps_flutter_platform_interface - sha256: a3e9e6896501e566d902c6c69f010834d410ef4b7b5c18b90c77e871c86b7907 - url: "https://pub.dev" - source: hosted - version: "2.4.1" - google_maps_flutter_web: - dependency: transitive - description: - name: google_maps_flutter_web - sha256: "05067c5aa762ebee44b7ef4902a311ed8cf891ef655e2798bae063aa3050c8d9" - url: "https://pub.dev" - source: hosted - version: "0.5.4+1" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http: - dependency: transitive - description: - name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - js_wrapping: - dependency: transitive - description: - name: js_wrapping - sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c - url: "https://pub.dev" - source: hosted - version: "0.7.4" - matcher: - dependency: transitive - description: - name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" - url: "https://pub.dev" - source: hosted - version: "0.12.15" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa - url: "https://pub.dev" - source: hosted - version: "2.1.1" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - platform: - dependency: transitive - description: - name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d - url: "https://pub.dev" - source: hosted - version: "2.1.6" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - sanitize_html: - dependency: transitive - description: - name: sanitize_html - sha256: "0a445f19bbaa196f5a4f93461aa066b94e6e025622eb1e9bc77872a5e25233a5" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" - source: hosted - version: "1.9.1" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb - url: "https://pub.dev" - source: hosted - version: "0.5.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - uuid: - dependency: transitive - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - win32: - dependency: transitive - description: - name: win32 - sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067 - url: "https://pub.dev" - source: hosted - version: "5.0.8" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" - url: "https://pub.dev" - source: hosted - version: "1.0.3" -sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4d282c7..d9d5fd0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,11 +10,10 @@ environment: dependencies: flutter: sdk: flutter - geolocator: ^10.0.1 google_maps_flutter: ^2.2.5 flutter_cache_manager: ^3.0.2 fluster: any - geocoding: ^2.1.0 + dev_dependencies: flutter_test: From 6c598b8e11756c93f2c7f06a4e353ef24157f2ec Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Thu, 12 Oct 2023 05:14:22 +0100 Subject: [PATCH 58/71] Feature dynamic filtering Pageviewbuilder --- lib/interactive_maps_marker.dart | 68 ++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 823d883..dbe9f4d 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -36,16 +36,19 @@ class InteractiveMapsMarker extends StatefulWidget { final IndexedWidgetBuilder? itemContent; final IndexedWidgetBuilder? itemBuilder; + final IndexedWidgetBuilder? restItemBuilder; final EdgeInsetsGeometry itemPadding; final Alignment contentAlignment; final LatLng? initialPositionFromlist; final String? filteredCity; InteractiveMapsController? controller; VoidCallback? onLastItem; - + final List keys; + final List remainingKeys; InteractiveMapsMarker( {required this.items, this.itemBuilder, + this.restItemBuilder, this.center = const LatLng(0.0, 0.0), this.itemContent, this.itemHeight = 116, @@ -56,6 +59,8 @@ class InteractiveMapsMarker extends StatefulWidget { this.contentAlignment = Alignment.bottomCenter, this.controller, this.onLastItem, + required this.keys, + required this.remainingKeys, this.initialPositionFromlist, this.filteredCity}) { if (itemBuilder == null && itemContent == null) { @@ -121,6 +126,7 @@ class InteractiveMapsMarkerState extends State { /// Markers loading flag bool _areMarkersLoading = true; + bool setFromSameCity = false; /// Url image used on normal markers /// Url image used on normal markers @@ -137,16 +143,22 @@ class InteractiveMapsMarkerState extends State { final Color _clusterTextColor = Colors.white; final List markers = []; - late List newMarkerPostions = []; + Map indexMapping = {}; + Map indexMappingRemaining = {}; + late List newMarkerPostions = []; + late int? originalIndex = null; @override void initState() { /* _getUserLocation(); */ newMarkerPostions = widget.items.map((e) => e.location).toList(); print("toul el lista initial" + widget.items.toString()); - - print("toul el lista" + newMarkerPostions.toString()); + indexMapping = Map.fromIterable(widget.keys, + key: (item) => widget.keys.indexOf(item), value: (item) => item); + indexMappingRemaining = Map.fromIterable(widget.remainingKeys, + key: (item) => widget.remainingKeys.indexOf(item), + value: (item) => item); /* rebuildMarkers(currentIndex); */ super.initState(); @@ -212,6 +224,14 @@ class InteractiveMapsMarkerState extends State { .toList(); }); } */ + int? getKeyForValue(Map map, int targetValue) { + for (var entry in map.entries) { + if (entry.value == targetValue) { + return entry.key; + } + } + return null; // Return null if the value is not found. + } /// Inits [Fluster] and all the markers with network images and updates the loading state. void _initMarkers() async { @@ -226,8 +246,16 @@ class InteractiveMapsMarkerState extends State { MapMarker( onTap: () { int tappedIndex = newMarkerPostions.indexOf(markerLocation); + originalIndex = getKeyForValue(indexMapping, tappedIndex); + setFromSameCity = true; + if (getKeyForValue(indexMapping, tappedIndex) == null) { + originalIndex = + getKeyForValue(indexMappingRemaining, tappedIndex); + setFromSameCity = false; + } + pageController.animateToPage( - tappedIndex, + originalIndex!, duration: Duration(milliseconds: 300), curve: Curves.bounceInOut, ); @@ -293,12 +321,22 @@ class InteractiveMapsMarkerState extends State { child: SizedBox( height: widget.itemHeight, child: PageView.builder( - itemCount: widget.items.length, + itemCount: originalIndex != null && !setFromSameCity + ? indexMappingRemaining.length + : indexMapping.length, controller: pageController, - onPageChanged: _pageChanged, - itemBuilder: widget.itemBuilder != null - ? widget.itemBuilder! - : _buildItem, + onPageChanged: (int pageIndex) { + print("called on page change" + pageIndex.toString()); + final NeworiginalIndex = originalIndex != null && !setFromSameCity + ? indexMappingRemaining[pageIndex] + : indexMapping[pageIndex]; + if (NeworiginalIndex != null) { + _pageChanged(NeworiginalIndex); + } + }, + itemBuilder: originalIndex != null && !setFromSameCity + ? widget.restItemBuilder! + : widget.itemBuilder!, ), ), ), @@ -380,7 +418,7 @@ class InteractiveMapsMarkerState extends State { } rebuildMarkers(index); Marker marker = markers.elementAt(index).toMarker(); - + print("index ali taapta" + index.toString()); mapController ?.animateCamera( widget.zoomKeepOnTap @@ -392,7 +430,13 @@ class InteractiveMapsMarkerState extends State { ), ) .then((val) { - setState(() {}); + setState(() { + /* pageController.animateToPage( + index, + duration: Duration(milliseconds: 300), + curve: Curves.bounceInOut, + ); */ + }); }); } catch (e) { print(e); From db5768f0c1633dd841b780a4d6fa16aaa871b934 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sat, 14 Oct 2023 03:23:11 +0100 Subject: [PATCH 59/71] cleaning unused prints and comments --- lib/interactive_maps_controller.dart | 16 +- lib/interactive_maps_marker.dart | 253 +++++---------------------- pubspec.yaml | 2 +- 3 files changed, 49 insertions(+), 222 deletions(-) diff --git a/lib/interactive_maps_controller.dart b/lib/interactive_maps_controller.dart index b6f2e31..dc0bbf5 100644 --- a/lib/interactive_maps_controller.dart +++ b/lib/interactive_maps_controller.dart @@ -11,25 +11,13 @@ class InteractiveMapsController { _state = state; } - void setCurrentIndex(int index) { - if (_state != null) { - _state?.setIndex(index); - } - } - - void rebuild([int? index]) { - _state?.setState(() { - _state?.setIndex(index ?? _state?.currentIndex ?? 0); - }); - } - void reset({int? index}) { Future.delayed(Duration(milliseconds: 200)).then((value) { _state?.pageController.jumpToPage(index ?? _state?.currentIndex ?? 0); - _state?.rebuildMarkers(index ?? _state?.currentIndex ?? 0); getMapController()?.animateCamera( CameraUpdate.newCameraPosition( - CameraPosition(target: _state!.widget.center, zoom: _state!.widget.zoom), + CameraPosition( + target: _state!.widget.center, zoom: _state!.widget.zoom), ), ); }); diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index dbe9f4d..fdfd0e6 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -5,6 +5,7 @@ import 'dart:ui' as ui; import 'package:fluster/fluster.dart'; import "package:flutter/material.dart"; +import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; import 'package:geocoding/geocoding.dart'; @@ -66,19 +67,8 @@ class InteractiveMapsMarker extends StatefulWidget { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } -/* readIcons(); - */ } - /* void readIcons() async { - if (markerIcon == null) - markerIcon = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/marker.png', 100); - if (markerIconSelected == null) - markerIconSelected = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/marker_selected.png', 100); - } */ - Uint8List? markerIcon; Uint8List? markerIconSelected; Uint8List? markerIconDark; @@ -127,6 +117,7 @@ class InteractiveMapsMarkerState extends State { /// Markers loading flag bool _areMarkersLoading = true; bool setFromSameCity = false; + bool markerTapped = false; /// Url image used on normal markers /// Url image used on normal markers @@ -150,80 +141,20 @@ class InteractiveMapsMarkerState extends State { late int? originalIndex = null; @override void initState() { -/* _getUserLocation(); - */ newMarkerPostions = widget.items.map((e) => e.location).toList(); - print("toul el lista initial" + widget.items.toString()); indexMapping = Map.fromIterable(widget.keys, key: (item) => widget.keys.indexOf(item), value: (item) => item); indexMappingRemaining = Map.fromIterable(widget.remainingKeys, key: (item) => widget.remainingKeys.indexOf(item), value: (item) => item); -/* rebuildMarkers(currentIndex); - */ super.initState(); } @override void didChangeDependencies() { -/* rebuildMarkers(currentIndex); - */ super.didChangeDependencies(); } -/* Future _handleLocationPermission() async { - bool serviceEnabled; - LocationPermission permission; - - serviceEnabled = await Geolocator.isLocationServiceEnabled(); - if (!serviceEnabled) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text( - 'Location services are disabled. Please enable the services'))); - return false; - } - permission = await Geolocator.checkPermission(); - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - if (permission == LocationPermission.denied) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Location permissions are denied'))); - return false; - } - } - if (permission == LocationPermission.deniedForever) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text( - 'Location permissions are permanently denied, we cannot request permissions.'))); - return false; - } - return true; - } */ - -/* void _getUserLocation() async { - final hasPermission = await _handleLocationPermission(); - - if (!hasPermission) return; - var position = await GeolocatorPlatform.instance.getCurrentPosition( - locationSettings: const LocationSettings( - accuracy: LocationAccuracy.bestForNavigation)); - List placemarks = await placemarkFromCoordinates( - position.latitude, - position.longitude, - ); - - print(placemarks[0]); - setState(() { - _initialPosition = LatLng(position.latitude, position.longitude); - NewFilteredMarkerPositions = widget.filteredCity != null - ? widget.items - .where((element) => element.ville == widget.filteredCity) - .toList() - : widget.items - .where((element) => element.ville == placemarks[0].locality) - .toList(); - }); - } */ int? getKeyForValue(Map map, int targetValue) { for (var entry in map.entries) { if (entry.value == targetValue) { @@ -248,18 +179,22 @@ class InteractiveMapsMarkerState extends State { int tappedIndex = newMarkerPostions.indexOf(markerLocation); originalIndex = getKeyForValue(indexMapping, tappedIndex); setFromSameCity = true; + setState(() { + markerTapped = true; + }); if (getKeyForValue(indexMapping, tappedIndex) == null) { originalIndex = getKeyForValue(indexMappingRemaining, tappedIndex); setFromSameCity = false; } - - pageController.animateToPage( - originalIndex!, - duration: Duration(milliseconds: 300), - curve: Curves.bounceInOut, - ); - _pageChanged(tappedIndex); + if (_currentZoom > 10) { + pageController.animateToPage( + originalIndex!, + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + ); + } + _pageChanged(tappedIndex!); }, id: newMarkerPostions.indexOf(markerLocation).toString(), position: markerLocation, @@ -279,6 +214,10 @@ class InteractiveMapsMarkerState extends State { Future _updateMarkers([double? updatedZoom]) async { if (_clusterManager == null || updatedZoom == _currentZoom) return; + if (_currentZoom <= 10) { + markerTapped = false; + } else + markerTapped = true; if (updatedZoom != null) { _currentZoom = updatedZoom; @@ -295,7 +234,6 @@ class InteractiveMapsMarkerState extends State { _clusterTextColor, 80, ); - _markers ..clear() ..addAll(updatedMarkers); @@ -320,24 +258,25 @@ class InteractiveMapsMarkerState extends State { padding: widget.itemPadding, child: SizedBox( height: widget.itemHeight, - child: PageView.builder( - itemCount: originalIndex != null && !setFromSameCity - ? indexMappingRemaining.length - : indexMapping.length, - controller: pageController, - onPageChanged: (int pageIndex) { - print("called on page change" + pageIndex.toString()); - final NeworiginalIndex = originalIndex != null && !setFromSameCity - ? indexMappingRemaining[pageIndex] - : indexMapping[pageIndex]; - if (NeworiginalIndex != null) { - _pageChanged(NeworiginalIndex); - } - }, - itemBuilder: originalIndex != null && !setFromSameCity - ? widget.restItemBuilder! - : widget.itemBuilder!, - ), + child: markerTapped + ? PageView.builder( + itemCount: originalIndex != null && !setFromSameCity + ? indexMappingRemaining.length + : indexMapping.length, + controller: pageController, + onPageChanged: (int pageIndex) { + final NeworiginalIndex = + originalIndex != null && !setFromSameCity + ? indexMappingRemaining[pageIndex] + : indexMapping[pageIndex]; + if (NeworiginalIndex != null) { + _pageChanged(NeworiginalIndex); + } + }, + itemBuilder: originalIndex != null && !setFromSameCity + ? widget.restItemBuilder! + : widget.itemBuilder!) + : SizedBox.shrink(), ), ), ) @@ -352,7 +291,6 @@ class InteractiveMapsMarkerState extends State { child: ValueListenableBuilder( valueListenable: selectedMarker, builder: (context, value, child) { - print('Values changed'); return GoogleMap( zoomControlsEnabled: false, markers: _markers, @@ -387,38 +325,22 @@ class InteractiveMapsMarkerState extends State { ); } - Widget? _buildItem(BuildContext context, int i) { - return Transform.scale( - scale: i == currentIndex ? 1 : 0.9, - child: ClipRRect( - borderRadius: BorderRadius.circular(10.0), - child: Container( - height: widget.itemHeight, - decoration: BoxDecoration( - color: Color(0xffffffff), - boxShadow: [ - BoxShadow( - offset: Offset(0.5, 0.5), - color: Color(0xff000000).withOpacity(0.12), - blurRadius: 20, - ), - ], - ), - child: widget.itemContent!(context, i), - ), - ), - ); - } - void _pageChanged(int index) { try { setState(() => currentIndex = index); if (widget.onLastItem != null && index == widget.items.length - 1) { widget.onLastItem!(); } - rebuildMarkers(index); Marker marker = markers.elementAt(index).toMarker(); - print("index ali taapta" + index.toString()); + if (_currentZoom <= 10) { + Future.delayed(Duration(milliseconds: 500), () { + pageController.animateToPage( + index, + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + ); + }); + } mapController ?.animateCamera( widget.zoomKeepOnTap @@ -430,93 +352,10 @@ class InteractiveMapsMarkerState extends State { ), ) .then((val) { - setState(() { - /* pageController.animateToPage( - index, - duration: Duration(milliseconds: 300), - curve: Curves.bounceInOut, - ); */ - }); + setState(() {}); }); } catch (e) { print(e); } } - - /* Future getBytesFromAsset(String path, int width) async { - ByteData data = await rootBundle.load(path); - ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), - targetWidth: width); - ui.FrameInfo fi = await codec.getNextFrame(); - return (await fi.image.toByteData(format: ui.ImageByteFormat.png))! - .buffer - .asUint8List(); - } */ - - Future rebuildMarkers(int index) async { - if (widget.items.length == 0) return; - int current = widget.items[index].id; - - Set _markers = Set(); - if (widget.markerIcon == null) - widget.markerIcon = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/marker.png', 100); - if (widget.markerIconSelected == null) - widget.markerIconSelected = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/marker_selected.png', 100); - if (widget.markerIconDark == null) - widget.markerIconDark = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/marker_darkmode.png', 100); - if (widget.markerIconSelectedDark == null) - widget.markerIconSelectedDark = await getBytesFromAsset( - 'packages/interactive_maps_marker/assets/selectedMarker_darkmode.png', - 100); -/* widget.items.forEach((item) async { - _markers.add( - Marker( - markerId: MarkerId(item.id.toString()), - position: LatLng(item.latitude, item.longitude), - onTap: () { - int tappedIndex = - widget.items.indexWhere((element) => element.id == item.id); - pageController.animateToPage( - tappedIndex, - duration: Duration(milliseconds: 300), - curve: Curves.bounceInOut, - ); - _pageChanged(tappedIndex); - }, - /* icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current - ? BitmapDescriptor.hueGreen - : BitmapDescriptor.hueRed), - // */ - icon: item.id == current - ? BitmapDescriptor.fromBytes( - Theme.of(context).brightness != Brightness.dark - ? widget.markerIconSelected as Uint8List - : widget.markerIconSelectedDark as Uint8List) - : BitmapDescriptor.fromBytes( - Theme.of(context).brightness != Brightness.dark - ? widget.markerIcon as Uint8List - : widget.markerIconDark as Uint8List), - ), - ); - }); - - setState(() { - markers = _markers; - }); */ - // selectedMarker.value = current; - selectedMarker.value = current; - // selectedMarker.notifyListeners(); - } - - void setIndex(int index) { - pageController.animateToPage( - index, - duration: Duration(milliseconds: 300), - curve: Curves.bounceInOut, - ); - _pageChanged(index); - } } diff --git a/pubspec.yaml b/pubspec.yaml index d9d5fd0..fcd5357 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: google_maps_flutter: ^2.2.5 flutter_cache_manager: ^3.0.2 fluster: any - + maps_toolkit: ^3.0.0 dev_dependencies: flutter_test: From 4e29e7ad06e5bf19a7eca791c563cd0edd458646 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sat, 14 Oct 2023 04:04:52 +0100 Subject: [PATCH 60/71] Feature add map auto detect position --- lib/interactive_maps_marker.dart | 24 +- pubspec.lock | 466 +++++++++++++++++++++++++++++++ 2 files changed, 483 insertions(+), 7 deletions(-) create mode 100644 pubspec.lock diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index fdfd0e6..2523abf 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -1,22 +1,17 @@ library interactive_maps_marker; // interactive_marker_list import 'dart:async'; -import 'dart:ui' as ui; import 'package:fluster/fluster.dart'; import "package:flutter/material.dart"; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; -import 'package:geocoding/geocoding.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:interactive_maps_marker/interactive_maps_controller.dart'; export 'package:interactive_maps_marker/interactive_maps_controller.dart'; -import 'package:geolocator/geolocator.dart'; -import './utils.dart'; import 'helpers/map_helper.dart'; import 'helpers/map_marker.dart'; +import 'package:maps_toolkit/maps_toolkit.dart' as mp; class MarkerItem { int id; @@ -133,9 +128,14 @@ class InteractiveMapsMarkerState extends State { /// Color of the cluster text final Color _clusterTextColor = Colors.white; final List markers = []; - Map indexMapping = {}; Map indexMappingRemaining = {}; + List polygonPoints = [ + mp.LatLng(36.53, 10.33), + mp.LatLng(36.92, 10.96), + mp.LatLng(36.66, 11.32), + mp.LatLng(36.34, 10.56), + ]; late List newMarkerPostions = []; late int? originalIndex = null; @@ -243,6 +243,12 @@ class InteractiveMapsMarkerState extends State { }); } + bool isInPolygon(LatLng point) { + final pointMp = mp.LatLng(point.latitude, point.longitude); + + return mp.PolygonUtil.containsLocation(pointMp, polygonPoints, false); + } + @override Widget build(BuildContext context) { return StreamBuilder( @@ -317,6 +323,10 @@ class InteractiveMapsMarkerState extends State { zoom: widget.zoom, ), onCameraMove: (position) => { + isInPolygon(position.target), + print( + "Fi nabeul ? " + isInPolygon(position.target).toString(), + ), _updateMarkers(position.zoom), }, ); diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..bef6e96 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,466 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" + source: hosted + version: "1.17.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fluster: + dependency: "direct main" + description: + name: fluster + sha256: "3807f5d088b7798f0416b8578498046338af98bb4fb922a70e2810b8293963f6" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + url: "https://pub.dev" + source: hosted + version: "2.0.16" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + google_maps: + dependency: transitive + description: + name: google_maps + sha256: "555d5d736339b0478e821167ac521c810d7b51c3b2734e6802a9f046b64ea37a" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + google_maps_flutter: + dependency: "direct main" + description: + name: google_maps_flutter + sha256: d4914cb38b3dcb62c39c085d968d434de0f8050f00f4d9f5ba4a7c7e004934cb + url: "https://pub.dev" + source: hosted + version: "2.5.0" + google_maps_flutter_android: + dependency: transitive + description: + name: google_maps_flutter_android + sha256: e6cb018169e49332f88d23b1d2119b09e8ab4e7d3a1b889a1b7b3fd113e034ba + url: "https://pub.dev" + source: hosted + version: "2.5.1" + google_maps_flutter_ios: + dependency: transitive + description: + name: google_maps_flutter_ios + sha256: "2aa28eb9b9d5dfdce6932a7b7f096430bf83a1a09b4e21e81939351f407c787f" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + google_maps_flutter_platform_interface: + dependency: transitive + description: + name: google_maps_flutter_platform_interface + sha256: a3e9e6896501e566d902c6c69f010834d410ef4b7b5c18b90c77e871c86b7907 + url: "https://pub.dev" + source: hosted + version: "2.4.1" + google_maps_flutter_web: + dependency: transitive + description: + name: google_maps_flutter_web + sha256: "05067c5aa762ebee44b7ef4902a311ed8cf891ef655e2798bae063aa3050c8d9" + url: "https://pub.dev" + source: hosted + version: "0.5.4+1" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: transitive + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + js_wrapping: + dependency: transitive + description: + name: js_wrapping + sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c + url: "https://pub.dev" + source: hosted + version: "0.7.4" + maps_toolkit: + dependency: "direct main" + description: + name: maps_toolkit + sha256: "277877f9505208acacd2a0794ef190e836a5ffee58ebc8efc5b9ca8de50e3e2f" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" + source: hosted + version: "0.12.15" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + url: "https://pub.dev" + source: hosted + version: "2.1.6" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + sanitize_html: + dependency: transitive + description: + name: sanitize_html + sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" + source: hosted + version: "0.5.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + win32: + dependency: transitive + description: + name: win32 + sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + url: "https://pub.dev" + source: hosted + version: "5.0.9" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + url: "https://pub.dev" + source: hosted + version: "1.0.3" +sdks: + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" From 883a2a22e39a80b5d7006601d46ee90c329b7560 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Sat, 14 Oct 2023 05:47:29 +0100 Subject: [PATCH 61/71] add callback to retrive city value --- lib/interactive_maps_marker.dart | 35 ++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 2523abf..cfdcb05 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -37,6 +37,8 @@ class InteractiveMapsMarker extends StatefulWidget { final Alignment contentAlignment; final LatLng? initialPositionFromlist; final String? filteredCity; + final Function(dynamic) onValueReceived; + InteractiveMapsController? controller; VoidCallback? onLastItem; final List keys; @@ -44,6 +46,7 @@ class InteractiveMapsMarker extends StatefulWidget { InteractiveMapsMarker( {required this.items, this.itemBuilder, + required this.onValueReceived, this.restItemBuilder, this.center = const LatLng(0.0, 0.0), this.itemContent, @@ -63,6 +66,9 @@ class InteractiveMapsMarker extends StatefulWidget { throw Exception('itemBuilder or itemContent must be provided'); } } + void sendValueToParent(dynamic data) { + onValueReceived(data); + } Uint8List? markerIcon; Uint8List? markerIconSelected; @@ -113,6 +119,8 @@ class InteractiveMapsMarkerState extends State { bool _areMarkersLoading = true; bool setFromSameCity = false; bool markerTapped = false; + bool showDetailsNabeul = false; + bool showDetailsTunis = false; /// Url image used on normal markers /// Url image used on normal markers @@ -136,6 +144,12 @@ class InteractiveMapsMarkerState extends State { mp.LatLng(36.66, 11.32), mp.LatLng(36.34, 10.56), ]; + List polygonPointsTunis = [ + mp.LatLng(36.93, 10.04), + mp.LatLng(37.00, 10.32), + mp.LatLng(36.64, 10.15), + mp.LatLng(36.72, 10.43), + ]; late List newMarkerPostions = []; late int? originalIndex = null; @@ -243,7 +257,7 @@ class InteractiveMapsMarkerState extends State { }); } - bool isInPolygon(LatLng point) { + bool isInPolygon(LatLng point, List polygonPoints) { final pointMp = mp.LatLng(point.latitude, point.longitude); return mp.PolygonUtil.containsLocation(pointMp, polygonPoints, false); @@ -264,7 +278,7 @@ class InteractiveMapsMarkerState extends State { padding: widget.itemPadding, child: SizedBox( height: widget.itemHeight, - child: markerTapped + child: markerTapped && (showDetailsNabeul || showDetailsTunis) ? PageView.builder( itemCount: originalIndex != null && !setFromSameCity ? indexMappingRemaining.length @@ -323,10 +337,19 @@ class InteractiveMapsMarkerState extends State { zoom: widget.zoom, ), onCameraMove: (position) => { - isInPolygon(position.target), - print( - "Fi nabeul ? " + isInPolygon(position.target).toString(), - ), + setState(() { + showDetailsNabeul = isInPolygon(position.target, polygonPoints); + showDetailsTunis = + isInPolygon(position.target, polygonPointsTunis); + if (showDetailsNabeul) { + widget.sendValueToParent("Nabeul"); + } else if (showDetailsTunis) { + widget.sendValueToParent("Tunis"); + } else { + widget.sendValueToParent(""); + } + }), + print(showDetailsTunis), _updateMarkers(position.zoom), }, ); From 4822debbc4840daedaba291f76aaec2e4d14812a Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 17 Oct 2023 07:15:23 +0100 Subject: [PATCH 62/71] change details pageview to correspponding city --- lib/interactive_maps_marker.dart | 38 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index cfdcb05..fac3ad8 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -37,6 +37,7 @@ class InteractiveMapsMarker extends StatefulWidget { final Alignment contentAlignment; final LatLng? initialPositionFromlist; final String? filteredCity; + final String? originalCity; final Function(dynamic) onValueReceived; InteractiveMapsController? controller; @@ -61,7 +62,8 @@ class InteractiveMapsMarker extends StatefulWidget { required this.keys, required this.remainingKeys, this.initialPositionFromlist, - this.filteredCity}) { + this.filteredCity, + this.originalCity}) { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } @@ -121,6 +123,9 @@ class InteractiveMapsMarkerState extends State { bool markerTapped = false; bool showDetailsNabeul = false; bool showDetailsTunis = false; + bool isNabeul = false; + bool isTunis = false; + String city = ""; /// Url image used on normal markers /// Url image used on normal markers @@ -145,10 +150,10 @@ class InteractiveMapsMarkerState extends State { mp.LatLng(36.34, 10.56), ]; List polygonPointsTunis = [ - mp.LatLng(36.93, 10.04), - mp.LatLng(37.00, 10.32), - mp.LatLng(36.64, 10.15), - mp.LatLng(36.72, 10.43), + mp.LatLng(36.91, 9.95), + mp.LatLng(37.00, 10.25), + mp.LatLng(36.84, 10.47), + mp.LatLng(36.62, 10.18), ]; late List newMarkerPostions = []; @@ -337,19 +342,20 @@ class InteractiveMapsMarkerState extends State { zoom: widget.zoom, ), onCameraMove: (position) => { + isNabeul = isInPolygon(position.target, polygonPoints), + isTunis = isInPolygon(position.target, polygonPointsTunis), + city = isNabeul ? "Nabeul" : (isTunis ? "Tunis" : ""), setState(() { - showDetailsNabeul = isInPolygon(position.target, polygonPoints); - showDetailsTunis = - isInPolygon(position.target, polygonPointsTunis); - if (showDetailsNabeul) { - widget.sendValueToParent("Nabeul"); - } else if (showDetailsTunis) { - widget.sendValueToParent("Tunis"); - } else { - widget.sendValueToParent(""); - } + showDetailsNabeul = isNabeul; + showDetailsTunis = isTunis; + if (city == 'Nabeul' && + widget.originalCity == "Nabeul Governorate") { + print("hani hne "); + setFromSameCity = true; + } else + setFromSameCity = false; }), - print(showDetailsTunis), + widget.sendValueToParent(city), _updateMarkers(position.zoom), }, ); From 1ed3c95572cdabca39081ca8ad999b5eac9a861b Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 17 Oct 2023 08:21:40 +0100 Subject: [PATCH 63/71] stable for last deploy --- lib/interactive_maps_marker.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index fac3ad8..083dbc1 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -46,6 +46,7 @@ class InteractiveMapsMarker extends StatefulWidget { final List remainingKeys; InteractiveMapsMarker( {required this.items, + Key? key, this.itemBuilder, required this.onValueReceived, this.restItemBuilder, @@ -63,7 +64,7 @@ class InteractiveMapsMarker extends StatefulWidget { required this.remainingKeys, this.initialPositionFromlist, this.filteredCity, - this.originalCity}) { + this.originalCity}): super(key: key) { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } @@ -352,10 +353,18 @@ class InteractiveMapsMarkerState extends State { widget.originalCity == "Nabeul Governorate") { print("hani hne "); setFromSameCity = true; + originalIndex = 1; } else setFromSameCity = false; + originalIndex = 1; }), widget.sendValueToParent(city), + /* widget.initialPositionFromlist != null + ? mapController?.animateCamera(CameraUpdate.newLatLng( + LatLng(widget.initialPositionFromlist!.latitude, + widget.initialPositionFromlist!.longitude), + )) + : "", */ _updateMarkers(position.zoom), }, ); From c393c761d23bf66987819b84b15eb8c98da19fa6 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 17 Oct 2023 11:24:18 +0100 Subject: [PATCH 64/71] fix bug city not available to new city selection --- lib/interactive_maps_marker.dart | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 083dbc1..1f312ac 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -46,7 +46,7 @@ class InteractiveMapsMarker extends StatefulWidget { final List remainingKeys; InteractiveMapsMarker( {required this.items, - Key? key, + Key? key, this.itemBuilder, required this.onValueReceived, this.restItemBuilder, @@ -64,7 +64,8 @@ class InteractiveMapsMarker extends StatefulWidget { required this.remainingKeys, this.initialPositionFromlist, this.filteredCity, - this.originalCity}): super(key: key) { + this.originalCity}) + : super(key: key) { if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } @@ -127,6 +128,7 @@ class InteractiveMapsMarkerState extends State { bool isNabeul = false; bool isTunis = false; String city = ""; + String previousCity = ""; /// Url image used on normal markers /// Url image used on normal markers @@ -343,23 +345,46 @@ class InteractiveMapsMarkerState extends State { zoom: widget.zoom, ), onCameraMove: (position) => { - isNabeul = isInPolygon(position.target, polygonPoints), - isTunis = isInPolygon(position.target, polygonPointsTunis), - city = isNabeul ? "Nabeul" : (isTunis ? "Tunis" : ""), setState(() { + isNabeul = isInPolygon(position.target, polygonPoints); + isTunis = isInPolygon(position.target, polygonPointsTunis); + city = isNabeul + ? "Nabeul" + : (isTunis ? "Tunis" : "different city"); + showDetailsNabeul = isNabeul; showDetailsTunis = isTunis; - if (city == 'Nabeul' && - widget.originalCity == "Nabeul Governorate") { + if (city != previousCity) { + if (previousCity == "different city" && city == "Nabeul") { + // City changed from Tunis to Nabeul + print("City changed from Tunis to Nabeul"); + setFromSameCity = true; + originalIndex = 1; + } else if (previousCity == "different city" && + city == "Tunis") { + // City changed from Nabeul to Tunis + setFromSameCity = false; + originalIndex = 1; + print("City changed from Nabeul to Tunis"); + } + + // Update the previousCity + previousCity = city; + } + + /* if ((city == 'Nabeul' && + widget.originalCity == "Nabeul Governorate") || + (city == 'Tunis' && + widget.originalCity == "Tunis Governorate")) { print("hani hne "); setFromSameCity = true; originalIndex = 1; } else setFromSameCity = false; - originalIndex = 1; + originalIndex = 1; */ }), widget.sendValueToParent(city), - /* widget.initialPositionFromlist != null + /* widget.initialPositionFromlist != null ? mapController?.animateCamera(CameraUpdate.newLatLng( LatLng(widget.initialPositionFromlist!.latitude, widget.initialPositionFromlist!.longitude), From e7f147087806c1c07558509a0c275e6a28abaef4 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 17 Oct 2023 11:29:22 +0100 Subject: [PATCH 65/71] delete unused variables --- lib/interactive_maps_marker.dart | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 1f312ac..bc8a7d2 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -371,25 +371,8 @@ class InteractiveMapsMarkerState extends State { // Update the previousCity previousCity = city; } - - /* if ((city == 'Nabeul' && - widget.originalCity == "Nabeul Governorate") || - (city == 'Tunis' && - widget.originalCity == "Tunis Governorate")) { - print("hani hne "); - setFromSameCity = true; - originalIndex = 1; - } else - setFromSameCity = false; - originalIndex = 1; */ }), widget.sendValueToParent(city), - /* widget.initialPositionFromlist != null - ? mapController?.animateCamera(CameraUpdate.newLatLng( - LatLng(widget.initialPositionFromlist!.latitude, - widget.initialPositionFromlist!.longitude), - )) - : "", */ _updateMarkers(position.zoom), }, ); From a98ab7aa9f2abdc126340a05ba77294c30fd031e Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 18 Oct 2023 10:49:54 +0100 Subject: [PATCH 66/71] fix select city not working --- lib/interactive_maps_marker.dart | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index bc8a7d2..fecf5ff 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -354,18 +354,24 @@ class InteractiveMapsMarkerState extends State { showDetailsNabeul = isNabeul; showDetailsTunis = isTunis; + if (widget.filteredCity == "Tunis Governorate" && isTunis) { + print("here"); + setFromSameCity = false; + originalIndex = 1; + } if (city != previousCity) { - if (previousCity == "different city" && city == "Nabeul") { - // City changed from Tunis to Nabeul - print("City changed from Tunis to Nabeul"); - setFromSameCity = true; - originalIndex = 1; - } else if (previousCity == "different city" && - city == "Tunis") { - // City changed from Nabeul to Tunis - setFromSameCity = false; - originalIndex = 1; - print("City changed from Nabeul to Tunis"); + if (previousCity == "different city") { + if (city == "Nabeul") { + // City changed from Tunis to Nabeul + setFromSameCity = true; + originalIndex = 1; + print("City changed from Tunis to Nabeul"); + } else if (city == "Tunis") { + // City changed from Nabeul to Tunis + setFromSameCity = false; + originalIndex = 1; + print("City changed from Nabeul to Tunis"); + } } // Update the previousCity From 261e6f6e40f26d98cc4420c7960017e1a9c19b5a Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Wed, 18 Oct 2023 12:45:26 +0100 Subject: [PATCH 67/71] All map features done --- lib/interactive_maps_marker.dart | 71 +++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index fecf5ff..469688d 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -354,28 +354,67 @@ class InteractiveMapsMarkerState extends State { showDetailsNabeul = isNabeul; showDetailsTunis = isTunis; - if (widget.filteredCity == "Tunis Governorate" && isTunis) { + if (widget.originalCity == "Nabeul Governorate") { + if (widget.filteredCity == "Tunis Governorate" && + isTunis /* widget.originalCity != "Tunis Governorate" */) { + print("here"); + setFromSameCity = false; + originalIndex = 1; + } + } else if (widget.originalCity == "Tunis Governorate") { + if (widget.filteredCity == "Nabeul Governorate" && + isNabeul /* widget.originalCity != "Tunis Governorate" */) { + print("here"); + setFromSameCity = false; + originalIndex = 1; + } + } + /* if (widget.filteredCity == "Nabeul Governorate" && + isTunis && + widget.originalCity != "Nabeul Governorate") { print("here"); setFromSameCity = false; originalIndex = 1; - } - if (city != previousCity) { - if (previousCity == "different city") { - if (city == "Nabeul") { - // City changed from Tunis to Nabeul - setFromSameCity = true; - originalIndex = 1; - print("City changed from Tunis to Nabeul"); - } else if (city == "Tunis") { - // City changed from Nabeul to Tunis - setFromSameCity = false; - originalIndex = 1; - print("City changed from Nabeul to Tunis"); + } */ + if (widget.originalCity == "Tunis Governorate") { + if (city != previousCity) { + if (previousCity == "different city") { + if (city == "Nabeul") { + // City changed from Tunis to Nabeul + setFromSameCity = false; + originalIndex = 1; + print("City changed from Tunis to Nabeul"); + } else if (city == "Tunis") { + // City changed from Nabeul to Tunis + setFromSameCity = true; + originalIndex = 1; + print("City changed from Nabeul to Tunis"); + } } + + // Update the previousCity + previousCity = city; } + } + if (widget.originalCity == "Nabeul Governorate") { + if (city != previousCity) { + if (previousCity == "different city") { + if (city == "Nabeul") { + // City changed from Tunis to Nabeul + setFromSameCity = true; + originalIndex = 1; + print("City changed from Tunis to Nabeul"); + } else if (city == "Tunis") { + // City changed from Nabeul to Tunis + setFromSameCity = false; + originalIndex = 1; + print("City changed from Nabeul to Tunis"); + } + } - // Update the previousCity - previousCity = city; + // Update the previousCity + previousCity = city; + } } }), widget.sendValueToParent(city), From a54707bb252f27662093db59d08394b5b41c6de5 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 14 Nov 2023 18:59:56 +0100 Subject: [PATCH 68/71] hotfix map single marker --- lib/interactive_maps_marker.dart | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 469688d..0aeebe7 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -210,11 +210,13 @@ class InteractiveMapsMarkerState extends State { setFromSameCity = false; } if (_currentZoom > 10) { - pageController.animateToPage( - originalIndex!, - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut, - ); + Future.delayed(Duration(milliseconds: 500), () { + pageController.animateToPage( + originalIndex!, + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + ); + }); } _pageChanged(tappedIndex!); }, @@ -434,13 +436,15 @@ class InteractiveMapsMarkerState extends State { } Marker marker = markers.elementAt(index).toMarker(); if (_currentZoom <= 10) { - Future.delayed(Duration(milliseconds: 500), () { - pageController.animateToPage( - index, - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut, - ); - }); + if (pageController.hasClients) { + Future.delayed(Duration(milliseconds: 500), () { + pageController.animateToPage( + index, + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + ); + }); + } } mapController ?.animateCamera( From ae9944a1a832f2eae32856f12c802448f5dd32eb Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Mon, 25 Dec 2023 20:43:17 +0100 Subject: [PATCH 69/71] change max zoom level to cluster --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 0aeebe7..a874bba 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -108,7 +108,7 @@ class InteractiveMapsMarkerState extends State { final int _minClusterZoom = 0; /// Maximum zoom at which the markers will cluster - final int _maxClusterZoom = 19; + final int _maxClusterZoom = 10; /// [Fluster] instance used to manage the clusters Fluster? _clusterManager; From c3c603cc38bc8fad41181a556ad5665006476667 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 5 Jan 2024 00:09:51 +0100 Subject: [PATCH 70/71] fix double animation --- lib/interactive_maps_marker.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index a874bba..2348a87 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -211,10 +211,10 @@ class InteractiveMapsMarkerState extends State { } if (_currentZoom > 10) { Future.delayed(Duration(milliseconds: 500), () { - pageController.animateToPage( + pageController.jumpToPage( originalIndex!, - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut, + /* duration: Duration(milliseconds: 0), + curve: Curves.bounceInOut, */ ); }); } From 1a42c54300bbe9718765d50e64ac62ca0212de08 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Fri, 5 Jan 2024 15:52:29 +0100 Subject: [PATCH 71/71] fix stuck on one pin --- lib/interactive_maps_marker.dart | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 2348a87..0ceea45 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -210,13 +210,11 @@ class InteractiveMapsMarkerState extends State { setFromSameCity = false; } if (_currentZoom > 10) { - Future.delayed(Duration(milliseconds: 500), () { - pageController.jumpToPage( - originalIndex!, - /* duration: Duration(milliseconds: 0), + pageController.jumpToPage( + originalIndex!, + /* duration: Duration(milliseconds: 0), curve: Curves.bounceInOut, */ - ); - }); + ); } _pageChanged(tappedIndex!); }, @@ -436,15 +434,13 @@ class InteractiveMapsMarkerState extends State { } Marker marker = markers.elementAt(index).toMarker(); if (_currentZoom <= 10) { - if (pageController.hasClients) { - Future.delayed(Duration(milliseconds: 500), () { - pageController.animateToPage( - index, - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut, - ); - }); - } + Future.delayed(Duration(milliseconds: 500), () { + pageController.animateToPage( + index, + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + ); + }); } mapController ?.animateCamera(