Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Post process tiles #582

Merged
merged 7 commits into from Mar 16, 2021
Merged
2 changes: 2 additions & 0 deletions example/lib/main.dart
Expand Up @@ -19,6 +19,7 @@ import './pages/plugin_zoombuttons.dart';
import './pages/polyline.dart';
import './pages/sliding_map.dart';
import './pages/tap_to_add.dart';
import './pages/tile_builder_example.dart';
import './pages/tile_loading_error_handle.dart';
import './pages/widgets.dart';
import './pages/wms_tile_layer.dart';
Expand Down Expand Up @@ -58,6 +59,7 @@ class MyApp extends StatelessWidget {
CustomCrsPage.route: (context) => CustomCrsPage(),
LiveLocationPage.route: (context) => LiveLocationPage(),
TileLoadingErrorHandle.route: (context) => TileLoadingErrorHandle(),
TileBuilderPage.route: (context) => TileBuilderPage(),
},
);
}
Expand Down
142 changes: 142 additions & 0 deletions example/lib/pages/tile_builder_example.dart
@@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong/latlong.dart';

import '../widgets/drawer.dart';

class TileBuilderPage extends StatefulWidget {
static const String route = '/tile_builder_example';

@override
_TileBuilderPageState createState() => _TileBuilderPageState();
}

class _TileBuilderPageState extends State<TileBuilderPage> {
var darkMode = false;
var loadingTime = false;
var showCoords = false;
var grid = false;

// mix of [coordinateDebugTileBuilder] and [loadingTimeDebugTileBuilder] from tile_builder.dart
Widget tileBuilder(BuildContext context, Widget tileWidget, Tile tile) {
final coords = tile.coords;

return Container(
decoration: BoxDecoration(
border: grid ? Border.all() : null,
),
child: Stack(
fit: StackFit.passthrough,
children: [
tileWidget,
if (loadingTime || showCoords)
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (showCoords)
Text(
'${coords.x.floor()} : ${coords.y.floor()} : ${coords.z.floor()}',
style: Theme.of(context).textTheme.headline5,
),
if (loadingTime)
Text(
tile.loaded == null
? 'Loading'
// sometimes result is negative which shouldn't happen, abs() corrects it
: '${(tile.loaded.millisecond - tile.loadStarted.millisecond).abs()} ms',
style: Theme.of(context).textTheme.headline5,
),
],
),
],
),
);
}

@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),
),
SizedBox(height: 8),
FloatingActionButton.extended(
heroTag: 'coords',
label: Text(
showCoords ? 'Hide coords' : 'Show coords',
textAlign: TextAlign.center,
),
icon: Icon(showCoords ? Icons.unarchive : Icons.bug_report),
onPressed: () => setState(() => showCoords = !showCoords),
),
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),
),
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),
),
],
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: FlutterMap(
options: MapOptions(
center: LatLng(51.5, -0.09),
zoom: 5.0,
),
layers: [
TileLayerOptions(
urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'],
tileProvider: NonCachingNetworkTileProvider(),
tileBuilder: tileBuilder,
tilesContainerBuilder:
darkMode ? darkModeTilesContainerBuilder : null,
),
MarkerLayerOptions(
markers: <Marker>[
Marker(
width: 80.0,
height: 80.0,
point: LatLng(51.5, -0.09),
builder: (ctx) => Container(
child: FlutterLogo(
colors: Colors.blue,
key: ObjectKey(Colors.blue),
),
),
),
],
)
],
),
),
);
}
}
57 changes: 30 additions & 27 deletions example/lib/widgets/drawer.dart
Expand Up @@ -19,6 +19,7 @@ import '../pages/plugin_zoombuttons.dart';
import '../pages/polyline.dart';
import '../pages/sliding_map.dart';
import '../pages/tap_to_add.dart';
import '../pages/tile_builder_example.dart';
import '../pages/tile_loading_error_handle.dart';
import '../pages/widgets.dart';
import '../pages/wms_tile_layer.dart';
Expand Down Expand Up @@ -157,33 +158,35 @@ Drawer buildDrawer(BuildContext context, String currentRoute) {
OverlayImagePage.route,
currentRoute,
),
ListTile(
title: const Text('Sliding Map'),
selected: currentRoute == SlidingMapPage.route,
onTap: () =>
Navigator.pushReplacementNamed(context, SlidingMapPage.route),
),
ListTile(
title: const Text('Widgets'),
selected: currentRoute == WidgetsPage.route,
onTap: () {
Navigator.pushReplacementNamed(context, WidgetsPage.route);
},
),
ListTile(
title: const Text('Live Location Update'),
selected: currentRoute == LiveLocationPage.route,
onTap: () {
Navigator.pushReplacementNamed(context, LiveLocationPage.route);
},
),
ListTile(
title: const Text('Tile loading error handle'),
selected: currentRoute == TileLoadingErrorHandle.route,
onTap: () {
Navigator.pushReplacementNamed(
context, TileLoadingErrorHandle.route);
},
_buildMenuItem(
context,
const Text('Sliding Map'),
SlidingMapPage.route,
currentRoute,
),
_buildMenuItem(
context,
const Text('Widgets'),
WidgetsPage.route,
currentRoute,
),
_buildMenuItem(
context,
const Text('Live Location Update'),
LiveLocationPage.route,
currentRoute,
),
_buildMenuItem(
context,
const Text('Tile loading error handle'),
TileLoadingErrorHandle.route,
currentRoute,
),
_buildMenuItem(
context,
const Text('Tile builder'),
TileBuilderPage.route,
currentRoute,
),
],
),
Expand Down
1 change: 1 addition & 0 deletions lib/flutter_map.dart
Expand Up @@ -21,6 +21,7 @@ export 'package:flutter_map/src/layer/marker_layer.dart';
export 'package:flutter_map/src/layer/overlay_image_layer.dart';
export 'package:flutter_map/src/layer/polygon_layer.dart';
export 'package:flutter_map/src/layer/polyline_layer.dart';
export 'package:flutter_map/src/layer/tile_builder/tile_builder.dart';
export 'package:flutter_map/src/layer/tile_layer.dart';
export 'package:flutter_map/src/layer/tile_provider/mbtiles_image_provider.dart';
export 'package:flutter_map/src/layer/tile_provider/tile_provider.dart';
Expand Down
124 changes: 124 additions & 0 deletions lib/src/layer/tile_builder/tile_builder.dart
@@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/src/layer/tile_layer.dart';

typedef TileBuilder = Widget Function(
BuildContext context, Widget tileWidget, Tile tile);

typedef TilesContainerBuilder = Widget Function(
BuildContext context, Widget tilesContainer, List<Tile> tiles);

/// Applies inversion color matrix on Tiles container which may simulate Dark mode.
final TilesContainerBuilder darkModeTilesContainerBuilder =
(BuildContext context, Widget tilesContainer, List<Tile> tiles) {
return ColorFiltered(
colorFilter: const ColorFilter.matrix(<double>[
-1,
0,
0,
0,
255,
0,
-1,
0,
0,
255,
0,
0,
-1,
0,
255,
0,
0,
0,
1,
0,
]),
child: tilesContainer,
);
};

/// Applies inversion color matrix on Tiles which may simulate Dark mode.
/// [darkModeTilesContainerBuilder] is better at performance because it applies color matrix on the container instead of on every Tile
final TileBuilder darkModeTileBuilder =
(BuildContext context, Widget tileWidget, Tile tile) {
return ColorFiltered(
colorFilter: const ColorFilter.matrix(<double>[
-1,
0,
0,
0,
255,
0,
-1,
0,
0,
255,
0,
0,
-1,
0,
255,
0,
0,
0,
1,
0,
]),
child: tileWidget,
);
};

/// Shows coordinates over Tiles
final TileBuilder coordinateDebugTileBuilder =
(BuildContext context, Widget tileWidget, Tile tile) {
final coords = tile.coords;
final readableKey =
'${coords.x.floor()} : ${coords.y.floor()} : ${coords.z.floor()}';

return Container(
decoration: BoxDecoration(
border: Border.all(),
),
child: Stack(
fit: StackFit.passthrough,
children: [
tileWidget,
Center(
child: Text(
readableKey,
style: Theme.of(context).textTheme.headline5,
),
),
],
),
);
};

/// Shows the Tile loading time in ms
final TileBuilder loadingTimeDebugTileBuilder =
(BuildContext context, Widget tileWidget, Tile tile) {
var loadStarted = tile.loadStarted;
var loaded = tile.loaded;

final time = loaded == null
? 'Loading'
: '${(loaded.millisecond - loadStarted.millisecond).abs()} ms';

return Container(
decoration: BoxDecoration(
border: Border.all(),
),
child: Stack(
fit: StackFit.passthrough,
children: [
tileWidget,
Center(
child: Text(
time,
style: Theme.of(context).textTheme.headline5,
),
),
],
),
);
};