diff --git a/.github/workflows/on-commit-main.yml b/.github/workflows/on-commit-main.yml index acc45d3..cf75846 100644 --- a/.github/workflows/on-commit-main.yml +++ b/.github/workflows/on-commit-main.yml @@ -29,7 +29,8 @@ jobs: - name: Format all projects run: dart format --output=none --set-exit-if-changed . test: - runs-on: ubuntu-latest + # run on windows because + runs-on: windows-latest name: Test steps: - name: Checkout project diff --git a/README.md b/README.md index d3905ec..2f1ed50 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,19 @@ storage backend you would possibly want. A compass for flutter_map that indicates the map rotation. It rotates the map back to north on top when clicked. +### [flutter_map_mbtiles](https://pub.dev/packages/flutter_map_mbtiles) + +[![Pub Version](https://img.shields.io/pub/v/flutter_map_mbtiles)](https://pub.dev/packages/flutter_map_mbtiles) +[![likes](https://img.shields.io/pub/likes/flutter_map_mbtiles?logo=flutter)](https://pub.dev/packages/flutter_map_mbtiles) +[![Pub Popularity](https://img.shields.io/pub/popularity/flutter_map_mbtiles)](https://pub.dev/packages/flutter_map_mbtiles) + +This package provides the `MbTilesTileProvider` that can be used with +flutter_map tile layers. + +- This package uses [mbtiles](https://pub.dev/packages/mbtiles) under the hood + for the MBTiles support. +- MBTiles is a file format to store map tiles in a single SQLite database. + ### [flutter_map_pmtiles](https://pub.dev/packages/flutter_map_pmtiles) [![Pub Version](https://img.shields.io/pub/v/flutter_map_pmtiles)](https://pub.dev/packages/flutter_map_pmtiles) diff --git a/example/assets/mbtiles/countries-raster.mbtiles b/example/assets/mbtiles/countries-raster.mbtiles new file mode 100644 index 0000000..173ad5a Binary files /dev/null and b/example/assets/mbtiles/countries-raster.mbtiles differ diff --git a/example/assets/mbtiles/countries-vector.mbtiles b/example/assets/mbtiles/countries-vector.mbtiles new file mode 100644 index 0000000..496f725 Binary files /dev/null and b/example/assets/mbtiles/countries-vector.mbtiles differ diff --git a/example/assets/mbtiles/malta-vector.mbtiles b/example/assets/mbtiles/malta-vector.mbtiles new file mode 100644 index 0000000..e719a1a Binary files /dev/null and b/example/assets/mbtiles/malta-vector.mbtiles differ diff --git a/example/lib/common/utils.dart b/example/lib/common/utils.dart new file mode 100644 index 0000000..6fd2156 --- /dev/null +++ b/example/lib/common/utils.dart @@ -0,0 +1,22 @@ +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; + +/// This function copies an asset file from the asset bundle to the temporary +/// app directory. +/// +/// **It is not recommended to use this in production and for larger files.** +/// Instead download your files from a web server or s3 storage. +Future copyAssetToFile(String assetFile) async { + final tempDir = await getTemporaryDirectory(); + final filename = assetFile.split('/').last; + final file = File('${tempDir.path}/$filename'); + + final data = await rootBundle.load(assetFile); + await file.writeAsBytes( + data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes), + flush: true, + ); + return file; +} diff --git a/example/lib/flutter_map_mbtiles/page.dart b/example/lib/flutter_map_mbtiles/page.dart new file mode 100644 index 0000000..05c0059 --- /dev/null +++ b/example/lib/flutter_map_mbtiles/page.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_mbtiles/flutter_map_mbtiles.dart'; +import 'package:flutter_map_plugins_example/common/utils.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:mbtiles/mbtiles.dart'; + +class FlutterMapMbTilesPage extends StatefulWidget { + const FlutterMapMbTilesPage({super.key}); + + @override + State createState() => _FlutterMapMbTilesPageState(); +} + +class _FlutterMapMbTilesPageState extends State { + final Future _futureMbtiles = _initMbtiles(); + MbTiles? _mbtiles; + + static Future _initMbtiles() async { + // This function copies an asset file from the asset bundle to the temporary + // app directory. + // It is not recommended to use this in production. Instead download your + // mbtiles file from a web server or object storage. + final file = await copyAssetToFile( + 'assets/mbtiles/countries-raster.mbtiles', + ); + return MbTiles(mbtilesPath: file.path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + title: const Text('flutter_map_mbtiles'), + ), + body: FutureBuilder( + future: _futureMbtiles, + builder: (context, snapshot) { + if (snapshot.hasData) { + _mbtiles = snapshot.data; + final metadata = _mbtiles!.getMetadata(); + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: Text( + 'MBTiles Name: ${metadata.name}, ' + 'Format: ${metadata.format}', + ), + ), + Expanded( + child: FlutterMap( + options: const MapOptions( + minZoom: 0, + maxZoom: 6, + initialZoom: 2, + initialCenter: LatLng(49, 9), + ), + children: [ + TileLayer( + tileProvider: MbTilesTileProvider( + mbtiles: _mbtiles!, + silenceTileNotFound: true, + ), + ), + ], + ), + ), + ], + ); + } + if (snapshot.hasError) { + return Center(child: Text(snapshot.error.toString())); + } + return const Center(child: CircularProgressIndicator()); + }, + ), + ); + } + + @override + void dispose() { + // close the open database connection + _mbtiles?.dispose(); + super.dispose(); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index e19031b..50cdbc2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,10 @@ import 'dart:math'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map_plugins_example/flutter_map_cache/page.dart'; import 'package:flutter_map_plugins_example/flutter_map_compass/page.dart'; +import 'package:flutter_map_plugins_example/flutter_map_mbtiles/page.dart'; import 'package:flutter_map_plugins_example/flutter_map_pmtiles/page.dart'; import 'package:flutter_map_plugins_example/vector_map_tiles_pmtiles/page.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -29,6 +31,7 @@ class MyApp extends StatelessWidget { 'flutter_map_cache': (context) => const FlutterMapCachePage(), 'flutter_map_pmtiles': (context) => const FlutterMapPmTilesPage(), 'vector_map_tiles_pmtiles': (context) => VectorMapTilesPmTilesPage(), + 'flutter_map_mbtiles': (context) => const FlutterMapMbTilesPage(), 'flutter_map_compass': (context) => const FlutterMapCompassPage(), }, ); @@ -53,12 +56,17 @@ class SelectionPage extends StatelessWidget { 'reset the rotation on click', routeName: 'flutter_map_compass', ), + SelectionItemWidget.disabledOnWeb( + title: 'flutter_map_mbtiles', + desc: 'MBTiles for flutter_map', + routeName: 'flutter_map_mbtiles', + ), SelectionItemWidget( title: 'flutter_map_pmtiles', desc: 'PMTiles for flutter_map', routeName: 'flutter_map_pmtiles', ), - SelectionItemWidget( + SelectionItemWidget.disabledOnWeb( title: 'vector_map_tiles_pmtiles', desc: 'PMTiles for vector_map_files / flutter_map', routeName: 'vector_map_tiles_pmtiles', @@ -101,35 +109,66 @@ class SelectionItemWidget extends StatelessWidget { fontSize: 16, fontWeight: FontWeight.bold, ); + static const _bannerTextStyle = TextStyle( + color: Color(0xFFFFFFFF), + fontSize: 10, + fontWeight: FontWeight.w900, + height: 1, + ); final String title; final String desc; final String routeName; + final bool disabled; + final String disabledMessage; const SelectionItemWidget({ super.key, required this.title, required this.desc, required this.routeName, + this.disabled = false, + this.disabledMessage = 'Disabled', }); + const SelectionItemWidget.disabledOnWeb({ + super.key, + required this.title, + required this.desc, + required this.routeName, + }) : disabled = kIsWeb, + disabledMessage = 'Not on web'; + @override Widget build(BuildContext context) { + final content = Padding( + padding: const EdgeInsets.all(12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(title, style: _titleStyle, textAlign: TextAlign.center), + const Spacer(), + Text(desc, textAlign: TextAlign.center), + const Spacer(), + ], + ), + ); + return Card( - color: Colors.white, + color: disabled ? Colors.white54 : Colors.white, child: InkWell( - onTap: () => Navigator.of(context).pushNamed(routeName), - child: Padding( - padding: const EdgeInsets.all(12), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(title, style: _titleStyle, textAlign: TextAlign.center), - const Spacer(), - Text(desc, textAlign: TextAlign.center), - const Spacer(), - ], - ), - ), + onTap: + disabled ? null : () => Navigator.of(context).pushNamed(routeName), + child: disabled + ? ClipRect( + child: Banner( + message: disabledMessage, + textStyle: _bannerTextStyle, + color: Colors.grey, + location: BannerLocation.bottomEnd, + child: content, + ), + ) + : content, ), ); } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 959c2d5..5dc0483 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,15 +9,10 @@ environment: dependencies: flutter: sdk: flutter - # commom packages flutter_map: ^6.0.0 latlong2: ^0.9.0 url_launcher: ^6.2.4 url_strategy: ^0.2.0 - - # flutter_map_cache specific packages - flutter_map_cache: - path: ../flutter_map_cache connectivity_plus: ^5.0.0 path_provider: ^2.0.15 dio: ^5.2.0+1 @@ -25,17 +20,19 @@ dependencies: dio_cache_interceptor_db_store: ^5.1.0 dio_cache_interceptor_file_store: ^1.2.2 dio_cache_interceptor_hive_store: ^3.2.1 - sqlite3_flutter_libs: ^0.5.15 # Drift + sqlite3_flutter_libs: ^0.5.15 + vector_map_tiles: ^7.0.1 + vector_tile_renderer: ^5.2.0 + mbtiles: ^0.3.1 - # flutter_map_pmtiles specific packages - flutter_map_pmtiles: - path: ../flutter_map_pmtiles - - # vector_map_tiles_pmtiles specific packages + flutter_map_mbtiles: + path: ../flutter_map_mbtiles vector_map_tiles_pmtiles: path: ../vector_map_tiles_pmtiles - vector_map_tiles: ^7.0.1 - vector_tile_renderer: ^5.2.0 + flutter_map_pmtiles: + path: ../flutter_map_pmtiles + flutter_map_cache: + path: ../flutter_map_cache # vector_map_tiles_compass specific packages flutter_map_compass: @@ -47,3 +44,5 @@ dev_dependencies: flutter: uses-material-design: true + assets: + - assets/mbtiles/ \ No newline at end of file diff --git a/flutter_map_cache/pubspec.yaml b/flutter_map_cache/pubspec.yaml index f3de18c..60e5507 100644 --- a/flutter_map_cache/pubspec.yaml +++ b/flutter_map_cache/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - total_lints: ^3.0.0 test: ^1.24.9 + total_lints: ^3.0.0 http_mock_adapter: ^0.6.1 latlong2: ^0.9.0 \ No newline at end of file diff --git a/flutter_map_mbtiles/CHANGELOG.md b/flutter_map_mbtiles/CHANGELOG.md new file mode 100644 index 0000000..5cdb0b7 --- /dev/null +++ b/flutter_map_mbtiles/CHANGELOG.md @@ -0,0 +1,3 @@ +## [1.0.0] 2024-mm-dd + +- Initial release diff --git a/flutter_map_mbtiles/LICENSE b/flutter_map_mbtiles/LICENSE new file mode 100644 index 0000000..6370cf7 --- /dev/null +++ b/flutter_map_mbtiles/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Joscha Eckert (josxha) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flutter_map_mbtiles/README.md b/flutter_map_mbtiles/README.md new file mode 100644 index 0000000..cd1067f --- /dev/null +++ b/flutter_map_mbtiles/README.md @@ -0,0 +1,79 @@ +# flutter_map_mbtiles + +Raster and Vector MBTiles `TileProvider` +for [flutter_map](https://pub.dev/packages/flutter_map). + +- Supported raster tiles: `jpg`, `png`, `webp` +- If you want to use `pbf` vector tiles + you can + use [vector_map_tiles_mbtiles](https://pub.dev/packages/vector_map_tiles_mbtiles). +- Web is not supported because of it's lacking SQLite support. + +[![Pub Version](https://img.shields.io/pub/v/flutter_map_mbtiles)](https://pub.dev/packages/flutter_map_mbtiles) +[![likes](https://img.shields.io/pub/likes/flutter_map_mbtiles?logo=flutter)](https://pub.dev/packages/flutter_map_mbtiles) +[![Pub Points](https://img.shields.io/pub/points/flutter_map_mbtiles)](https://pub.dev/packages/flutter_map_mbtiles/score) +[![Pub Popularity](https://img.shields.io/pub/popularity/flutter_map_mbtiles)](https://pub.dev/packages/flutter_map_mbtiles) + +[![GitHub last commit](https://img.shields.io/github/last-commit/josxha/flutter_map_plugins)](https://github.com/josxha/flutter_map_plugins) +[![stars](https://badgen.net/github/stars/josxha/flutter_map_plugins?label=stars&color=green&icon=github)](https://github.com/josxha/flutter_map_plugins/stargazers) +[![GitHub issues](https://img.shields.io/github/issues/josxha/flutter_map_plugins)](https://github.com/josxha/flutter_map_plugins/issues) +[![codecov](https://codecov.io/gh/josxha/flutter_map_plugins/graph/badge.svg?token=5045489G7X)](https://codecov.io/gh/josxha/flutter_map_plugins) + +## Getting started + +#### pubspec.yaml + +```yaml +dependencies: + flutter_map: ^6.0.0 # in case you don't have it yet + flutter_map_mbtiles: ^1.0.0 # this package +``` + +## Usage + +```dart +late final MBTiles mbtiles; + +@override +void initState() { + // open mbtiles + mbtiles = MBTiles( + mbtilesPath: 'assets/mbtiles/countries-raster.mbtiles', + ); + super.initState(); +} + +@override +Widget build(BuildContext context) { + return FlutterMap( + options: MapOptions( + initialZoom: zoom, + initialCenter: center, + onPositionChanged: (position, hasGesture) { + if (position.center == null || position.zoom == null) return; + setState(() { + center = position.center!; + zoom = position.zoom!; + }); + }, + minZoom: 0, + maxZoom: 6, + ), + children: [ + TileLayer( + tileProvider: MBTilesTileProvider( + mbtiles: mbtiles, + ), + ), + ], + ); +} + +@override +void dispose() { + // close mbtiles + mbtiles.dispose(); + super.dispose(); +} + +``` \ No newline at end of file diff --git a/flutter_map_mbtiles/example/example.md b/flutter_map_mbtiles/example/example.md new file mode 100644 index 0000000..deb0596 --- /dev/null +++ b/flutter_map_mbtiles/example/example.md @@ -0,0 +1,29 @@ +### Basic usage + +```dart +// initiate your tile provider +final _futureTileProvider = MbTilesTileProvider + .fromSource('path/to/file.mbtiles'); + +@override +Widget build(BuildContext context) { + return FlutterMap( + options: MapOptions(), + children: [ + TileLayer( + // use your awaited MbTilesTileProvider + tileProvider: tileProvider, + ), + ], + ); +} +``` + +### Need more information? + +- Read + the [README.md](https://github.com/josxha/flutter_map_plugins/blob/main/flutter_map_mbtiles/README.md) +- Check out + the [combined example app](https://github.com/josxha/flutter_map_plugins/tree/main/example) + that showcases this and other flutter_map + packages. \ No newline at end of file diff --git a/flutter_map_mbtiles/lib/flutter_map_mbtiles.dart b/flutter_map_mbtiles/lib/flutter_map_mbtiles.dart new file mode 100644 index 0000000..bb7016e --- /dev/null +++ b/flutter_map_mbtiles/lib/flutter_map_mbtiles.dart @@ -0,0 +1 @@ +export 'src/mbtiles_tile_provider.dart'; diff --git a/flutter_map_mbtiles/lib/src/mbtiles_image_provider.dart b/flutter_map_mbtiles/lib/src/mbtiles_image_provider.dart new file mode 100644 index 0000000..370e7a4 --- /dev/null +++ b/flutter_map_mbtiles/lib/src/mbtiles_image_provider.dart @@ -0,0 +1,71 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:mbtiles/mbtiles.dart'; + +/// Image provider with additional caching functionality +class MbTilesImageProvider extends ImageProvider { + /// The tile coordinates of the requested tile image + final TileCoordinates coordinates; + + /// MBTiles database + final MbTiles mbtiles; + + /// Whether an exception should get thrown if a tile is not found. + final bool silenceTileNotFound; + + /// Default constructor for the [MbTilesImageProvider] + const MbTilesImageProvider({ + required this.coordinates, + required this.mbtiles, + required this.silenceTileNotFound, + }); + + @override + Future obtainKey(ImageConfiguration configuration) { + return Future.value(this); + } + + @override + ImageStreamCompleter loadImage( + MbTilesImageProvider key, + ImageDecoderCallback decode, + ) { + final chunkEvents = StreamController(); + + return MultiFrameImageStreamCompleter( + // ignore: discarded_futures, not actually but the lint thinks so + codec: _loadAsync(key, chunkEvents, decode), + chunkEvents: chunkEvents.stream, + scale: 1, + debugLabel: coordinates.toString(), + informationCollector: () => [ + DiagnosticsProperty('Current provider', key), + ], + ); + } + + Future _loadAsync( + MbTilesImageProvider key, + StreamController chunkEvents, + ImageDecoderCallback decode, + ) async { + final tmsY = ((1 << coordinates.z) - 1) - coordinates.y; + final bytes = mbtiles.getTile(z: coordinates.z, x: coordinates.x, y: tmsY); + if (bytes == null) { + if (silenceTileNotFound) { + return decode( + await ImmutableBuffer.fromUint8List(TileProvider.transparentImage), + ); + } + throw Exception( + 'Tile could not be found in MBTiles ' + '(z:${coordinates.z}, x:${coordinates.x}, y:${coordinates.y})', + ); + } + return decode(await ImmutableBuffer.fromUint8List(bytes)); + } +} diff --git a/flutter_map_mbtiles/lib/src/mbtiles_tile_provider.dart b/flutter_map_mbtiles/lib/src/mbtiles_tile_provider.dart new file mode 100644 index 0000000..d6fffdb --- /dev/null +++ b/flutter_map_mbtiles/lib/src/mbtiles_tile_provider.dart @@ -0,0 +1,50 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_mbtiles/src/mbtiles_image_provider.dart'; +import 'package:mbtiles/mbtiles.dart'; + +/// MBTiles raster [TileProvider], use for `png`, `jpg` or `webp` tiles. +class MbTilesTileProvider extends TileProvider { + /// MBTiles database + final MbTiles mbtiles; + + /// Create a new [MbTilesTileProvider] instance with an MBTiles instance. + MbTilesTileProvider({ + required this.mbtiles, + this.silenceTileNotFound = !kDebugMode, + }) : _createdInternally = false; + + /// If the MBTiles file was created internally, the connection gets closed + /// on [dispose]. + final bool _createdInternally; + + /// Set to true if you don't want to throw exceptions for tiles that are + /// not found. + /// + /// Defaults to false in debug mode and to true else. + final bool silenceTileNotFound; + + /// Create a new [MbTilesTileProvider] instance by providing the path of the + /// MBTiles file. + /// The MBTiles database will be opened internally. + MbTilesTileProvider.fromPath({ + required String path, + this.silenceTileNotFound = !kDebugMode, + }) : mbtiles = MbTiles(mbtilesPath: path), + _createdInternally = true; + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) => + MbTilesImageProvider( + coordinates: coordinates, + mbtiles: mbtiles, + silenceTileNotFound: silenceTileNotFound, + ); + + @override + void dispose() { + if (_createdInternally) mbtiles.dispose(); + super.dispose(); + } +} diff --git a/flutter_map_mbtiles/pubspec.yaml b/flutter_map_mbtiles/pubspec.yaml new file mode 100644 index 0000000..f96b198 --- /dev/null +++ b/flutter_map_mbtiles/pubspec.yaml @@ -0,0 +1,25 @@ +name: flutter_map_mbtiles +description: Mapbox MBTiles tile provider for flutter_map that can be used for a map that is 100% offline. +version: 1.0.0 +repository: https://github.com/josxha/flutter_map_plugins +issue_tracker: https://github.com/josxha/flutter_map_plugins/issues +topics: [ flutter-map, mbtiles, map ] + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" + +dependencies: + flutter: + sdk: flutter + flutter_map: ^6.0.0 + mbtiles: ^0.3.1 + sqlite3_flutter_libs: ^0.5.18 + +dev_dependencies: + flutter_test: + sdk: flutter + test: ^1.24.9 + mockito: ^5.4.4 + build_runner: ^2.4.8 + total_lints: ^3.2.0 diff --git a/flutter_map_mbtiles/test/integration_test.dart b/flutter_map_mbtiles/test/integration_test.dart new file mode 100644 index 0000000..bcdfb8c --- /dev/null +++ b/flutter_map_mbtiles/test/integration_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mbtiles/mbtiles.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +@GenerateNiceMocks([MockSpec()]) +import 'integration_test.mocks.dart'; +import 'utils/test_app.dart'; + +Future main() async { + testWidgets('FlutterMap with MbTilesTileProvider', (tester) async { + final mbtiles = MockMbTiles(); + when(mbtiles.getMetadata()) + .thenAnswer((_) => MbTilesMetadata(name: 'MockMbTiles', format: 'png')); + when(mbtiles.getTile(z: captureAny, x: captureAny, y: captureAny)) + .thenAnswer((_) => TileProvider.transparentImage); + await tester.pumpWidget(TestApp(mbtiles: mbtiles)); + await tester.pumpAndSettle(); + }); +} diff --git a/flutter_map_mbtiles/test/integration_test.mocks.dart b/flutter_map_mbtiles/test/integration_test.mocks.dart new file mode 100644 index 0000000..377252c --- /dev/null +++ b/flutter_map_mbtiles/test/integration_test.mocks.dart @@ -0,0 +1,139 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in flutter_map_mbtiles/test/integration_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:typed_data' as _i4; + +import 'package:mbtiles/src/mbtiles.dart' as _i3; +import 'package:mbtiles/src/model/mbtiles_metadata.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeMBTilesMetadata_0 extends _i1.SmartFake + implements _i2.MBTilesMetadata { + _FakeMBTilesMetadata_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [MbTiles]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMbTiles extends _i1.Mock implements _i3.MbTiles { + @override + bool get editable => (super.noSuchMethod( + Invocation.getter(#editable), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i2.MBTilesMetadata getMetadata({bool? allowCache = true}) => + (super.noSuchMethod( + Invocation.method( + #getMetadata, + [], + {#allowCache: allowCache}, + ), + returnValue: _FakeMBTilesMetadata_0( + this, + Invocation.method( + #getMetadata, + [], + {#allowCache: allowCache}, + ), + ), + returnValueForMissingStub: _FakeMBTilesMetadata_0( + this, + Invocation.method( + #getMetadata, + [], + {#allowCache: allowCache}, + ), + ), + ) as _i2.MBTilesMetadata); + + @override + _i4.Uint8List? getTile({ + required int? z, + required int? x, + required int? y, + }) => + (super.noSuchMethod( + Invocation.method( + #getTile, + [], + { + #z: z, + #x: x, + #y: y, + }, + ), + returnValueForMissingStub: null, + ) as _i4.Uint8List?); + + @override + void createTables() => super.noSuchMethod( + Invocation.method( + #createTables, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void putTile({ + required int? z, + required int? x, + required int? y, + required _i4.Uint8List? bytes, + }) => + super.noSuchMethod( + Invocation.method( + #putTile, + [], + { + #z: z, + #x: x, + #y: y, + #bytes: bytes, + }, + ), + returnValueForMissingStub: null, + ); + + @override + void setMetadata(_i2.MBTilesMetadata? metadata) => super.noSuchMethod( + Invocation.method( + #setMetadata, + [metadata], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/flutter_map_mbtiles/test/utils/test_app.dart b/flutter_map_mbtiles/test/utils/test_app.dart new file mode 100644 index 0000000..7d3053c --- /dev/null +++ b/flutter_map_mbtiles/test/utils/test_app.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_mbtiles/src/mbtiles_tile_provider.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:mbtiles/mbtiles.dart'; + +// ignore_for_file: diagnostic_describe_all_properties + +class TestApp extends StatelessWidget { + final MbTiles mbtiles; + + const TestApp({super.key, required this.mbtiles}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: FlutterMap( + options: const MapOptions( + initialZoom: 0, + initialCenter: LatLng(0, 0), + ), + children: [ + TileLayer( + tileProvider: MbTilesTileProvider(mbtiles: mbtiles), + ), + ], + ), + ), + ); + } +} diff --git a/flutter_map_pmtiles/README.md b/flutter_map_pmtiles/README.md index de0489d..507498a 100644 --- a/flutter_map_pmtiles/README.md +++ b/flutter_map_pmtiles/README.md @@ -50,7 +50,6 @@ Widget build(BuildContext context) { options: MapOptions(), children: [ TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', // use your awaited PmTilesTileProvider tileProvider: tileProvider, ), diff --git a/flutter_map_pmtiles/example/example.md b/flutter_map_pmtiles/example/example.md index 5889ca2..1ef4634 100644 --- a/flutter_map_pmtiles/example/example.md +++ b/flutter_map_pmtiles/example/example.md @@ -11,7 +11,6 @@ Widget build(BuildContext context) { options: MapOptions(), children: [ TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', // use your awaited PmTilesTileProvider tileProvider: tileProvider, ), diff --git a/flutter_map_pmtiles/test/utils/test_app.dart b/flutter_map_pmtiles/test/utils/test_app.dart index 2300626..845e0b2 100644 --- a/flutter_map_pmtiles/test/utils/test_app.dart +++ b/flutter_map_pmtiles/test/utils/test_app.dart @@ -22,7 +22,6 @@ class TestApp extends StatelessWidget { ), children: [ TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', tileProvider: PmTilesTileProvider.fromArchive(pmTiles), ), ], diff --git a/melos.yaml b/melos.yaml index 371b48d..44cf18c 100644 --- a/melos.yaml +++ b/melos.yaml @@ -13,7 +13,7 @@ scripts: run: melos exec --file-exists="pubspec.yaml" --fail-fast flutter pub get package_score: description: Run pana for all packages - run: melos exec --fail-fast --ignore="flutter_map_plugins_example" pana --exit-code-threshold 0 --no-dartdoc . + run: melos exec --fail-fast --concurrency 1 --no-private pana --exit-code-threshold 0 --no-dartdoc . test_with_coverage: description: Run tests of all packages with coverage and merge its results run: | diff --git a/vector_map_tiles_pmtiles/pubspec.yaml b/vector_map_tiles_pmtiles/pubspec.yaml index 2a41eb9..1859ba0 100644 --- a/vector_map_tiles_pmtiles/pubspec.yaml +++ b/vector_map_tiles_pmtiles/pubspec.yaml @@ -24,4 +24,4 @@ dev_dependencies: build_runner: ^2.4.8 latlong2: ^0.9.0 flutter_map: ^6.1.0 - flutter_lints: ^3.0.0 \ No newline at end of file + flutter_lints: ^3.0.0