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

Support Flutter 3.10 (& Rework Tile Providers) #1512

Merged
merged 5 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions .github/workflows/delete_old_runs.yml

This file was deleted.

7 changes: 6 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
name: Analyse, Test & Build
on: [push, pull_request, workflow_dispatch]
on:
push:
branches:
- master
pull_request:
workflow_dispatch:

jobs:
score-package:
Expand Down
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
2 changes: 2 additions & 0 deletions example/lib/pages/network_tile_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ class NetworkTileProviderPage extends StatelessWidget {
padding: const EdgeInsets.all(8),
child: Column(
children: [
// ignore: prefer_const_constructors
Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8),
// ignore: prefer_const_constructors
JaffaKetchup marked this conversation as resolved.
Show resolved Hide resolved
child: Wrap(
children: const [
Text(
Expand Down
8 changes: 4 additions & 4 deletions lib/flutter_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export 'package:flutter_map/src/layer/tile_layer/tile_image.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_layer.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_provider/asset_tile_provider.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_provider/file_tile_provider_io.dart'
if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_io.dart'
if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_web.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart'
if (dart.library.io) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_io.dart'
if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_html.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_update_event.dart';
export 'package:flutter_map/src/layer/tile_layer/tile_update_transformer.dart';

Expand Down
46 changes: 23 additions & 23 deletions lib/src/layer/tile_layer/tile_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ 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_manager.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/asset_tile_provider.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_io.dart'
if (dart.library.html) 'package:flutter_map/src/layer/tile_layer/tile_provider/tile_provider_web.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/file_providers/tile_provider_stub.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_range.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_range_calculator.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_scale_calculator.dart';
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/flutter_map_state.dart';
import 'package:http/retry.dart';

part 'tile_layer_options.dart';

Expand Down Expand Up @@ -117,34 +119,32 @@ class TileLayer extends StatefulWidget {

/// Provider with which to load map tiles
///
/// The default is [NetworkNoRetryTileProvider]. Alternatively, use
/// [NetworkTileProvider] for a network provider which will retry requests.
/// The default is [NetworkTileProvider] which supports both IO and web
/// platforms. It uses a [RetryClient] to retry failed requests, but that can
/// be overriden by specifying [NetworkTileProvider.httpClient].
///
/// Both network providers will use some form of caching, although not reliable. For
/// better options, see https://docs.fleaflet.dev/usage/layers/tile-layer#caching.
/// Does not automatically cache (past Flutter's [ImageCache]). For options to
/// add offline mapping, see
/// https://docs.fleaflet.dev/tile-servers/offline-mapping.
///
/// `userAgentPackageName` is a construction parameter, which should be passed
/// the application's correct package name, such as 'com.example.app'. If no
/// value is passed, it defaults to 'unknown'. This parameter is used to form
/// part of the 'User-Agent' header, which is important to avoid blocking by
/// tile servers. Namely, the header is the following 'flutter_map (<packageName>)'.
///
/// Header rules are as follows, after 'User-Agent' is generated as above:
///
/// * If no provider is specified here, the default will be used with
/// 'User-Agent' header injected (recommended)
/// * If a provider is specified here with no 'User-Agent' header, that
/// provider will be used and the 'User-Agent' header will be injected
/// * If a provider is specified here with a 'User-Agent' header, that
/// provider will be used and the 'User-Agent' header will not be changed to any created here
/// `userAgentPackageName` is a [TileLayer] parameter, which should be passed
/// the application's correct package name, such as 'com.example.app'. This is
/// important to avoid blocking by tile servers due to high-levels of
/// unidentified traffic. This is passed through to the [NetworkTileProvider]
/// in a suitably formatted string, where it forms the 'User-Agent' header,
/// overriding any custom user agent specified in the HTTP client. To override
/// this behaviour, specify a 'User-Agent' key in the
/// [NetworkTileProvider.headers] property. If no value is passed, it defaults
/// to 'unknown'. This is all ignored on the web, where the 'User-Agent' header
/// cannot be changed due to a limitation of Dart/browsers.
///
/// [AssetTileProvider] and [FileTileProvider] are alternatives to network
/// providers, which use the [urlTemplate] as a path instead.
/// For example, 'assets/map/{z}/{x}/{y}.png' or
/// '/storage/emulated/0/map_app/tiles/{z}/{x}/{y}.png'.
///
/// Custom [TileProvider]s can also be used, but these will not follow the header
/// rules above.
/// Custom [TileProvider]s can also be used, but these will not necessarily
/// follow the header rules above.
final TileProvider tileProvider;

/// When panning the map, keep this many rows and columns of tiles before
Expand Down Expand Up @@ -280,7 +280,7 @@ class TileLayer extends StatefulWidget {
? const <String, String>{}
: Map.from(additionalOptions),
tileProvider = tileProvider == null
? NetworkNoRetryTileProvider(
? NetworkTileProvider(
headers: {'User-Agent': 'flutter_map ($userAgentPackageName)'},
)
: (tileProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart';

/// [TileProvider] to fetch tiles from the local filesystem (not asset store)
///
/// This web implmentation does not support reading from the local filesystem,
/// and therefore resorts to the web implementation of [NetworkTileProvider].
class FileTileProvider extends TileProvider {
@override
ImageProvider getImage(TileCoordinates coordinates, TileLayer options) =>
NetworkTileProvider().getImage(coordinates, options);
JaffaKetchup marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart';

/// [TileProvider] that uses [FileImage] internally on platforms other than web
/// [TileProvider] to fetch tiles from the local filesystem (not asset store)
///
/// Uses [FileImage] internally.
class FileTileProvider extends TileProvider {
FileTileProvider();

@override
ImageProvider getImage(TileCoordinates coordinates, TileLayer options) {
return FileImage(File(getTileUrl(coordinates, options)));
}
ImageProvider getImage(TileCoordinates coordinates, TileLayer options) =>
FileImage(File(getTileUrl(coordinates, options)));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:flutter/rendering.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_coordinates.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_layer.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/base_tile_provider.dart';
import 'package:flutter_map/src/layer/tile_layer/tile_provider/network_tile_provider.dart';

/// [TileProvider] to fetch tiles from the local filesystem (not asset store)
///
/// Stub for IO & web specific implementations.
///
/// The web implmentation does not support reading from the local filesystem, and
/// therefore resorts to the web implementation of [NetworkTileProvider].
class FileTileProvider extends TileProvider {
@override
ImageProvider getImage(TileCoordinates coordinates, TileLayer options) =>
throw UnimplementedError();
}
17 changes: 0 additions & 17 deletions lib/src/layer/tile_layer/tile_provider/file_tile_provider_web.dart

This file was deleted.

140 changes: 93 additions & 47 deletions lib/src/layer/tile_layer/tile_provider/network_image_provider.dart
Original file line number Diff line number Diff line change
@@ -1,73 +1,119 @@
import 'dart:async';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:http/http.dart' as http;
import 'package:http/retry.dart';
import 'package:http/http.dart';

class FMNetworkImageProvider extends ImageProvider<FMNetworkImageProvider> {
/// The URL from which the image will be fetched.
/// Dedicated [ImageProvider] to fetch tiles from the network
class FlutterMapNetworkImageProvider
extends ImageProvider<FlutterMapNetworkImageProvider> {
/// The URL to fetch the tile from (GET request)
final String url;

/// The fallback URL from which the image will be fetched.
/// The URL to fetch the tile from (GET request), in the event the original
/// [url] request fails
final String? fallbackUrl;

/// The http client that is used for the requests. Defaults to a [RetryClient]
/// with a [http.Client].
final http.Client httpClient;
/// The HTTP client to use to make network requests
final BaseClient httpClient;

/// Custom headers to add to the image fetch request
/// The headers to include with the tile fetch request
final Map<String, String> headers;

FMNetworkImageProvider(
this.url, {
/// Dedicated [ImageProvider] to fetch tiles from the network
FlutterMapNetworkImageProvider({
required this.url,
required this.fallbackUrl,
http.Client? httpClient,
this.headers = const {},
}) : httpClient = httpClient ?? RetryClient(http.Client());
required this.headers,
required this.httpClient,
});

// TODO: These [load] and [loadBuffer] method ensure support for Flutter 3.3
// thru 3.10, hence the multiple deprecation ignorances. Once the methods &
// types have been removed, they need to be replaced with [loadImage], and the
// min SDK constraint will need to be bumped.

@override
ImageStreamCompleter loadBuffer(
FMNetworkImageProvider key, DecoderBufferCallback decode) {
return OneFrameImageStreamCompleter(_loadWithRetry(key, decode),
informationCollector: () sync* {
yield ErrorDescription('Image provider: $this');
yield ErrorDescription('Image key: $key');
});
ImageStreamCompleter load(
FlutterMapNetworkImageProvider key,
// ignore: deprecated_member_use
DecoderCallback decode,
) {
final StreamController<ImageChunkEvent> chunkEvents =
StreamController<ImageChunkEvent>();

return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, chunkEvents, decodeDepreacted: decode),
chunkEvents: chunkEvents.stream,
scale: 1,
debugLabel: key.url,
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty('URL', url),
DiagnosticsProperty('Fallback URL', fallbackUrl),
DiagnosticsProperty('Current provider', key),
],
);
}

@override
Future<FMNetworkImageProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<FMNetworkImageProvider>(this);
ImageStreamCompleter loadBuffer(
FlutterMapNetworkImageProvider key,
// ignore: deprecated_member_use
DecoderBufferCallback decode,
) {
final StreamController<ImageChunkEvent> chunkEvents =
StreamController<ImageChunkEvent>();

return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, chunkEvents, decode: decode),
chunkEvents: chunkEvents.stream,
scale: 1,
debugLabel: key.url,
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty('URL', url),
DiagnosticsProperty('Fallback URL', fallbackUrl),
DiagnosticsProperty('Current provider', key),
],
);
}

Future<ImageInfo> _loadWithRetry(
FMNetworkImageProvider key,
DecoderBufferCallback decode, [
Future<Codec> _loadAsync(
FlutterMapNetworkImageProvider key,
StreamController<ImageChunkEvent> chunkEvents, {
// ignore: deprecated_member_use
DecoderBufferCallback? decode,
// ignore: deprecated_member_use
JaffaKetchup marked this conversation as resolved.
Show resolved Hide resolved
DecoderCallback? decodeDepreacted,
bool useFallback = false,
]) async {
assert(key == this);
assert(useFallback == false || fallbackUrl != null);

}) async {
final Uint8List bytes;
try {
final uri = Uri.parse(useFallback ? fallbackUrl! : url);
final response = await httpClient.get(uri, headers: headers);

if (response.statusCode != 200) {
throw NetworkImageLoadException(
statusCode: response.statusCode, uri: uri);
}

final codec =
await decode(await ImmutableBuffer.fromUint8List(response.bodyBytes));
final image = (await codec.getNextFrame()).image;
bytes = await httpClient.readBytes(
Uri.parse(useFallback ? fallbackUrl ?? '' : url),
headers: headers,
);
} catch (_) {
if (useFallback) rethrow;
return _loadAsync(
key,
chunkEvents,
decode: decode,
decodeDepreacted: decodeDepreacted,
useFallback: true,
);
}

return ImageInfo(image: image);
} catch (e) {
if (!useFallback && fallbackUrl != null) {
return _loadWithRetry(key, decode, true);
}
rethrow;
if (decode != null) {
return decode(await ImmutableBuffer.fromUint8List(bytes));
} else {
return decodeDepreacted!(bytes);
}
}

@override
Future<FlutterMapNetworkImageProvider> obtainKey(
ImageConfiguration configuration,
) =>
SynchronousFuture<FlutterMapNetworkImageProvider>(this);
}
Loading