Skip to content

Commit

Permalink
feat: add mbtiles tile provider for flutter_map (#21)
Browse files Browse the repository at this point in the history
* add flutter_map_mbtiles package

* add files

* fix errors

* update example page

* add doc strings

* mbtiles example: support for non desktop plattforms

* update readme files

* disabled banner for example app

* add tests

* don't expose the mbtiles package

* add `silenceTileNotFound` parameter

* bump dependencies

- mbtiles
- codecov workflow

* add examples, sort example cards alphabetically

* add `silenceTileNotFound` param, only dispose if created internally

* fix example

* disable vector_map_tiles_pmtiles on web

vector_map_tiles is not compatible with web

* small fixes in docs, examples and tests

* more fixes in docs

* update vector_map_tiles_mbtiles

* add flutter_map_mbtiles

* remove vector_map_tiles_mbtiles

* Update main.dart

* Update pubspec.yaml

* score packages one by one

* fix git url

* mbtiles ^0.3.1
  • Loading branch information
josxha authored Feb 12, 2024
1 parent dd23e50 commit c9945ae
Show file tree
Hide file tree
Showing 26 changed files with 665 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/on-commit-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Binary file added example/assets/mbtiles/countries-raster.mbtiles
Binary file not shown.
Binary file added example/assets/mbtiles/countries-vector.mbtiles
Binary file not shown.
Binary file added example/assets/mbtiles/malta-vector.mbtiles
Binary file not shown.
22 changes: 22 additions & 0 deletions example/lib/common/utils.dart
Original file line number Diff line number Diff line change
@@ -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<File> 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;
}
88 changes: 88 additions & 0 deletions example/lib/flutter_map_mbtiles/page.dart
Original file line number Diff line number Diff line change
@@ -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<FlutterMapMbTilesPage> createState() => _FlutterMapMbTilesPageState();
}

class _FlutterMapMbTilesPageState extends State<FlutterMapMbTilesPage> {
final Future<MbTiles> _futureMbtiles = _initMbtiles();
MbTiles? _mbtiles;

static Future<MbTiles> _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<MbTiles>(
future: _futureMbtiles,
builder: (context, snapshot) {
if (snapshot.hasData) {
_mbtiles = snapshot.data;
final metadata = _mbtiles!.getMetadata();
return Column(
children: <Widget>[
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();
}
}
69 changes: 54 additions & 15 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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(),
},
);
Expand All @@ -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',
Expand Down Expand Up @@ -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,
),
);
}
Expand Down
25 changes: 12 additions & 13 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,30 @@ 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
dio_cache_interceptor: ^3.4.2
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:
Expand All @@ -47,3 +44,5 @@ dev_dependencies:

flutter:
uses-material-design: true
assets:
- assets/mbtiles/
2 changes: 1 addition & 1 deletion flutter_map_cache/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions flutter_map_mbtiles/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## [1.0.0] 2024-mm-dd

- Initial release
21 changes: 21 additions & 0 deletions flutter_map_mbtiles/LICENSE
Original file line number Diff line number Diff line change
@@ -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.
Loading

0 comments on commit c9945ae

Please sign in to comment.