Skip to content

Commit

Permalink
Added example for cancellable TileProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
JaffaKetchup committed Aug 27, 2023
1 parent 9fcb2ac commit 69ad3ed
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 0 deletions.
3 changes: 3 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +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/circle.dart';
import 'package:flutter_map_example/pages/custom_crs/custom_crs.dart';
import 'package:flutter_map_example/pages/epsg3413_crs.dart';
Expand Down Expand Up @@ -47,6 +48,8 @@ class MyApp extends StatelessWidget {
),
home: const HomePage(),
routes: <String, WidgetBuilder>{
CancellableTileProviderPage.route: (context) =>
const CancellableTileProviderPage(),
PolylinePage.route: (context) => const PolylinePage(),
MapControllerPage.route: (context) => const MapControllerPage(),
AnimatedMapControllerPage.route: (context) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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_example/widgets/drawer.dart';
import 'package:latlong2/latlong.dart';

class CancellableTileProviderPage extends StatelessWidget {
static const String route = '/cancellable_tile_provider_page';

const CancellableTileProviderPage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Cancellable Tile Provider')),
drawer: buildDrawer(context, CancellableTileProviderPage.route),
body: Column(
children: [
const Padding(
padding: EdgeInsets.all(12),
child: Text(
'This map uses a custom `TileProvider` that cancels HTTP requests for unnecessary tiles. This should help speed up tile loading and reduce unneccessary costly tile requests, mainly on the web!',
),
),
Expanded(
child: FlutterMap(
options: MapOptions(
initialCenter: const LatLng(51.5, -0.09),
initialZoom: 5,
cameraConstraint: CameraConstraint.contain(
bounds: LatLngBounds(
const LatLng(-90, -180),
const LatLng(90, 180),
),
),
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'dev.fleaflet.flutter_map.example',
tileProvider: CancellableNetworkTileProvider(),
),
],
),
),
],
),
);
}
}
115 changes: 115 additions & 0 deletions example/lib/pages/cancellable_tile_provider/ctp_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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<void> cancelLoading,
) =>
CancellableNetworkImageProvider(
url: getTileUrl(coordinates, options),
fallbackUrl: getTileFallbackUrl(coordinates, options),
headers: headers,
httpClient: httpClient,
cancelLoading: cancelLoading,
);
}

class CancellableNetworkImageProvider
extends ImageProvider<CancellableNetworkImageProvider> {
final String url;
final String? fallbackUrl;
final BaseClient httpClient;
final Map<String, String> headers;
final Future<void> 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<ImageChunkEvent>();

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<CancellableNetworkImageProvider> obtainKey(
ImageConfiguration configuration,
) =>
SynchronousFuture<CancellableNetworkImageProvider>(this);

Future<Codec> _loadAsync(
CancellableNetworkImageProvider key,
StreamController<ImageChunkEvent> 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<Uint8List>(
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));
}
}
7 changes: 7 additions & 0 deletions example/lib/widgets/drawer.dart
Original file line number Diff line number Diff line change
@@ -1,6 +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/circle.dart';
import 'package:flutter_map_example/pages/custom_crs/custom_crs.dart';
import 'package:flutter_map_example/pages/epsg3413_crs.dart';
Expand Down Expand Up @@ -153,6 +154,12 @@ Drawer buildDrawer(BuildContext context, String currentRoute) {
FallbackUrlNetworkPage.route,
currentRoute,
),
_buildMenuItem(
context,
const Text('Cancellable Tile Provider'),
CancellableTileProviderPage.route,
currentRoute,
),
const Divider(),
_buildMenuItem(
context,
Expand Down
2 changes: 2 additions & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies:
url_launcher: ^6.1.10
shared_preferences: ^2.1.1
url_strategy: ^0.2.0
http: ^1.1.0
dio: ^5.3.2

dev_dependencies:
flutter_lints: ^2.0.1
Expand Down

0 comments on commit 69ad3ed

Please sign in to comment.