diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..2c9cff271 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: "https://docs.fleaflet.dev/supporters#support-us" \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 98de63dcf..dadb08ecd 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: Get Help - url: https://discord.gg/egEGeByf4q + url: https://discord.gg/BwpEsjqMAH about: Don't quite understand how to implement something, or just want to talk? Join the Discord server! - name: Frequently Asked Questions url: https://docs.fleaflet.dev/frequently-asked-questions diff --git a/CHANGELOG.md b/CHANGELOG.md index ce62416fb..46042216c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,84 @@ # Changelog -## [5.0.0] - 2023/06/XX +## [6.0.0] - 2023/09/XX + +**"Photography"** + +Contains the following user-affecting changes: + +- 🟢 Added new `MapCamera` object to reduce scope of `MapController` & re-implemented internal state management - [#1551](https://github.com/fleaflet/flutter_map/pull/1551) with [#1614](https://github.com/fleaflet/flutter_map/pull/1614) +- 🟢 Added support for rotation on desktop with cursor/pointer and keyboard trigger - [#1592](https://github.com/fleaflet/flutter_map/pull/1592) & [#1642](https://github.com/fleaflet/flutter_map/pull/1642) for [#1568](https://github.com/fleaflet/flutter_map/issues/1568) +- 🟢 Added support for cancelling in-loading tiles to `TileProvider`s & refactored methods - [#1622](https://github.com/fleaflet/flutter_map/pull/1622) +- 🟢 Added new `FlutterMap.simple` constructor for super-fast set up - [#1615](https://github.com/fleaflet/flutter_map/pull/1615) +- 🟢 Added support for allowing gestures to bubble down to all layers - [#1615](https://github.com/fleaflet/flutter_map/pull/1615) +- 🟢 Added `minZoom` property to `CameraFit`s - [#1562](https://github.com/fleaflet/flutter_map/pull/1562) +- 🟢 Added `InteractiveFlag.doubleTapDragZoom` - [#1603](https://github.com/fleaflet/flutter_map/pull/1603) +- 🟢 Added in-memory caching support for tiles loaded by `NetworkTileProvider`, to reduce tile loading times and reduce unnecessary tile server requests - [#1629](https://github.com/fleaflet/flutter_map/pull/1629) +- 🟢 Added new options for rotation support to bounds fitting - [#1550](https://github.com/fleaflet/flutter_map/pull/1550) for [#1342](https://github.com/fleaflet/flutter_map/issues/1342) +- 🟢 Added new '{d}' 'dimension' placeholder to `TileLayer.urlTemplate` (via `TileProvider.generateReplacementMap`) to be filled with `TileLayer.tileSize` - [#1665](https://github.com/fleaflet/flutter_map/pull/1665) for [#1664](https://github.com/fleaflet/flutter_map/issues/1664) +- 🟡 Replaced interaction configurations from `MapOptions` with `InteractionOptions` (accessed from `MapOptions`) - [#1551](https://github.com/fleaflet/flutter_map/pull/1551) +- 🟡 Replaced `MapOptions.` `center`, `bounds`, `zoom`, and `rotation` with `initialCenter`, `initialCameraFit`, `initialZoom`, and `initialRotation` - [#1551](https://github.com/fleaflet/flutter_map/pull/1551) +- 🟡 Replaced `MapOptions.maxBounds` with `MapOptions.cameraConstraint` - [#1551](https://github.com/fleaflet/flutter_map/pull/1551) +- 🟡 Replaced `TileLayer.backgroundColor` property with `MapOptions.backgroundColor` to simplify interaction when using multiple tile layers - [#1578](https://github.com/fleaflet/flutter_map/pull/1578) & [#1647](https://github.com/fleaflet/flutter_map/pull/1647) for [#1577](https://github.com/fleaflet/flutter_map/issues/1577) & [#1566](https://github.com/fleaflet/flutter_map/issues/1566) +- 🟡 Replaced `FlutterMap.nonRotatedChildren` with an inverse purpose (usually internal) `MobileLayerTransformer` - [#1615](https://github.com/fleaflet/flutter_map/pull/1615) +- 🟡 Replaced `Marker.anchor` with `Marker.alignment` that uses built-in `Alignment` object - [#1659](https://github.com/fleaflet/flutter_map/pull/1659) +- 🟡 Replaced `Marker.builder` with non-builder `Marker.child` - [#1659](https://github.com/fleaflet/flutter_map/pull/1659) +- 🟡 Changed `TileLayer.retinaMode` behaviour - [#1673](https://github.com/fleaflet/flutter_map/pull/1673) for [#1670](https://github.com/fleaflet/flutter_map/issues/1670) +- 🟡 Changed some default arguments, including `TileLayer.panBuffer` and `MapOptions.cameraConstraint` - multiple PRs +- 🔴 Removed `CustomPoint` in favour of extension methods on `Point` - [#1585](https://github.com/fleaflet/flutter_map/pull/1585) for [#1522](https://github.com/fleaflet/flutter_map/issues/1522) +- 🔴 Removed `MoveAndRotateResult` in favour of a `Record` in format of `({bool moveSuccess, bool rotateSuccess})` - [#1636](https://github.com/fleaflet/flutter_map/pull/1636) +- 🔴 Removed `Anchor`, `AnchorPos`, and all anchor related terminology - [#1659](https://github.com/fleaflet/flutter_map/pull/1659) +- 🔴 Removed dedicated plugins API import - [#1632](https://github.com/fleaflet/flutter_map/pull/1632) + +Contains the following user-affecting bug fixes: + +- Handled exceptions correctly in default image provider if no `fallbackUrl` is defined - [#1555](https://github.com/fleaflet/flutter_map/pull/1555) for [#1554](https://github.com/fleaflet/flutter_map/issues/1554) +- Ignored gestures on closed `RichAnimationWidget` when using `FadeRAWA` - [#1591](https://github.com/fleaflet/flutter_map/pull/1591) for [#1589](https://github.com/fleaflet/flutter_map/issues/1589) +- Avoided setting 'User-Agent' header in `TileProvider` when running on the web, to avoid polluting the debug console with ignorable error messages - [#1677](https://github.com/fleaflet/flutter_map/pull/1677) for [#1654](https://github.com/fleaflet/flutter_map/issues/1654) +- Fixed some `Polygon` edge-cases - [#1598](https://github.com/fleaflet/flutter_map/pull/1598) & [#1599](https://github.com/fleaflet/flutter_map/pull/1599) +- Fixed `TileLayer.reset` failing to load new tiles - [#1620](https://github.com/fleaflet/flutter_map/pull/1620) for [#1619](https://github.com/fleaflet/flutter_map/issues/1619) +- Fixed incorrect `Marker` anchoring when counter-rotating - [#1623](https://github.com/fleaflet/flutter_map/pull/1623) for [#1500](https://github.com/fleaflet/flutter_map/issues/1500) +- Fixed `fitBounds` failing to load new tiles - [#1626](https://github.com/fleaflet/flutter_map/pull/1626) for [#1563](https://github.com/fleaflet/flutter_map/issues/1563) +- Fixed `maxNativeZoom` apparently failing to have any effect - [#1627](https://github.com/fleaflet/flutter_map/pull/1627) for [#1625](https://github.com/fleaflet/flutter_map/issues/1625) +- Fixed `hasGesture` being `false` after double tap zoom in `onPositionChanged` callback - [#1465](https://github.com/fleaflet/flutter_map/issues/1465) for [#1630](https://github.com/fleaflet/flutter_map/pull/1630) +- Fixed failure to attempt tile requests with `fallbackUrl` when an exception is thrown whilst `decode`ing instead of during the network request - [#1648](https://github.com/fleaflet/flutter_map/pull/1648) for [#1649](https://github.com/fleaflet/flutter_map/issues/1649) +- Fixed potential issues with building `RichAttributionWidget` - [#1661](https://github.com/fleaflet/flutter_map/pull/1661) +- Fixed `TileLayer.fallbackUrl` not being attempted when `FlutterMapNetworkImageProvider` failed to decode a non-image - part of [#1662](https://github.com/fleaflet/flutter_map/pull/1662) for [#1667](https://github.com/fleaflet/flutter_map/issues/1667) +- Fixed "PositionedTapDetector2" not correctly transforming global coords to local coords when the `FlutterMap` widget was `Transform`ed - [#1676](https://github.com/fleaflet/flutter_map/pull/1676) for [#1675](https://github.com/fleaflet/flutter_map/issues/1675) + +Contains the following user-affecting performance improvements: + +- Created official plugin to reduce tile loading times when running on web, using [#1622](https://github.com/fleaflet/flutter_map/pull/1622)'s infrastructure - [flutter_map_cancellable_tile_provider](https://pub.dev/packages/flutter_map_cancellable_tile_provider) +- Improved `Polygon` performance when using labels, by increasing batching ability and label location calculations - [#1607](https://github.com/fleaflet/flutter_map/pull/1607) & [#1641](https://github.com/fleaflet/flutter_map/pull/1641) +- Improved `CircleMarker` performance, by using a single `CustomPainter` instance and more efficient draw calls - [#1679](https://github.com/fleaflet/flutter_map/pull/1679) +- Improved internal code style and strictness - [#1594](https://github.com/fleaflet/flutter_map/pull/1594) +- Added logging to warn of potential performance issues and recommend workarounds (such as 'flutter_map_cancellable_tile_provider') - [#1632](https://github.com/fleaflet/flutter_map/pull/1632) + +In other news: + +- There's many new performance and cost-reducing improvments available (some may need some manual work to enable): please see the [Highlights section](https://docs.fleaflet.dev/getting-started/migrating-to-v6#changelog-and-highlights) on the Migrating To v6 docs page! +- We now accept one-time donations! We're extremely grateful for anything you can spare. We'll donate 15% of what we receive to the OpenStreetMap Foundation, as a thanks for their excellent work. For more info, please see [Support Us](https://docs.fleaflet.dev/supporters#support-us). +- We're looking for sponsors and OSS projects that use FM significantly, and want to be advertised! For more info, please see [Showcase](https://docs.fleaflet.dev/showcase). +- We're on the hunt for maintainers to join the team! For more information, please see [the application form](https://docs.fleaflet.dev/credits#apply-to-be-a-maintainer). +- OpenStreetMap Operations has warned us that users should move away from using subdomains with their tile servers, so please do! Appropriate warnings will now be logged in console should you fail to do this. + +Many thanks to these contributors (in no particular order): + +- @jjoelson +- @envomer +- @rorystephenson +- @Robbendebiene +- @ignatz +- @josxha +- @lonelyteapot +- @s6o +- @bramp +- @Alexays +- ... and all the maintainers + +And an additional special thanks to @rorystephenson for investing so much of their time into this project recently - we appreciate it! + +## [5.0.0] - 2023/06/04 **"Dart The Third"** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be4ebdb3e..f8c11770b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,14 @@ # Contributing -'flutter_map' is only as big and useful as it is because of generous open-source contributors! +'flutter_map' is only as great as it is because of generous open-source contributors! We're always happy to receive improvements and fixes, so please submit them whenever you can! A few key points are listed below. > Many feature additions are more suitable for plugins, instead of being added to the core. This is aimed to reduce the future maintenance burden/cost on the maintainers. If we deny your PR for this reason, please do consider publishing a plugin, and we'll be happy to add it to the [Plugins List](https://docs.fleaflet.dev/plugins/list)! See [Making A Plugin](https://docs.fleaflet.dev/plugins/making-a-plugin) for more information. +* If your PR will add a major or breaking change, please discuss it with us first, via the Issue Tracker * Always link your PR to at least one issue, and as many as are resolved * Create a draft PR as soon as work starts, and take it out of draft status when ready for review * Avoid changing the package version or GitHub workflows -* Fix issues reported by the GitHub workflows (such as lints & formatting) +* Fix issues reported by the GitHub workflows (such as formatting) yourself diff --git a/LICENSE b/LICENSE index d5c2bcf01..36c2abfc0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2018-2023, the 'flutter_map' authors and maintainers, loosely based on the original works of 'leaflet.js' by Vladimir Agafonkin & CloudMade +BSD 3-Clause License + +Copyright (c) 2018-2023, the 'flutter_map' authors and maintainers All rights reserved. diff --git a/README.md b/README.md index 8f939d8c3..2805772ff 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ A versatile mapping package for Flutter. Simple and easy to learn, yet completely customizable and configurable, it's the best choice for mapping in your Flutter app. -[![Pub.dev](https://img.shields.io/pub/v/flutter_map.svg?label=Latest+Version)](https://pub.dev/packages/flutter_map) [![Checks & Tests](https://badgen.net/github/checks/fleaflet/flutter_map?label=Checks+%26+Tests&color=orange)](https://github.com/fleaflet/flutter_map/actions?query=branch%3Amaster) [![points](https://img.shields.io/pub/points/flutter_map?logo=flutter)](https://pub.dev/packages/flutter_map/score) -[![stars](https://badgen.net/github/stars/fleaflet/flutter_map?label=stars&color=green&icon=github)](https://github.com/fleaflet/flutter_map/stargazers) [![likes](https://img.shields.io/pub/likes/flutter_map?logo=flutter)](https://pub.dev/packages/flutter_map/score)      [![Open Issues](https://badgen.net/github/open-issues/fleaflet/flutter_map?label=Open+Issues&color=green)](https://GitHub.com/fleaflet/flutter_map/issues) [![Open PRs](https://badgen.net/github/open-prs/fleaflet/flutter_map?label=Open+PRs&color=green)](https://GitHub.com/fleaflet/flutter_map/pulls) +[![pub.dev](https://img.shields.io/pub/v/flutter_map.svg?label=Latest+Version)](https://pub.dev/packages/flutter_map) [![stars](https://badgen.net/github/stars/fleaflet/flutter_map?label=stars&color=green&icon=github)](https://github.com/fleaflet/flutter_map/stargazers) [![likes](https://img.shields.io/pub/likes/flutter_map?logo=flutter)](https://pub.dev/packages/flutter_map/score)      [![Open Issues](https://badgen.net/github/open-issues/fleaflet/flutter_map?label=Open+Issues&color=green)](https://GitHub.com/fleaflet/flutter_map/issues) [![Open PRs](https://badgen.net/github/open-prs/fleaflet/flutter_map?label=Open+PRs&color=green)](https://GitHub.com/fleaflet/flutter_map/pulls) ---- +## [Donate](https://docs.fleaflet.dev/supporters#support-us) + +We now accept one-time donations via Stripe! We're extremely grateful for anything you can spare. We'll donate 15% of what we receive to the OpenStreetMap Foundation, as a thanks for their excellent work. +For more information, details about benefits, and the link to donate, please see [Donate](https://docs.fleaflet.dev/supporters#support-us). ## [Documentation](https://docs.fleaflet.dev/) diff --git a/example/.metadata b/example/.metadata index 4fd81afbb..bead5eefe 100644 --- a/example/.metadata +++ b/example/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: 4b12645012342076800eb701bcdfe18f87da21cf - channel: stable + revision: "2524052335ec76bb03e04ede244b071f1b86d190" + channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 4b12645012342076800eb701bcdfe18f87da21cf - base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - - platform: web - create_revision: 4b12645012342076800eb701bcdfe18f87da21cf - base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + create_revision: 2524052335ec76bb03e04ede244b071f1b86d190 + base_revision: 2524052335ec76bb03e04ede244b071f1b86d190 + - platform: windows + create_revision: 2524052335ec76bb03e04ede244b071f1b86d190 + base_revision: 2524052335ec76bb03e04ede244b071f1b86d190 # User provided section diff --git a/example/assets/ProjectIcon.ico b/example/assets/ProjectIcon.ico deleted file mode 100644 index 07baf3221..000000000 Binary files a/example/assets/ProjectIcon.ico and /dev/null differ diff --git a/example/lib/main.dart b/example/lib/main.dart index e4d59eca8..6a9389732 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_map_example/pages/animated_map_controller.dart'; -import 'package:flutter_map_example/pages/cancellable_tile_provider/cancellable_tile_provider.dart'; +import 'package:flutter_map_example/pages/cancellable_tile_provider.dart'; import 'package:flutter_map_example/pages/circle.dart'; import 'package:flutter_map_example/pages/custom_crs/custom_crs.dart'; import 'package:flutter_map_example/pages/epsg3413_crs.dart'; @@ -19,7 +19,7 @@ import 'package:flutter_map_example/pages/offline_map.dart'; import 'package:flutter_map_example/pages/overlay_image.dart'; import 'package:flutter_map_example/pages/plugin_scalebar.dart'; import 'package:flutter_map_example/pages/plugin_zoombuttons.dart'; -import 'package:flutter_map_example/pages/point_to_latlng.dart'; +import 'package:flutter_map_example/pages/screen_point_to_latlng.dart'; import 'package:flutter_map_example/pages/polygon.dart'; import 'package:flutter_map_example/pages/polyline.dart'; import 'package:flutter_map_example/pages/reset_tile_layer.dart'; @@ -27,7 +27,7 @@ import 'package:flutter_map_example/pages/retina.dart'; import 'package:flutter_map_example/pages/secondary_tap.dart'; import 'package:flutter_map_example/pages/sliding_map.dart'; import 'package:flutter_map_example/pages/stateful_markers.dart'; -import 'package:flutter_map_example/pages/tile_builder_example.dart'; +import 'package:flutter_map_example/pages/tile_builder.dart'; import 'package:flutter_map_example/pages/tile_loading_error_handle.dart'; import 'package:flutter_map_example/pages/wms_tile_layer.dart'; import 'package:url_strategy/url_strategy.dart'; @@ -71,16 +71,17 @@ class MyApp extends StatelessWidget { TileLoadingErrorHandle.route: (context) => const TileLoadingErrorHandle(), TileBuilderPage.route: (context) => const TileBuilderPage(), - InteractiveTestPage.route: (context) => const InteractiveTestPage(), + InteractiveFlagsPage.route: (context) => const InteractiveFlagsPage(), ManyMarkersPage.route: (context) => const ManyMarkersPage(), StatefulMarkersPage.route: (context) => const StatefulMarkersPage(), MapInsideListViewPage.route: (context) => const MapInsideListViewPage(), ResetTileLayerPage.route: (context) => const ResetTileLayerPage(), EPSG4326Page.route: (context) => const EPSG4326Page(), EPSG3413Page.route: (context) => const EPSG3413Page(), - PointToLatLngPage.route: (context) => const PointToLatLngPage(), - LatLngScreenPointTestPage.route: (context) => - const LatLngScreenPointTestPage(), + ScreenPointToLatLngPage.route: (context) => + const ScreenPointToLatLngPage(), + LatLngToScreenPointPage.route: (context) => + const LatLngToScreenPointPage(), FallbackUrlNetworkPage.route: (context) => const FallbackUrlNetworkPage(), SecondaryTapPage.route: (context) => const SecondaryTapPage(), diff --git a/example/lib/pages/animated_map_controller.dart b/example/lib/pages/animated_map_controller.dart index df57a0102..c99ecb828 100644 --- a/example/lib/pages/animated_map_controller.dart +++ b/example/lib/pages/animated_map_controller.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class AnimatedMapControllerPage extends StatefulWidget { - static const String route = 'map_controller_animated'; + static const String route = '/map_controller_animated'; const AnimatedMapControllerPage({Key? key}) : super(key: key); diff --git a/example/lib/pages/cancellable_tile_provider/cancellable_tile_provider.dart b/example/lib/pages/cancellable_tile_provider.dart similarity index 93% rename from example/lib/pages/cancellable_tile_provider/cancellable_tile_provider.dart rename to example/lib/pages/cancellable_tile_provider.dart index d1618f24f..5a6382c33 100644 --- a/example/lib/pages/cancellable_tile_provider/cancellable_tile_provider.dart +++ b/example/lib/pages/cancellable_tile_provider.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map/plugin_api.dart'; -import 'package:flutter_map_example/pages/cancellable_tile_provider/ctp_impl.dart'; +import 'package:flutter_map_cancellable_tile_provider/flutter_map_cancellable_tile_provider.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; diff --git a/example/lib/pages/cancellable_tile_provider/ctp_impl.dart b/example/lib/pages/cancellable_tile_provider/ctp_impl.dart deleted file mode 100644 index b291a9674..000000000 --- a/example/lib/pages/cancellable_tile_provider/ctp_impl.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; - -import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:http/http.dart'; -import 'package:http/retry.dart'; - -class CancellableNetworkTileProvider extends TileProvider { - CancellableNetworkTileProvider({ - super.headers, - BaseClient? httpClient, - }) : httpClient = httpClient ?? RetryClient(Client()); - - final BaseClient httpClient; - - @override - bool get supportsCancelLoading => true; - - @override - ImageProvider getImageWithCancelLoadingSupport( - TileCoordinates coordinates, - TileLayer options, - Future cancelLoading, - ) => - CancellableNetworkImageProvider( - url: getTileUrl(coordinates, options), - fallbackUrl: getTileFallbackUrl(coordinates, options), - headers: headers, - httpClient: httpClient, - cancelLoading: cancelLoading, - ); -} - -class CancellableNetworkImageProvider - extends ImageProvider { - final String url; - final String? fallbackUrl; - final BaseClient httpClient; - final Map headers; - final Future cancelLoading; - - const CancellableNetworkImageProvider({ - required this.url, - required this.fallbackUrl, - required this.headers, - required this.httpClient, - required this.cancelLoading, - }); - - @override - ImageStreamCompleter loadImage( - CancellableNetworkImageProvider key, - ImageDecoderCallback decode, - ) { - final chunkEvents = StreamController(); - - return MultiFrameImageStreamCompleter( - codec: _loadAsync(key, chunkEvents, decode), - chunkEvents: chunkEvents.stream, - scale: 1, - debugLabel: url, - informationCollector: () => [ - DiagnosticsProperty('URL', url), - DiagnosticsProperty('Fallback URL', fallbackUrl), - DiagnosticsProperty('Current provider', key), - ], - ); - } - - @override - Future obtainKey( - ImageConfiguration configuration, - ) => - SynchronousFuture(this); - - Future _loadAsync( - CancellableNetworkImageProvider key, - StreamController chunkEvents, - ImageDecoderCallback decode, { - bool useFallback = false, - }) async { - final cancelToken = CancelToken(); - cancelLoading.then((_) => cancelToken.cancel()); - - final Uint8List bytes; - try { - final dio = Dio(); - final response = await dio.get( - useFallback ? fallbackUrl ?? '' : url, - cancelToken: cancelToken, - options: Options( - headers: headers, - responseType: ResponseType.bytes, - ), - ); - bytes = response.data!; - } on DioException catch (err) { - if (CancelToken.isCancel(err)) { - return decode( - await ImmutableBuffer.fromUint8List(TileProvider.transparentImage), - ); - } - if (useFallback || fallbackUrl == null) rethrow; - return _loadAsync(key, chunkEvents, decode, useFallback: true); - } catch (_) { - if (useFallback || fallbackUrl == null) rethrow; - return _loadAsync(key, chunkEvents, decode, useFallback: true); - } - - return decode(await ImmutableBuffer.fromUint8List(bytes)); - } -} diff --git a/example/lib/pages/circle.dart b/example/lib/pages/circle.dart index 88cc3f8e7..bfd7bac1d 100644 --- a/example/lib/pages/circle.dart +++ b/example/lib/pages/circle.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class CirclePage extends StatelessWidget { - static const String route = 'circle'; + static const String route = '/circle'; const CirclePage({Key? key}) : super(key: key); diff --git a/example/lib/pages/custom_crs/custom_crs.dart b/example/lib/pages/custom_crs/custom_crs.dart index a05e011f4..ae8e2f602 100644 --- a/example/lib/pages/custom_crs/custom_crs.dart +++ b/example/lib/pages/custom_crs/custom_crs.dart @@ -1,14 +1,14 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; import 'package:proj4dart/proj4dart.dart' as proj4; import 'package:url_launcher/url_launcher.dart'; class CustomCrsPage extends StatefulWidget { - static const String route = 'custom_crs'; + static const String route = '/crs_custom'; const CustomCrsPage({Key? key}) : super(key: key); diff --git a/example/lib/pages/epsg3413_crs.dart b/example/lib/pages/epsg3413_crs.dart index 5aad2f0a5..e8e744232 100644 --- a/example/lib/pages/epsg3413_crs.dart +++ b/example/lib/pages/epsg3413_crs.dart @@ -1,14 +1,14 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; import 'package:proj4dart/proj4dart.dart' as proj4; import 'package:url_launcher/url_launcher.dart'; class EPSG3413Page extends StatefulWidget { - static const String route = 'EPSG3413 Page'; + static const String route = '/crs_epsg3413'; const EPSG3413Page({Key? key}) : super(key: key); diff --git a/example/lib/pages/epsg4326_crs.dart b/example/lib/pages/epsg4326_crs.dart index b230ec567..5e543b301 100644 --- a/example/lib/pages/epsg4326_crs.dart +++ b/example/lib/pages/epsg4326_crs.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class EPSG4326Page extends StatelessWidget { - static const String route = 'EPSG4326 Page'; + static const String route = '/crs_epsg4326'; const EPSG4326Page({Key? key}) : super(key: key); diff --git a/example/lib/pages/home.dart b/example/lib/pages/home.dart index 59b3d2dcc..25c5cf533 100644 --- a/example/lib/pages/home.dart +++ b/example/lib/pages/home.dart @@ -99,7 +99,6 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - //appBar: AppBar(title: const Text('Home')), drawer: buildDrawer(context, HomePage.route), body: Stack( children: [ diff --git a/example/lib/pages/interactive_test_page.dart b/example/lib/pages/interactive_test_page.dart index ab22a3ca5..3dada1045 100644 --- a/example/lib/pages/interactive_test_page.dart +++ b/example/lib/pages/interactive_test_page.dart @@ -3,16 +3,16 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; -class InteractiveTestPage extends StatefulWidget { - static const String route = 'interactive_test_page'; +class InteractiveFlagsPage extends StatefulWidget { + static const String route = '/interactive_flags_page'; - const InteractiveTestPage({Key? key}) : super(key: key); + const InteractiveFlagsPage({Key? key}) : super(key: key); @override - State createState() => _InteractiveTestPageState(); + State createState() => _InteractiveFlagsPageState(); } -class _InteractiveTestPageState extends State { +class _InteractiveFlagsPageState extends State { static const availableFlags = { 'Movement': { InteractiveFlag.drag: 'Drag', @@ -39,7 +39,7 @@ class _InteractiveTestPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Interactive Flags')), - drawer: buildDrawer(context, InteractiveTestPage.route), + drawer: buildDrawer(context, InteractiveFlagsPage.route), body: Padding( padding: const EdgeInsets.all(8), child: Column( diff --git a/example/lib/pages/latlng_to_screen_point.dart b/example/lib/pages/latlng_to_screen_point.dart index f5e3e69bd..08bcc9be5 100644 --- a/example/lib/pages/latlng_to_screen_point.dart +++ b/example/lib/pages/latlng_to_screen_point.dart @@ -1,76 +1,94 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; -class LatLngScreenPointTestPage extends StatefulWidget { - static const String route = 'latlng_screen_point_test_page'; +class LatLngToScreenPointPage extends StatefulWidget { + static const String route = '/latlng_to_screen_point'; - const LatLngScreenPointTestPage({Key? key}) : super(key: key); + const LatLngToScreenPointPage({Key? key}) : super(key: key); @override - State createState() { - return _LatLngScreenPointTestPageState(); - } + State createState() => + _LatLngToScreenPointPageState(); } -class _LatLngScreenPointTestPageState extends State { - late final MapController _mapController; +class _LatLngToScreenPointPageState extends State { + static const double pointSize = 65; + + final mapController = MapController(); - Point _textPos = const Point(10, 10); + LatLng? tappedCoords; + Point? tappedPoint; @override void initState() { super.initState(); - _mapController = MapController(); - } - void onMapEvent(MapEvent mapEvent) { - if (mapEvent is! MapEventMove && mapEvent is! MapEventRotate) { - // do not flood console with move and rotate events - debugPrint(mapEvent.toString()); - } + SchedulerBinding.instance + .addPostFrameCallback((_) => ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Tap/click to set coordinate')), + )); } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('LatLng To Screen Point')), - drawer: buildDrawer(context, LatLngScreenPointTestPage.route), + appBar: AppBar(title: const Text('Lat/Lng 🡒 Screen Point')), + drawer: buildDrawer(context, LatLngToScreenPointPage.route), body: Stack( children: [ - Padding( - padding: const EdgeInsets.all(8), - child: FlutterMap( - mapController: _mapController, - options: MapOptions( - onMapEvent: onMapEvent, - onTap: (tapPos, latLng) { - final pt1 = _mapController.camera.latLngToScreenPoint(latLng); - _textPos = Point(pt1.x, pt1.y); - setState(() {}); - }, - initialCenter: const LatLng(51.5, -0.09), - initialZoom: 11, - initialRotation: 0, + FlutterMap( + mapController: mapController, + options: MapOptions( + initialCenter: const LatLng(51.5, -0.09), + initialZoom: 11, + interactionOptions: const InteractionOptions( + flags: ~InteractiveFlag.doubleTapZoom, ), - children: [ - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'dev.fleaflet.flutter_map.example', - ), - ], + onTap: (_, latLng) { + final point = mapController.camera + .latLngToScreenPoint(tappedCoords = latLng); + setState(() => tappedPoint = Point(point.x, point.y)); + }, ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'dev.fleaflet.flutter_map.example', + ), + if (tappedCoords != null) + MarkerLayer( + markers: [ + Marker( + width: pointSize, + height: pointSize, + point: tappedCoords!, + child: const Icon( + Icons.circle, + size: 10, + color: Colors.black, + ), + ) + ], + ), + ], ), - Positioned( - left: _textPos.x.toDouble(), - top: _textPos.y.toDouble(), - width: 20, - height: 20, - child: const FlutterLogo()) + if (tappedPoint != null) + Positioned( + left: tappedPoint!.x - 60 / 2, + top: tappedPoint!.y - 60 / 2, + child: const IgnorePointer( + child: Icon( + Icons.center_focus_strong_outlined, + color: Colors.black, + size: 60, + ), + ), + ) ], ), ); diff --git a/example/lib/pages/many_markers.dart b/example/lib/pages/many_markers.dart index 1441a5340..c2d77b1b7 100644 --- a/example/lib/pages/many_markers.dart +++ b/example/lib/pages/many_markers.dart @@ -5,7 +5,7 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; -const maxMarkersCount = 10000; +const maxMarkersCount = 20000; /// On this page, [maxMarkersCount] markers are randomly generated /// across europe, and then you can limit them with a slider @@ -36,11 +36,9 @@ class _ManyMarkersPageState extends State { allMarkers.add( Marker( point: LatLng(doubleInRange(r, 37, 55), doubleInRange(r, -9, 30)), - child: const Icon( - Icons.circle, - color: Colors.red, - size: 12, - ), + height: 12, + width: 12, + child: ColoredBox(color: Colors.blue[900]!), ), ); } diff --git a/example/lib/pages/map_inside_listview.dart b/example/lib/pages/map_inside_listview.dart index bbb7f2a74..36716d25a 100644 --- a/example/lib/pages/map_inside_listview.dart +++ b/example/lib/pages/map_inside_listview.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/pages/zoombuttons_plugin_option.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class MapInsideListViewPage extends StatelessWidget { - static const String route = 'map_inside_listview'; + static const String route = '/map_inside_listview'; const MapInsideListViewPage({Key? key}) : super(key: key); diff --git a/example/lib/pages/overlay_image.dart b/example/lib/pages/overlay_image.dart index 7d5f6d170..127153d1d 100644 --- a/example/lib/pages/overlay_image.dart +++ b/example/lib/pages/overlay_image.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class OverlayImagePage extends StatelessWidget { - static const String route = 'overlay_image'; + static const String route = '/overlay_image'; const OverlayImagePage({Key? key}) : super(key: key); diff --git a/example/lib/pages/plugin_scalebar.dart b/example/lib/pages/plugin_scalebar.dart index 106c3e896..03640e8ad 100644 --- a/example/lib/pages/plugin_scalebar.dart +++ b/example/lib/pages/plugin_scalebar.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/pages/scale_layer_plugin_option.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; diff --git a/example/lib/pages/plugin_zoombuttons.dart b/example/lib/pages/plugin_zoombuttons.dart index 0bbc90836..21d38e831 100644 --- a/example/lib/pages/plugin_zoombuttons.dart +++ b/example/lib/pages/plugin_zoombuttons.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/pages/zoombuttons_plugin_option.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; diff --git a/example/lib/pages/point_to_latlng.dart b/example/lib/pages/point_to_latlng.dart deleted file mode 100644 index bdb0f6be8..000000000 --- a/example/lib/pages/point_to_latlng.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map_example/widgets/drawer.dart'; -import 'package:latlong2/latlong.dart'; - -class PointToLatLngPage extends StatefulWidget { - static const String route = 'point_to_latlng'; - - const PointToLatLngPage({Key? key}) : super(key: key); - - @override - PointToLatlngPage createState() { - return PointToLatlngPage(); - } -} - -class PointToLatlngPage extends State { - late final MapController mapController = MapController(); - final pointSize = 40.0; - final pointY = 200.0; - - LatLng? latLng; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((_) { - updatePoint(null, context); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('PointToLatlng')), - floatingActionButton: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - FloatingActionButton( - heroTag: 'rotate', - child: const Icon(Icons.rotate_right), - onPressed: () => mapController.rotate(60), - ), - const SizedBox(height: 15), - FloatingActionButton( - heroTag: 'cancel', - child: const Icon(Icons.cancel), - onPressed: () => mapController.rotate(0), - ), - ], - ), - drawer: buildDrawer(context, PointToLatLngPage.route), - body: Stack( - children: [ - FlutterMap( - mapController: mapController, - options: MapOptions( - onMapEvent: (event) { - updatePoint(null, context); - }, - initialCenter: const LatLng(51.5, -0.09), - initialZoom: 5, - minZoom: 3, - ), - children: [ - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'dev.fleaflet.flutter_map.example', - ), - if (latLng != null) - MarkerLayer( - markers: [ - Marker( - width: pointSize, - height: pointSize, - point: latLng!, - child: const FlutterLogo(), - ) - ], - ), - ], - ), - Container( - color: Colors.white, - height: 60, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'flutter logo (${latLng?.latitude.toStringAsPrecision(4)},${latLng?.longitude.toStringAsPrecision(4)})', - textAlign: TextAlign.center, - ), - ], - ))), - Positioned( - top: pointY - pointSize / 2, - left: _getPointX(context) - pointSize / 2, - child: Icon(Icons.crop_free, size: pointSize)) - ], - ), - ); - } - - void updatePoint(MapEvent? event, BuildContext context) { - final pointX = _getPointX(context); - setState(() { - latLng = mapController.camera.pointToLatLng(Point(pointX, pointY)); - }); - } - - double _getPointX(BuildContext context) { - return MediaQuery.of(context).size.width / 2; - } -} diff --git a/example/lib/pages/polygon.dart b/example/lib/pages/polygon.dart index 0911737cd..891647f5b 100644 --- a/example/lib/pages/polygon.dart +++ b/example/lib/pages/polygon.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class PolygonPage extends StatelessWidget { - static const String route = 'polygon'; + static const String route = '/polygon'; const PolygonPage({Key? key}) : super(key: key); diff --git a/example/lib/pages/polyline.dart b/example/lib/pages/polyline.dart index ce6e170e9..5acc40753 100644 --- a/example/lib/pages/polyline.dart +++ b/example/lib/pages/polyline.dart @@ -4,7 +4,7 @@ import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; class PolylinePage extends StatefulWidget { - static const String route = 'polyline'; + static const String route = '/polyline'; const PolylinePage({Key? key}) : super(key: key); diff --git a/example/lib/pages/retina.dart b/example/lib/pages/retina.dart index 126e8a7d3..c5035707a 100644 --- a/example/lib/pages/retina.dart +++ b/example/lib/pages/retina.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/example/lib/pages/scale_layer_plugin_option.dart b/example/lib/pages/scale_layer_plugin_option.dart index 6bb64d146..b8196d1f4 100644 --- a/example/lib/pages/scale_layer_plugin_option.dart +++ b/example/lib/pages/scale_layer_plugin_option.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/pages/scalebar_utils.dart' as util; class ScaleLayerPluginOption { diff --git a/example/lib/pages/screen_point_to_latlng.dart b/example/lib/pages/screen_point_to_latlng.dart new file mode 100644 index 000000000..22e06ed45 --- /dev/null +++ b/example/lib/pages/screen_point_to_latlng.dart @@ -0,0 +1,107 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_example/widgets/drawer.dart'; +import 'package:latlong2/latlong.dart'; + +class ScreenPointToLatLngPage extends StatefulWidget { + static const String route = '/screen_point_to_latlng'; + + const ScreenPointToLatLngPage({Key? key}) : super(key: key); + + @override + PointToLatlngPage createState() { + return PointToLatlngPage(); + } +} + +class PointToLatlngPage extends State { + static const double pointSize = 65; + static const double pointY = 250; + + final mapController = MapController(); + + LatLng? latLng; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) => updatePoint(context)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Screen Point 🡒 Lat/Lng')), + drawer: buildDrawer(context, ScreenPointToLatLngPage.route), + body: Stack( + children: [ + FlutterMap( + mapController: mapController, + options: MapOptions( + onPositionChanged: (_, __) => updatePoint(context), + initialCenter: const LatLng(51.5, -0.09), + initialZoom: 5, + minZoom: 3, + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'dev.fleaflet.flutter_map.example', + ), + if (latLng != null) + MarkerLayer( + markers: [ + Marker( + width: pointSize, + height: pointSize, + point: latLng!, + child: const Icon( + Icons.circle, + size: 10, + color: Colors.black, + ), + ) + ], + ), + ], + ), + Positioned( + top: pointY - pointSize / 2, + left: _getPointX(context) - pointSize / 2, + child: const IgnorePointer( + child: Icon( + Icons.center_focus_strong_outlined, + size: pointSize, + color: Colors.black, + ), + ), + ), + Positioned( + top: pointY + pointSize / 2 + 6, + left: 0, + right: 0, + child: IgnorePointer( + child: Text( + '(${latLng?.latitude.toStringAsFixed(3)},${latLng?.longitude.toStringAsFixed(3)})', + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ) + ], + ), + ); + } + + void updatePoint(BuildContext context) => setState(() => latLng = + mapController.camera.pointToLatLng(Point(_getPointX(context), pointY))); + + double _getPointX(BuildContext context) => + MediaQuery.sizeOf(context).width / 2; +} diff --git a/example/lib/pages/tile_builder.dart b/example/lib/pages/tile_builder.dart new file mode 100644 index 000000000..d582b3981 --- /dev/null +++ b/example/lib/pages/tile_builder.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_example/widgets/drawer.dart'; +import 'package:latlong2/latlong.dart'; + +class TileBuilderPage extends StatefulWidget { + static const String route = '/tile_builder'; + + const TileBuilderPage({Key? key}) : super(key: key); + + @override + _TileBuilderPageState createState() => _TileBuilderPageState(); +} + +class _TileBuilderPageState extends State { + bool enableGrid = true; + bool showCoordinates = true; + bool showLoadingTime = true; + bool darkMode = true; + + // mix of [coordinateDebugTileBuilder] and [loadingTimeDebugTileBuilder] from tile_builder.dart + Widget tileBuilder(BuildContext context, Widget tileWidget, TileImage tile) { + final coords = tile.coordinates; + + return DecoratedBox( + decoration: BoxDecoration( + border: enableGrid ? Border.all(width: 2, color: Colors.white) : null, + ), + position: DecorationPosition.foreground, + child: Stack( + fit: StackFit.passthrough, + children: [ + tileWidget, + if (showLoadingTime || showCoordinates) + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (showCoordinates) + Text( + '${coords.x.floor()} : ${coords.y.floor()} : ${coords.z.floor()}', + style: Theme.of(context).textTheme.headlineSmall, + ), + if (showLoadingTime) + Text( + tile.loadFinishedAt == null + ? 'Loading' + // sometimes result is negative which shouldn't happen, abs() corrects it + : '${(tile.loadFinishedAt!.millisecond - tile.loadStarted!.millisecond).abs()} ms', + style: Theme.of(context).textTheme.headlineSmall, + ), + ], + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Tile Builder')), + drawer: buildDrawer(context, TileBuilderPage.route), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: FittedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Tooltip( + message: 'Overlay Tile Grid', + child: Icon(Icons.grid_4x4), + ), + Switch.adaptive( + value: enableGrid, + onChanged: (v) => setState(() => enableGrid = v), + ), + const SizedBox.square(dimension: 12), + const Tooltip( + message: 'Show Coordinates', + child: Icon(Icons.location_on), + ), + Switch.adaptive( + value: showCoordinates, + onChanged: (v) => setState(() => showCoordinates = v), + ), + const SizedBox.square(dimension: 12), + const Tooltip( + message: 'Show Tile Loading Duration', + child: Icon(Icons.timer_outlined), + ), + Switch.adaptive( + value: showLoadingTime, + onChanged: (v) => setState(() => showLoadingTime = v), + ), + const SizedBox.square(dimension: 12), + const Tooltip( + message: 'Simulate Dark Mode', + child: Icon(Icons.dark_mode), + ), + Switch.adaptive( + value: darkMode, + onChanged: (v) => setState(() => darkMode = v), + ), + ], + ), + ), + ), + Expanded( + child: FlutterMap( + options: const MapOptions( + initialCenter: LatLng(51.5, -0.09), + initialZoom: 5, + ), + children: [ + _darkModeContainerIfEnabled( + TileLayer( + urlTemplate: + 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'dev.fleaflet.flutter_map.example', + tileBuilder: tileBuilder, + ), + ), + const MarkerLayer( + markers: [ + Marker( + width: 80, + height: 80, + point: LatLng(51.5, -0.09), + child: FlutterLogo( + key: ObjectKey(Colors.blue), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + Widget _darkModeContainerIfEnabled(Widget child) { + if (!darkMode) return child; + + return darkModeTilesContainerBuilder(context, child); + } +} diff --git a/example/lib/pages/tile_builder_example.dart b/example/lib/pages/tile_builder_example.dart deleted file mode 100644 index 1de53c88a..000000000 --- a/example/lib/pages/tile_builder_example.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map_example/widgets/drawer.dart'; -import 'package:latlong2/latlong.dart'; - -class TileBuilderPage extends StatefulWidget { - static const String route = '/tile_builder_example'; - - const TileBuilderPage({Key? key}) : super(key: key); - - @override - _TileBuilderPageState createState() => _TileBuilderPageState(); -} - -class _TileBuilderPageState extends State { - bool darkMode = false; - bool loadingTime = false; - bool showCoordinates = false; - bool grid = false; - int panBuffer = 0; - - // mix of [coordinateDebugTileBuilder] and [loadingTimeDebugTileBuilder] from tile_builder.dart - Widget tileBuilder(BuildContext context, Widget tileWidget, TileImage tile) { - final coords = tile.coordinates; - - return Container( - decoration: BoxDecoration( - border: grid ? Border.all() : null, - ), - child: Stack( - fit: StackFit.passthrough, - children: [ - tileWidget, - if (loadingTime || showCoordinates) - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (showCoordinates) - Text( - '${coords.x.floor()} : ${coords.y.floor()} : ${coords.z.floor()}', - style: Theme.of(context).textTheme.headlineSmall, - ), - if (loadingTime) - Text( - tile.loadFinishedAt == null - ? 'Loading' - // sometimes result is negative which shouldn't happen, abs() corrects it - : '${(tile.loadFinishedAt!.millisecond - tile.loadStarted!.millisecond).abs()} ms', - style: Theme.of(context).textTheme.headlineSmall, - ), - ], - ), - ], - ), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Tile builder')), - drawer: buildDrawer(context, TileBuilderPage.route), - floatingActionButton: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - FloatingActionButton.extended( - heroTag: 'grid', - label: Text( - grid ? 'Hide grid' : 'Show grid', - textAlign: TextAlign.center, - ), - icon: Icon(grid ? Icons.grid_off : Icons.grid_on), - onPressed: () => setState(() => grid = !grid), - ), - const SizedBox(height: 8), - FloatingActionButton.extended( - heroTag: 'coords', - label: Text( - showCoordinates ? 'Hide coords' : 'Show coords', - textAlign: TextAlign.center, - ), - icon: Icon(showCoordinates ? Icons.unarchive : Icons.bug_report), - onPressed: () => setState(() => showCoordinates = !showCoordinates), - ), - const SizedBox(height: 8), - FloatingActionButton.extended( - heroTag: 'ms', - label: Text( - loadingTime ? 'Hide loading time' : 'Show loading time', - textAlign: TextAlign.center, - ), - icon: Icon(loadingTime ? Icons.timer_off : Icons.timer), - onPressed: () => setState(() => loadingTime = !loadingTime), - ), - const SizedBox(height: 8), - FloatingActionButton.extended( - heroTag: 'dark-light', - label: Text( - darkMode ? 'Light mode' : 'Dark mode', - textAlign: TextAlign.center, - ), - icon: Icon(darkMode ? Icons.brightness_high : Icons.brightness_2), - onPressed: () => setState(() => darkMode = !darkMode), - ), - const SizedBox(height: 8), - FloatingActionButton.extended( - heroTag: 'panBuffer', - label: Text( - panBuffer == 0 ? 'panBuffer off' : 'panBuffer on', - textAlign: TextAlign.center, - ), - icon: Icon(grid ? Icons.grid_off : Icons.grid_on), - onPressed: () => setState(() { - panBuffer = panBuffer == 0 ? 1 : 0; - }), - ), - ], - ), - body: Padding( - padding: const EdgeInsets.all(8), - child: FlutterMap( - options: const MapOptions( - initialCenter: LatLng(51.5, -0.09), - initialZoom: 5, - ), - children: [ - _darkModeContainerIfEnabled( - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'dev.fleaflet.flutter_map.example', - tileBuilder: tileBuilder, - panBuffer: panBuffer, - ), - ), - const MarkerLayer( - markers: [ - Marker( - width: 80, - height: 80, - point: LatLng(51.5, -0.09), - child: FlutterLogo(key: ObjectKey(Colors.blue)), - ), - ], - ), - ], - ), - ), - ); - } - - Widget _darkModeContainerIfEnabled(Widget child) { - if (!darkMode) return child; - - return darkModeTilesContainerBuilder(context, child); - } -} diff --git a/example/lib/pages/wms_tile_layer.dart b/example/lib/pages/wms_tile_layer.dart index d3bbdf5c6..36a274e3c 100644 --- a/example/lib/pages/wms_tile_layer.dart +++ b/example/lib/pages/wms_tile_layer.dart @@ -5,7 +5,7 @@ import 'package:latlong2/latlong.dart'; import 'package:url_launcher/url_launcher.dart'; class WMSLayerPage extends StatelessWidget { - static const String route = 'WMS layer'; + static const String route = '/wms_layer'; const WMSLayerPage({Key? key}) : super(key: key); diff --git a/example/lib/pages/zoombuttons_plugin_option.dart b/example/lib/pages/zoombuttons_plugin_option.dart index c3ce34990..0f0a0cadd 100644 --- a/example/lib/pages/zoombuttons_plugin_option.dart +++ b/example/lib/pages/zoombuttons_plugin_option.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; class FlutterMapZoomButtons extends StatelessWidget { final double minZoom; diff --git a/example/lib/widgets/drawer.dart b/example/lib/widgets/drawer.dart index c76c9214e..a74a58c36 100644 --- a/example/lib/widgets/drawer.dart +++ b/example/lib/widgets/drawer.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_map_example/pages/animated_map_controller.dart'; -import 'package:flutter_map_example/pages/cancellable_tile_provider/cancellable_tile_provider.dart'; +import 'package:flutter_map_example/pages/cancellable_tile_provider.dart'; import 'package:flutter_map_example/pages/circle.dart'; import 'package:flutter_map_example/pages/custom_crs/custom_crs.dart'; import 'package:flutter_map_example/pages/epsg3413_crs.dart'; @@ -20,7 +20,7 @@ import 'package:flutter_map_example/pages/offline_map.dart'; import 'package:flutter_map_example/pages/overlay_image.dart'; import 'package:flutter_map_example/pages/plugin_scalebar.dart'; import 'package:flutter_map_example/pages/plugin_zoombuttons.dart'; -import 'package:flutter_map_example/pages/point_to_latlng.dart'; +import 'package:flutter_map_example/pages/screen_point_to_latlng.dart'; import 'package:flutter_map_example/pages/polygon.dart'; import 'package:flutter_map_example/pages/polyline.dart'; import 'package:flutter_map_example/pages/reset_tile_layer.dart'; @@ -28,7 +28,7 @@ import 'package:flutter_map_example/pages/retina.dart'; import 'package:flutter_map_example/pages/secondary_tap.dart'; import 'package:flutter_map_example/pages/sliding_map.dart'; import 'package:flutter_map_example/pages/stateful_markers.dart'; -import 'package:flutter_map_example/pages/tile_builder_example.dart'; +import 'package:flutter_map_example/pages/tile_builder.dart'; import 'package:flutter_map_example/pages/tile_loading_error_handle.dart'; import 'package:flutter_map_example/pages/wms_tile_layer.dart'; @@ -134,7 +134,7 @@ Drawer buildDrawer(BuildContext context, String currentRoute) { _buildMenuItem( context, const Text('Interactive Flags'), - InteractiveTestPage.route, + InteractiveFlagsPage.route, currentRoute, ), const Divider(), @@ -177,14 +177,14 @@ Drawer buildDrawer(BuildContext context, String currentRoute) { ), _buildMenuItem( context, - const Text('Moving Marker'), - MovingMarkersPage.route, + const Text('Many Circles'), + ManyCirclesPage.route, currentRoute, ), _buildMenuItem( context, - const Text('Many Circles'), - ManyCirclesPage.route, + const Text('Moving Marker'), + MovingMarkersPage.route, currentRoute, ), const Divider(), @@ -268,14 +268,14 @@ Drawer buildDrawer(BuildContext context, String currentRoute) { const Divider(), _buildMenuItem( context, - const Text('Screen Point -> LatLng'), - PointToLatLngPage.route, + const Text('Screen Point 🡒 LatLng'), + ScreenPointToLatLngPage.route, currentRoute, ), _buildMenuItem( context, - const Text('LatLng -> Screen Point'), - LatLngScreenPointTestPage.route, + const Text('LatLng 🡒 Screen Point'), + LatLngToScreenPointPage.route, currentRoute, ), ], diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 7bd7ae8fc..3205a9379 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -11,17 +11,21 @@ dependencies: flutter: sdk: flutter flutter_map: - path: ../ + flutter_map_cancellable_tile_provider: latlong2: ^0.9.0 proj4dart: ^2.1.0 - url_launcher: ^6.1.10 - shared_preferences: ^2.1.1 + url_launcher: ^6.1.14 + shared_preferences: ^2.2.1 url_strategy: ^0.2.0 http: ^1.1.0 - dio: ^5.3.2 + +dependency_overrides: + flutter_map_cancellable_tile_provider: + flutter_map: + path: ../ dev_dependencies: - flutter_lints: ^2.0.1 + flutter_lints: ^2.0.3 flutter_test: sdk: flutter diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt index c0270746b..c09389c56 100644 --- a/example/windows/CMakeLists.txt +++ b/example/windows/CMakeLists.txt @@ -8,7 +8,7 @@ set(BINARY_NAME "example") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. -cmake_policy(SET CMP0063 NEW) +cmake_policy(VERSION 3.14...3.25) # Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) @@ -52,6 +52,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) # Application build; see runner/CMakeLists.txt. add_subdirectory("runner") + # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt index 17411a8ab..394917c05 100644 --- a/example/windows/runner/CMakeLists.txt +++ b/example/windows/runner/CMakeLists.txt @@ -33,6 +33,7 @@ target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") # Add dependency libraries and include directories. Add any application-specific # dependencies here. target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") # Run the Flutter tool portions of the build. This must not be removed. diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp index b43b9095e..955ee3038 100644 --- a/example/windows/runner/flutter_window.cpp +++ b/example/windows/runner/flutter_window.cpp @@ -26,6 +26,16 @@ bool FlutterWindow::OnCreate() { } RegisterPlugins(flutter_controller_->engine()); SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + return true; } diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp index ee928474d..48a091988 100644 --- a/example/windows/runner/main.cpp +++ b/example/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"flutter_map Demo", origin, size)) { + if (!window.Create(L"flutter_map Demo", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/example/windows/runner/resources/app_icon.ico b/example/windows/runner/resources/app_icon.ico index 07baf3221..bfa699324 100644 Binary files a/example/windows/runner/resources/app_icon.ico and b/example/windows/runner/resources/app_icon.ico differ diff --git a/example/windows/runner/utils.cpp b/example/windows/runner/utils.cpp index f5bf9fa0f..b2b08734d 100644 --- a/example/windows/runner/utils.cpp +++ b/example/windows/runner/utils.cpp @@ -47,16 +47,17 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { } int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); std::string utf8_string; - if (target_length == 0 || target_length > utf8_string.max_size()) { + if (target_length <= 0 || target_length > utf8_string.max_size()) { return utf8_string; } utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); + input_length, utf8_string.data(), target_length, nullptr, nullptr); if (converted_length == 0) { return std::string(); } diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp index c10f08dc7..60608d0fe 100644 --- a/example/windows/runner/win32_window.cpp +++ b/example/windows/runner/win32_window.cpp @@ -1,13 +1,31 @@ #include "win32_window.h" +#include #include #include "resource.h" namespace { +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + // The number of Win32Window objects that currently exist. static int g_active_window_count = 0; @@ -31,8 +49,8 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) { GetProcAddress(user32_module, "EnableNonClientDpiScaling")); if (enable_non_client_dpi_scaling != nullptr) { enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); } + FreeLibrary(user32_module); } } // namespace @@ -42,7 +60,7 @@ class WindowClassRegistrar { public: ~WindowClassRegistrar() = default; - // Returns the singleton registar instance. + // Returns the singleton registrar instance. static WindowClassRegistrar* GetInstance() { if (!instance_) { instance_ = new WindowClassRegistrar(); @@ -102,9 +120,9 @@ Win32Window::~Win32Window() { Destroy(); } -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { Destroy(); const wchar_t* window_class = @@ -117,7 +135,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title, double scale_factor = dpi / 96.0; HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, GetModuleHandle(nullptr), this); @@ -126,9 +144,15 @@ bool Win32Window::CreateAndShow(const std::wstring& title, return false; } + UpdateTheme(window); + return OnCreate(); } +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + // static LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, @@ -188,6 +212,10 @@ Win32Window::MessageHandler(HWND hwnd, SetFocus(child_content_); } return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; } return DefWindowProc(window_handle_, message, wparam, lparam); @@ -243,3 +271,18 @@ bool Win32Window::OnCreate() { void Win32Window::OnDestroy() { // No-op; provided for subclasses. } + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/example/windows/runner/win32_window.h b/example/windows/runner/win32_window.h index 17ba43112..e901dde68 100644 --- a/example/windows/runner/win32_window.h +++ b/example/windows/runner/win32_window.h @@ -28,15 +28,16 @@ class Win32Window { Win32Window(); virtual ~Win32Window(); - // Creates and shows a win32 window with |title| and position and size using + // Creates a win32 window with |title| that is positioned and sized using // |origin| and |size|. New windows are created on the default monitor. Window // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); // Release OS resources associated with window. void Destroy(); @@ -76,7 +77,7 @@ class Win32Window { // OS callback called by message pump. Handles the WM_NCCREATE message which // is passed when the non-client area is being created and enables automatic // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by + // responds to changes in DPI. All other messages are handled by // MessageHandler. static LRESULT CALLBACK WndProc(HWND const window, UINT const message, @@ -86,6 +87,9 @@ class Win32Window { // Retrieves a class instance pointer for |window| static Win32Window* GetThisFromHandle(HWND const window) noexcept; + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + bool quit_on_close_ = false; // window handle for top level window. diff --git a/lib/flutter_map.dart b/lib/flutter_map.dart index 80a332977..6e310529b 100644 --- a/lib/flutter_map.dart +++ b/lib/flutter_map.dart @@ -1,3 +1,19 @@ +/// # flutter_map +/// +/// A versatile mapping package for Flutter. Simple and easy to learn, yet +/// completely customizable and configurable, it's the best choice for mapping in +/// your Flutter app. +/// +/// --- +/// +/// * Documentation: +/// * API Reference: +/// +/// --- +/// +/// * github.com: +/// * pub.dev: +/// * discord.gg: library flutter_map; export 'package:flutter_map/src/geo/crs.dart'; @@ -37,9 +53,9 @@ export 'package:flutter_map/src/map/options/cursor_keyboard_rotation.dart'; export 'package:flutter_map/src/map/options/interaction.dart'; export 'package:flutter_map/src/map/options/options.dart'; export 'package:flutter_map/src/map/widget.dart'; +export 'package:flutter_map/src/misc/bounds.dart'; export 'package:flutter_map/src/misc/center_zoom.dart'; export 'package:flutter_map/src/misc/fit_bounds_options.dart'; export 'package:flutter_map/src/misc/move_and_rotate_result.dart'; export 'package:flutter_map/src/misc/point_extensions.dart'; export 'package:flutter_map/src/misc/position.dart'; -export 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart'; diff --git a/lib/plugin_api.dart b/lib/plugin_api.dart deleted file mode 100644 index 628c08fde..000000000 --- a/lib/plugin_api.dart +++ /dev/null @@ -1,5 +0,0 @@ -library flutter_map.plugin_api; - -export 'package:flutter_map/flutter_map.dart'; -export 'package:flutter_map/src/misc/private/bounds.dart'; -export 'package:flutter_map/src/misc/private/util.dart'; diff --git a/lib/src/geo/crs.dart b/lib/src/geo/crs.dart index 63bfc0e66..1abdd3f3e 100644 --- a/lib/src/geo/crs.dart +++ b/lib/src/geo/crs.dart @@ -1,7 +1,7 @@ import 'dart:math' as math hide Point; import 'dart:math' show Point; -import 'package:flutter_map/src/misc/private/bounds.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:latlong2/latlong.dart'; import 'package:meta/meta.dart'; import 'package:proj4dart/proj4dart.dart' as proj4; diff --git a/lib/src/gestures/flutter_map_interactive_viewer.dart b/lib/src/gestures/flutter_map_interactive_viewer.dart index b3fcb4352..46a634a25 100644 --- a/lib/src/gestures/flutter_map_interactive_viewer.dart +++ b/lib/src/gestures/flutter_map_interactive_viewer.dart @@ -8,13 +8,13 @@ import 'package:flutter_map/src/gestures/interactive_flag.dart'; import 'package:flutter_map/src/gestures/latlng_tween.dart'; import 'package:flutter_map/src/gestures/map_events.dart'; import 'package:flutter_map/src/gestures/multi_finger_gesture.dart'; +import 'package:flutter_map/src/gestures/positioned_tap_detector_2.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; import 'package:flutter_map/src/map/controller/internal.dart'; import 'package:flutter_map/src/map/options/cursor_keyboard_rotation.dart'; import 'package:flutter_map/src/map/options/interaction.dart'; import 'package:flutter_map/src/map/options/options.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart'; import 'package:latlong2/latlong.dart'; /// Applies interactions (gestures/scroll/taps etc) to the current [MapCamera] diff --git a/lib/src/gestures/interactive_flag.dart b/lib/src/gestures/interactive_flag.dart index 30541e85a..63674d542 100644 --- a/lib/src/gestures/interactive_flag.dart +++ b/lib/src/gestures/interactive_flag.dart @@ -1,5 +1,3 @@ -import 'package:flutter_map/flutter_map.dart'; - /// Use [InteractiveFlag] to disable / enable certain events Use /// [InteractiveFlag.all] to enable all events, use [InteractiveFlag.none] to /// disable all events diff --git a/lib/src/misc/private/positioned_tap_detector_2.dart b/lib/src/gestures/positioned_tap_detector_2.dart similarity index 100% rename from lib/src/misc/private/positioned_tap_detector_2.dart rename to lib/src/gestures/positioned_tap_detector_2.dart diff --git a/lib/src/layer/marker_layer.dart b/lib/src/layer/marker_layer.dart index 57480dba4..490ad7d73 100644 --- a/lib/src/layer/marker_layer.dart +++ b/lib/src/layer/marker_layer.dart @@ -3,8 +3,8 @@ import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/src/layer/general/mobile_layer_transformer.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:latlong2/latlong.dart'; /// A container for a [child] widget located at a geographic coordinate [point] diff --git a/lib/src/layer/overlay_image_layer.dart b/lib/src/layer/overlay_image_layer.dart index 220ee7be2..531b51061 100644 --- a/lib/src/layer/overlay_image_layer.dart +++ b/lib/src/layer/overlay_image_layer.dart @@ -3,8 +3,8 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_map/src/geo/latlng_bounds.dart'; import 'package:flutter_map/src/layer/general/mobile_layer_transformer.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:latlong2/latlong.dart'; /// Base class for all overlay images. diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 8662a3fa4..16381e423 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -72,7 +72,7 @@ class Polygon { /// Used to batch draw calls to the canvas. int get renderHashCode { - _hash ??= Object.hash( + return _hash ??= Object.hash( holePointsList, color, borderStrokeWidth, @@ -83,7 +83,6 @@ class Polygon { strokeJoin, _filledAndClockwise, ); - return _hash!; } int? _hash; diff --git a/lib/src/layer/polyline_layer.dart b/lib/src/layer/polyline_layer.dart index 46e661355..cc245a647 100644 --- a/lib/src/layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer.dart @@ -2,7 +2,9 @@ import 'dart:core'; import 'dart:ui' as ui; import 'package:flutter/widgets.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/src/geo/latlng_bounds.dart'; +import 'package:flutter_map/src/layer/general/mobile_layer_transformer.dart'; +import 'package:flutter_map/src/map/camera/camera.dart'; import 'package:latlong2/latlong.dart'; class Polyline { diff --git a/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart b/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart index 928ddf014..a5e9d9c5b 100644 --- a/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart +++ b/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart @@ -2,8 +2,8 @@ import 'package:flutter_map/src/geo/crs.dart'; import 'package:flutter_map/src/geo/latlng_bounds.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_bounds/tile_bounds_at_zoom.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:latlong2/latlong.dart'; import 'package:meta/meta.dart'; diff --git a/lib/src/layer/tile_layer/tile_layer.dart b/lib/src/layer/tile_layer/tile_layer.dart index b19d2b0df..3a924fd01 100644 --- a/lib/src/layer/tile_layer/tile_layer.dart +++ b/lib/src/layer/tile_layer/tile_layer.dart @@ -24,8 +24,10 @@ import 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_update_transformer.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; import 'package:flutter_map/src/map/controller/map_controller.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; +import 'package:http/retry.dart'; +import 'package:logger/logger.dart'; part 'retina_mode.dart'; part 'tile_error_evict_callback.dart'; @@ -215,6 +217,9 @@ class TileLayer extends StatefulWidget { final EvictErrorTileStrategy evictErrorTileStrategy; /// Stream to notify the [TileLayer] that it needs resetting + /// + /// The tile layer will not listen to this stream if it is not specified on + /// initial building, then later specified. final Stream? reset; /// Only load tiles that are within these bounds @@ -243,9 +248,9 @@ class TileLayer extends StatefulWidget { this.zoomReverse = false, double zoomOffset = 0.0, this.additionalOptions = const {}, - this.subdomains = const [], + this.subdomains = const ['a', 'b', 'c'], this.keepBuffer = 2, - this.panBuffer = 0, + this.panBuffer = 1, @Deprecated( 'Prefer `MapOptions.backgroundColor`. ' 'This property has been removed simplify interaction when using multiple `TileLayer`s. ' @@ -259,7 +264,9 @@ class TileLayer extends StatefulWidget { this.tileDisplay = const TileDisplay.fadeIn(), /// See [RetinaMode] for more information - final bool retinaMode = false, + /// + /// Defaults to `false` when `null`. + final bool? retinaMode, this.errorTileCallback, @Deprecated( 'Prefer creating a custom `TileProvider` instead. ' @@ -287,6 +294,42 @@ class TileLayer extends StatefulWidget { tileProvider = tileProvider ?? NetworkTileProvider(), tileUpdateTransformer = tileUpdateTransformer ?? TileUpdateTransformers.ignoreTapEvents { + // Debug Logging + if (kDebugMode && + urlTemplate != null && + urlTemplate!.contains('{s}.tile.openstreetmap.org')) { + Logger(printer: PrettyPrinter(methodCount: 0)).w( + '\x1B[1m\x1B[3mflutter_map\x1B[0m\nAvoid using subdomains with OSM\'s tile ' + 'server. Support may be become slow or be removed in future.\nSee ' + 'https://github.com/openstreetmap/operations/issues/737 for more info.', + ); + } + if (kDebugMode && + retinaMode == null && + wmsOptions == null && + urlTemplate!.contains('{r}')) { + Logger(printer: PrettyPrinter(methodCount: 0)).w( + '\x1B[1m\x1B[3mflutter_map\x1B[0m\nThe URL template includes a retina ' + "mode placeholder ('{r}') to retrieve native high-resolution\ntiles, " + 'which improve appearance especially on high-density displays.\n' + 'However, `TileLayer.retinaMode` was left unset, meaning flutter_map ' + 'will never retrieve these tiles.\nConsider using ' + '`RetinaMode.isHighDensity` to toggle this property automatically, ' + 'otherwise ensure\nit is set appropriately.\n' + 'See https://docs.fleaflet.dev/layers/tile-layer#retina-mode for ' + 'more info.', + ); + } + if (kDebugMode && kIsWeb && tileProvider is NetworkTileProvider?) { + Logger(printer: PrettyPrinter(methodCount: 0)).i( + '\x1B[1m\x1B[3mflutter_map\x1B[0m\nConsider installing the official ' + "'flutter_map_cancellable_tile_provider' plugin for improved\n" + 'performance on the web.\nSee ' + 'https://pub.dev/packages/flutter_map_cancellable_tile_provider for ' + 'more info.', + ); + } + // Tile Provider Setup if (!kIsWeb) { this.tileProvider.headers.putIfAbsent( @@ -294,7 +337,7 @@ class TileLayer extends StatefulWidget { } // Retina Mode Setup - resolvedRetinaMode = retinaMode + resolvedRetinaMode = (retinaMode ?? false) ? wmsOptions == null && urlTemplate!.contains('{r}') ? RetinaMode.server : RetinaMode.simulation @@ -323,9 +366,10 @@ class TileLayer extends StatefulWidget { class _TileLayerState extends State with TickerProviderStateMixin { bool _initializedFromMapCamera = false; - final TileImageManager _tileImageManager = TileImageManager(); + final _tileImageManager = TileImageManager(); late TileBounds _tileBounds; - late TileRangeCalculator _tileRangeCalculator; + late var _tileRangeCalculator = + TileRangeCalculator(tileSize: widget.tileSize); late TileScaleCalculator _tileScaleCalculator; // We have to hold on to the mapController hashCode to determine whether we @@ -334,28 +378,13 @@ class _TileLayerState extends State with TickerProviderStateMixin { // miss events. int? _mapControllerHashCode; - // Only one of these two subscriptions will be initialized. If - // TileLayer.tileUpdateTransformer is null then we subscribe to map movement - // otherwise we subscribe to tile update events which are transformed from - // map movements. StreamSubscription? _tileUpdateSubscription; - - StreamSubscription? _resetSub; Timer? _pruneLater; - @override - void initState() { - super.initState(); - - if (widget.reset != null) { - _resetSub = widget.reset?.listen((_) { - _tileImageManager.removeAll(widget.evictErrorTileStrategy); - _loadAndPruneInVisibleBounds(MapCamera.of(context)); - }); - } - - _tileRangeCalculator = TileRangeCalculator(tileSize: widget.tileSize); - } + late final _resetSub = widget.reset?.listen((_) { + _tileImageManager.removeAll(widget.evictErrorTileStrategy); + _loadAndPruneInVisibleBounds(MapCamera.of(context)); + }); // This is called on every map movement so we should avoid expensive logic // where possible. @@ -364,8 +393,8 @@ class _TileLayerState extends State with TickerProviderStateMixin { super.didChangeDependencies(); final camera = MapCamera.of(context); - final mapController = MapController.of(context); + if (_mapControllerHashCode != mapController.hashCode) { _tileUpdateSubscription?.cancel(); diff --git a/lib/src/layer/tile_layer/tile_range.dart b/lib/src/layer/tile_layer/tile_range.dart index daf43c8fb..ed3ebaeb4 100644 --- a/lib/src/layer/tile_layer/tile_range.dart +++ b/lib/src/layer/tile_layer/tile_range.dart @@ -2,8 +2,8 @@ import 'dart:math' as math hide Point; import 'dart:math' show Point; import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:meta/meta.dart'; @immutable diff --git a/lib/src/layer/tile_layer/tile_range_calculator.dart b/lib/src/layer/tile_layer/tile_range_calculator.dart index 80d6f6ae9..c30e70091 100644 --- a/lib/src/layer/tile_layer/tile_range_calculator.dart +++ b/lib/src/layer/tile_layer/tile_range_calculator.dart @@ -1,7 +1,7 @@ import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:latlong2/latlong.dart'; import 'package:meta/meta.dart'; diff --git a/lib/src/layer/tile_layer/tile_update_transformer.dart b/lib/src/layer/tile_layer/tile_update_transformer.dart index c8793268b..d71f4f8f7 100644 --- a/lib/src/layer/tile_layer/tile_update_transformer.dart +++ b/lib/src/layer/tile_layer/tile_update_transformer.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter_map/src/gestures/map_events.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart'; -import 'package:flutter_map/src/misc/private/util.dart'; import 'package:meta/meta.dart'; /// Defines which [TileUpdateEvent]s should cause which [TileUpdateEvent]s and @@ -20,49 +19,67 @@ typedef TileUpdateTransformer /// Set of default [TileUpdateTransformer]s @immutable abstract class TileUpdateTransformers { - /// Always load/update/prune tiles on events, except where the event is one of: + /// Always* load/update/prune tiles on events + /// + /// {@template tut-ignore_tap} + /// Ignores events where it is one of: /// - [MapEventTap] /// - [MapEventSecondaryTap] /// - [MapEventLongPress] /// /// It is assumed (/guaranteed) that these events should not cause the map to /// move, and therefore, tile changes are not required. + /// {@endtemplate} /// /// Default transformer for [TileLayer]. static final ignoreTapEvents = TileUpdateTransformer.fromHandlers(handleData: (event, sink) { - if (!_triggeredByTap(event)) sink.add(event); - }); - - /// This feature is deprecated since v5. - /// - /// Prefer `ignoreTapEvents` instead. This transformer produces theoretically - /// unnecessary tile updates which can harm performance. If you notice a - /// difference in behaviour, please open a bug report on GitHub. - @Deprecated( - 'Prefer `ignoreTapEvents` instead. ' - 'This transformer produces theoretically unnecessary tile updates which can harm performance. ' - 'If you notice a difference in behaviour, please open a bug report on GitHub. ' - 'This feature is deprecated since v5.', - ) - static final alwaysLoadAndPrune = - TileUpdateTransformer.fromHandlers(handleData: (event, sink) { - sink.add(event); + if (!wasTriggeredByTap(event)) sink.add(event); }); /// Throttle loading/updating/pruning tiles such that it only occurs once per /// [duration] - static TileUpdateTransformer throttle( - Duration duration, { - /// Whether to filter tap events as [ignoreTapEvents] does - bool ignoreTapEvents = true, - }) => - throttleStreamTransformerWithTrailingCall( - duration, - ignore: ignoreTapEvents ? _triggeredByTap : null, - ); + /// + /// {@macro tut-ignore_tap} + static TileUpdateTransformer throttle(Duration duration) { + Timer? timer; + TileUpdateEvent recentEvent; + var trailingCall = false; + + void throttleHandler( + TileUpdateEvent event, + EventSink sink, + ) { + if (wasTriggeredByTap(event)) return; + + recentEvent = event; + + if (timer == null) { + sink.add(recentEvent); + timer = Timer(duration, () { + timer = null; + + if (trailingCall) { + trailingCall = false; + throttleHandler(recentEvent, sink); + } + }); + } else { + trailingCall = true; + } + } + + return StreamTransformer.fromHandlers( + handleData: throttleHandler, + handleDone: (sink) { + timer?.cancel(); + sink.close(); + }, + ); + } - static bool _triggeredByTap(TileUpdateEvent event) => + @internal + static bool wasTriggeredByTap(TileUpdateEvent event) => event.mapEvent is MapEventTap || event.mapEvent is MapEventSecondaryTap || event.mapEvent is MapEventLongPress; diff --git a/lib/src/map/camera/camera.dart b/lib/src/map/camera/camera.dart index 5513a4b28..2875c2db1 100644 --- a/lib/src/map/camera/camera.dart +++ b/lib/src/map/camera/camera.dart @@ -6,8 +6,8 @@ import 'package:flutter_map/src/geo/crs.dart'; import 'package:flutter_map/src/geo/latlng_bounds.dart'; import 'package:flutter_map/src/map/inherited_model.dart'; import 'package:flutter_map/src/map/options/options.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:latlong2/latlong.dart'; /// Describes the view of a map. This includes the size/zoom/position/crs as diff --git a/lib/src/map/camera/camera_fit.dart b/lib/src/map/camera/camera_fit.dart index 1fd86402f..0e337ed9a 100644 --- a/lib/src/map/camera/camera_fit.dart +++ b/lib/src/map/camera/camera_fit.dart @@ -5,8 +5,8 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_map/src/geo/latlng_bounds.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; import 'package:flutter_map/src/map/camera/camera_constraint.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; import 'package:latlong2/latlong.dart'; /// Describes a position for a [MapCamera] diff --git a/lib/src/map/controller/internal.dart b/lib/src/map/controller/internal.dart index 5d244206b..6746c5ab8 100644 --- a/lib/src/map/controller/internal.dart +++ b/lib/src/map/controller/internal.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_map/src/gestures/flutter_map_interactive_viewer.dart'; import 'package:flutter_map/src/gestures/map_events.dart'; +import 'package:flutter_map/src/gestures/positioned_tap_detector_2.dart'; import 'package:flutter_map/src/map/camera/camera.dart'; import 'package:flutter_map/src/map/camera/camera_fit.dart'; import 'package:flutter_map/src/map/controller/impl.dart'; @@ -11,7 +12,6 @@ import 'package:flutter_map/src/map/options/options.dart'; import 'package:flutter_map/src/misc/move_and_rotate_result.dart'; import 'package:flutter_map/src/misc/point_extensions.dart'; import 'package:flutter_map/src/misc/position.dart'; -import 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart'; import 'package:latlong2/latlong.dart'; /// This controller is for internal use. All updates to the state should be done diff --git a/lib/src/map/options/options.dart b/lib/src/map/options/options.dart index 8c7b05f44..91928537b 100644 --- a/lib/src/map/options/options.dart +++ b/lib/src/map/options/options.dart @@ -6,6 +6,7 @@ import 'package:flutter_map/src/geo/latlng_bounds.dart'; import 'package:flutter_map/src/gestures/interactive_flag.dart'; import 'package:flutter_map/src/gestures/map_events.dart'; import 'package:flutter_map/src/gestures/multi_finger_gesture.dart'; +import 'package:flutter_map/src/gestures/positioned_tap_detector_2.dart'; import 'package:flutter_map/src/layer/general/translucent_pointer.dart'; import 'package:flutter_map/src/map/camera/camera_constraint.dart'; import 'package:flutter_map/src/map/camera/camera_fit.dart'; @@ -14,7 +15,6 @@ import 'package:flutter_map/src/map/options/interaction.dart'; import 'package:flutter_map/src/map/widget.dart'; import 'package:flutter_map/src/misc/fit_bounds_options.dart'; import 'package:flutter_map/src/misc/position.dart'; -import 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart'; import 'package:latlong2/latlong.dart'; typedef MapEventCallback = void Function(MapEvent); @@ -308,7 +308,10 @@ class MapOptions { _cameraConstraint ?? (maxBounds != null ? CameraConstraint.contain(bounds: maxBounds!) - : const CameraConstraint.unconstrained()); + : CameraConstraint.contain( + bounds: + LatLngBounds(const LatLng(-90, -180), const LatLng(90, 180)), + )); @override bool operator ==(Object other) => diff --git a/lib/src/map/widget.dart b/lib/src/map/widget.dart index 8f3f9658c..28183400a 100644 --- a/lib/src/map/widget.dart +++ b/lib/src/map/widget.dart @@ -2,6 +2,7 @@ import 'dart:math'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/src/gestures/flutter_map_interactive_viewer.dart'; import 'package:flutter_map/src/gestures/map_events.dart'; @@ -13,6 +14,7 @@ import 'package:flutter_map/src/map/controller/internal.dart'; import 'package:flutter_map/src/map/controller/map_controller.dart'; import 'package:flutter_map/src/map/inherited_model.dart'; import 'package:flutter_map/src/map/options/options.dart'; +import 'package:logger/logger.dart'; /// An interactive geographical map /// @@ -97,6 +99,15 @@ class _FlutterMapStateContainer extends State WidgetsBinding.instance .addPostFrameCallback((_) => widget.options.onMapReady?.call()); + + if (kDebugMode && kIsWeb && !isCanvasKit) { + Logger(printer: PrettyPrinter(methodCount: 0)).w( + '\x1B[1m\x1B[3mflutter_map\x1B[0m\nAvoid using HTML rendering on the web ' + 'platform. Prefer CanvasKit.\nSee ' + 'https://docs.fleaflet.dev/getting-started/installation#web for more ' + 'info.', + ); + } } @override diff --git a/lib/src/misc/private/bounds.dart b/lib/src/misc/bounds.dart similarity index 100% rename from lib/src/misc/private/bounds.dart rename to lib/src/misc/bounds.dart diff --git a/lib/src/misc/private/util.dart b/lib/src/misc/private/util.dart deleted file mode 100644 index bb7b57923..000000000 --- a/lib/src/misc/private/util.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:async'; - -var _templateRe = RegExp(r'\{ *([\w_-]+) *\}'); - -/// Replaces the templating placeholders with the provided data map. -/// -/// Example input: https://tile.openstreetmap.org/{z}/{x}/{y}.png -/// -/// Throws an [Exception] if any placeholder remains unresolved. -String template(String str, Map data) { - return str.replaceAllMapped(_templateRe, (match) { - final firstMatch = match.group(1); - if (firstMatch == null) { - throw Exception('incorrect URL template: $str'); - } - final value = data[firstMatch]; - if (value == null) { - throw Exception('No value provided for variable ${match.group(1)}'); - } else { - return value; - } - }); -} - -StreamTransformer throttleStreamTransformerWithTrailingCall( - Duration duration, { - bool Function(T)? ignore, -}) { - Timer? timer; - T recentData; - var trailingCall = false; - - late final void Function(T data, EventSink sink) throttleHandler; - - throttleHandler = (data, sink) { - if (ignore?.call(data) ?? false) return; - - recentData = data; - - if (timer == null) { - sink.add(recentData); - timer = Timer(duration, () { - timer = null; - - if (trailingCall) { - trailingCall = false; - throttleHandler(recentData, sink); - } - }); - } else { - trailingCall = true; - } - }; - - return StreamTransformer.fromHandlers( - handleData: throttleHandler, - handleDone: (sink) { - timer?.cancel(); - sink.close(); - }); -} diff --git a/pubspec.yaml b/pubspec.yaml index 46d347622..fe3776a33 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,14 @@ name: flutter_map -description: A versatile mapping package for Flutter, that's simple and easy to learn, yet completely customizable and configurable. -version: 6.0.0-dev.2 +description: A versatile mapping package for Flutter, that's simple and easy to learn, yet completely customizable and configurable +version: 6.0.0 repository: https://github.com/fleaflet/flutter_map issue_tracker: https://github.com/fleaflet/flutter_map/issues documentation: https://docs.fleaflet.dev +funding: + - https://docs.fleaflet.dev/supporters#support-us + topics: - flutter-map - map @@ -24,22 +27,23 @@ environment: dependencies: async: ^2.9.0 - collection: ^1.16.0 + collection: ^1.17.1 flutter: sdk: flutter http: ^1.0.0 latlong2: ^0.9.0 - meta: ^1.8.0 + logger: ^2.0.1 + meta: ^1.9.1 polylabel: ^1.0.1 proj4dart: ^2.1.0 vector_math: ^2.1.2 dev_dependencies: - flutter_lints: ^2.0.1 + flutter_lints: ^2.0.3 flutter_test: sdk: flutter - mocktail: ^1.0.0 - test: ^1.21.4 + mocktail: ^1.0.1 + test: ^1.24.3 flutter: assets: diff --git a/test/core/bounds_test.dart b/test/core/bounds_test.dart index 9ae29796d..3438d4f87 100644 --- a/test/core/bounds_test.dart +++ b/test/core/bounds_test.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:flutter_map/src/misc/private/bounds.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_test/flutter_test.dart'; import '../helpers/core.dart'; diff --git a/test/flutter_map_test.dart b/test/flutter_map_test.dart index 5b91f528b..41da4b439 100644 --- a/test/flutter_map_test.dart +++ b/test/flutter_map_test.dart @@ -1,5 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/src/geo/crs.dart'; +import 'package:flutter_map/src/gestures/interactive_flag.dart'; +import 'package:flutter_map/src/layer/marker_layer.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart'; +import 'package:flutter_map/src/map/camera/camera.dart'; +import 'package:flutter_map/src/map/controller/map_controller.dart'; +import 'package:flutter_map/src/map/options/interaction.dart'; +import 'package:flutter_map/src/map/options/options.dart'; +import 'package:flutter_map/src/map/widget.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:latlong2/latlong.dart'; diff --git a/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart b/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart index 2ad38f20a..e7e390af3 100644 --- a/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart +++ b/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:flutter_map/src/layer/tile_layer/tile_bounds/tile_bounds_at_zoom.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart b/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart index c97e83e02..5eeede06b 100644 --- a/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart +++ b/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_bounds/tile_bounds.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_bounds/tile_bounds_at_zoom.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; diff --git a/test/layer/tile_layer/tile_image_view_test.dart b/test/layer/tile_layer/tile_image_view_test.dart index b78ab4428..e9d3d9475 100644 --- a/test/layer/tile_layer/tile_image_view_test.dart +++ b/test/layer/tile_layer/tile_image_view_test.dart @@ -2,10 +2,12 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/src/scheduler/ticker.dart'; -import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_display.dart'; +import 'package:flutter_map/src/layer/tile_layer/tile_image.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image_view.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; -import 'package:flutter_map/src/misc/private/bounds.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:test/test.dart'; import '../../test_utils/test_tile_image.dart'; diff --git a/test/layer/tile_layer/tile_range_test.dart b/test/layer/tile_layer/tile_range_test.dart index 083912767..3a60cf71a 100644 --- a/test/layer/tile_layer/tile_range_test.dart +++ b/test/layer/tile_layer/tile_range_test.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; import 'package:test/test.dart'; diff --git a/test/misc/private/positioned_tap_detector_2_test.dart b/test/misc/private/positioned_tap_detector_2_test.dart index e30fd8ff5..e46b86892 100644 --- a/test/misc/private/positioned_tap_detector_2_test.dart +++ b/test/misc/private/positioned_tap_detector_2_test.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart'; +import 'package:flutter_map/src/gestures/positioned_tap_detector_2.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/windowsApplicationInstallerSetup.iss b/windowsApplicationInstallerSetup.iss index 1b03910b4..c6b02849e 100644 --- a/windowsApplicationInstallerSetup.iss +++ b/windowsApplicationInstallerSetup.iss @@ -26,7 +26,7 @@ LicenseFile=LICENSE PrivilegesRequired=lowest OutputDir=windowsTemp OutputBaseFilename=WindowsApplication -SetupIconFile=example\assets\ProjectIcon.ico +SetupIconFile=example\windows\runner\resources\app_icon.ico Compression=lzma SolidCompression=yes WizardStyle=modern