Update flutter_image per recent changes to framework (#15)
* Update analysis options
* Clean up analysis errors
* Update/fix broken API calls
https://github.com/flutter/flutter/issues/31962
diff --git a/packages/flutter_image/.analysis_options b/packages/flutter_image/.analysis_options
deleted file mode 100644
index 754054c..0000000
--- a/packages/flutter_image/.analysis_options
+++ /dev/null
@@ -1,132 +0,0 @@
-# Specify Dart code analysis options.
-#
-# For a list of lints, see: http://dart-lang.github.io/linter/lints/
-# See the configuration guide for more
-# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
-#
-# This file contains the analysis options used by Flutter editors, such as Atom.
-
-analyzer:
- language:
- enableStrictCallChecks: true
- enableSuperMixins: true
- enableAssertInitializer: true
- strong-mode:
- implicit-dynamic: false
- errors:
- # treat missing required parameters as a warning (not a hint)
- missing_required_param: warning
- # treat missing returns as a warning (not a hint)
- missing_return: warning
- # allow having TODOs in the code
- todo: ignore
- exclude:
- - 'bin/cache/**'
- # the following two are relative to the stocks example and the flutter package respectively
- # see https://github.com/dart-lang/sdk/issues/28463
- - 'lib/i18n/stock_messages_*.dart'
- - 'lib/src/http/**'
-
-linter:
- rules:
- # these rules are documented on and in the same order as
- # the Dart Lint rules page to make maintenance easier
- # http://dart-lang.github.io/linter/lints/
-
- # === error rules ===
- - avoid_empty_else
- - avoid_slow_async_io
- - cancel_subscriptions
- # - close_sinks # https://github.com/flutter/flutter/issues/5789
- # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153
- - control_flow_in_finally
- - empty_statements
- - hash_and_equals
- # - invariant_booleans # https://github.com/flutter/flutter/issues/5790
- - iterable_contains_unrelated_type
- - list_remove_unrelated_type
- # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791
- - no_adjacent_strings_in_list
- - no_duplicate_case_values
- - test_types_in_equals
- - throw_in_finally
- - unrelated_type_equality_checks
- - valid_regexps
-
- # === style rules ===
- - always_declare_return_types
- - always_put_control_body_on_new_line
- - always_require_non_null_named_parameters
- - always_specify_types
- - annotate_overrides
- # - avoid_annotating_with_dynamic # not yet tested
- - avoid_as
- # - avoid_catches_without_on_clauses # not yet tested
- # - avoid_catching_errors # not yet tested
- # - avoid_classes_with_only_static_members # not yet tested
- # - avoid_function_literals_in_foreach_calls # not yet tested
- - avoid_init_to_null
- - avoid_null_checks_in_equality_operators
- # - avoid_positional_boolean_parameters # not yet tested
- - avoid_return_types_on_setters
- # - avoid_returning_null # not yet tested
- # - avoid_returning_this # not yet tested
- # - avoid_setters_without_getters # not yet tested
- # - avoid_types_on_closure_parameters # not yet tested
- - await_only_futures
- - camel_case_types
- # - cascade_invocations # not yet tested
- # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
- - directives_ordering
- - empty_catches
- - empty_constructor_bodies
- - implementation_imports
- # - join_return_with_assignment # not yet tested
- - library_names
- - library_prefixes
- - non_constant_identifier_names
- # - omit_local_variable_types # opposite of always_specify_types
- # - one_member_abstracts # too many false positives
- # - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- - overridden_fields
- - package_api_docs
- - package_prefixed_library_names
- # - parameter_assignments # we do this commonly
- - prefer_adjacent_string_concatenation
- - prefer_collection_literals
- # - prefer_conditional_assignment # not yet tested
- - prefer_const_constructors
- # - prefer_constructors_over_static_methods # not yet tested
- - prefer_contains
- # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506
- - prefer_final_locals
- # - prefer_foreach # not yet tested
- # - prefer_function_declarations_over_variables # not yet tested
- - prefer_initializing_formals
- # - prefer_interpolation_to_compose_strings # not yet tested
- - prefer_is_empty
- - prefer_is_not_empty
- # - public_member_api_docs # this is the only difference from .analysis_options_repo
- # - recursive_getters # https://github.com/dart-lang/linter/issues/452
- - slash_for_doc_comments
- - sort_constructors_first
- - sort_unnamed_constructors_first
- - super_goes_last
- # - type_annotate_public_apis # subset of always_specify_types
- - type_init_formals
- # - unawaited_futures # https://github.com/flutter/flutter/issues/5793
- - unnecessary_brace_in_string_interps
- - unnecessary_getters_setters
- # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498
- - unnecessary_null_aware_assignments
- - unnecessary_null_in_if_null_operators
- # - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627
- - unnecessary_this
- - use_rethrow_when_possible
- # - use_setters_to_change_properties # not yet tested
- # - use_string_buffers # https://github.com/dart-lang/linter/pull/664
- # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
-
- # === pub rules ===
- - package_names
diff --git a/packages/flutter_image/lib/network.dart b/packages/flutter_image/lib/network.dart
index 0949316..abffcdc 100644
--- a/packages/flutter_image/lib/network.dart
+++ b/packages/flutter_image/lib/network.dart
@@ -24,17 +24,18 @@
/// [defaultFetchStrategy] to obtain instructions for fetching the URL.
///
/// The image will be cached regardless of cache headers from the server.
+@immutable
class NetworkImageWithRetry extends ImageProvider<NetworkImageWithRetry> {
/// Creates an object that fetches the image at the given [url].
///
/// The arguments must not be null.
- const NetworkImageWithRetry(this.url, { this.scale: 1.0, this.fetchStrategy: defaultFetchStrategy })
+ const NetworkImageWithRetry(this.url, { this.scale = 1.0, this.fetchStrategy = defaultFetchStrategy })
: assert(url != null),
assert(scale != null),
assert(fetchStrategy != null);
/// The HTTP client used to download images.
- static final io.HttpClient _client = new io.HttpClient();
+ static final io.HttpClient _client = io.HttpClient();
/// The URL from which the image will be fetched.
final String url;
@@ -65,12 +66,12 @@
@override
Future<NetworkImageWithRetry> obtainKey(ImageConfiguration configuration) {
- return new SynchronousFuture<NetworkImageWithRetry>(this);
+ return SynchronousFuture<NetworkImageWithRetry>(this);
}
@override
ImageStreamCompleter load(NetworkImageWithRetry key) {
- return new OneFrameImageStreamCompleter(
+ return OneFrameImageStreamCompleter(
_loadWithRetry(key),
informationCollector: () sync* {
yield ErrorDescription('Image provider: $this');
@@ -83,13 +84,13 @@
assert(() {
if (instructions == null) {
if (fetchStrategy == defaultFetchStrategy) {
- throw new StateError(
+ throw StateError(
'The default FetchStrategy returned null FetchInstructions. This\n'
'is likely a bug in $runtimeType. Please file a bug at\n'
'https://github.com/flutter/flutter/issues.'
);
} else {
- throw new StateError(
+ throw StateError(
'The custom FetchStrategy used to fetch $url returned null\n'
'FetchInstructions. FetchInstructions must never be null, but\n'
'instead instruct to either make another fetch attempt or give up.'
@@ -103,7 +104,7 @@
Future<ImageInfo> _loadWithRetry(NetworkImageWithRetry key) async {
assert(key == this);
- final Stopwatch stopwatch = new Stopwatch()..start();
+ final Stopwatch stopwatch = Stopwatch()..start();
final Uri resolved = Uri.base.resolve(key.url);
FetchInstructions instructions = await fetchStrategy(resolved, null);
_debugCheckInstructions(instructions);
@@ -118,7 +119,7 @@
final io.HttpClientResponse response = await request.close().timeout(instructions.timeout);
if (response == null || response.statusCode != 200) {
- throw new FetchFailure._(
+ throw FetchFailure._(
totalDuration: stopwatch.elapsed,
attemptCount: attemptCount,
httpStatusCode: response.statusCode,
@@ -126,7 +127,7 @@
}
final _Uint8ListBuilder builder = await response.fold(
- new _Uint8ListBuilder(),
+ _Uint8ListBuilder(),
(_Uint8ListBuilder buffer, List<int> bytes) => buffer..add(bytes),
).timeout(instructions.timeout);
@@ -139,7 +140,7 @@
if (image == null)
return null;
- return new ImageInfo(
+ return ImageInfo(
image: image,
scale: key.scale,
);
@@ -147,7 +148,7 @@
request?.close();
lastFailure = error is FetchFailure
? error
- : new FetchFailure._(
+ : FetchFailure._(
totalDuration: stopwatch.elapsed,
attemptCount: attemptCount,
originalException: error,
@@ -162,7 +163,7 @@
assert(lastFailure != null);
- FlutterError.onError(new FlutterErrorDetails(
+ FlutterError.onError(FlutterErrorDetails(
exception: lastFailure,
library: 'package:flutter_image',
context: ErrorDescription('$runtimeType failed to load ${instructions.uri}'),
@@ -191,7 +192,7 @@
///
/// The instructions are executed as soon as possible after the returned
/// [Future] resolves. If a delay in necessary between retries, use a delayed
-/// [Future], such as [new Future.delayed]. This is useful for implementing
+/// [Future], such as [Future.delayed]. This is useful for implementing
/// back-off strategies and for recovering from lack of connectivity.
///
/// [uri] is the last requested image URI. A [FetchStrategy] may choose to use
@@ -205,7 +206,7 @@
/// [NetworkImageWithRetry] to try again.
///
/// See [NetworkImageWithRetry.defaultFetchStrategy] for an example.
-typedef Future<FetchInstructions> FetchStrategy(Uri uri, FetchFailure failure);
+typedef FetchStrategy = Future<FetchInstructions> Function(Uri uri, FetchFailure failure);
/// Instructions [NetworkImageWithRetry] uses to fetch the image.
@immutable
@@ -295,9 +296,9 @@
static const int _kInitialSize = 100000; // 100KB-ish
int _usedLength = 0;
- Uint8List _buffer = new Uint8List(_kInitialSize);
+ Uint8List _buffer = Uint8List(_kInitialSize);
- Uint8List get data => new Uint8List.view(_buffer.buffer, 0, _usedLength);
+ Uint8List get data => Uint8List.view(_buffer.buffer, 0, _usedLength);
void add(List<int> bytes) {
_ensureCanAdd(bytes.length);
@@ -314,7 +315,7 @@
}
if (newLength != _buffer.length) {
- final Uint8List newBuffer = new Uint8List(newLength);
+ final Uint8List newBuffer = Uint8List(newLength);
newBuffer.setAll(0, _buffer);
newBuffer.setRange(0, _usedLength, _buffer);
_buffer = newBuffer;
@@ -323,7 +324,7 @@
}
/// Determines whether the given HTTP [statusCode] is transient.
-typedef bool TransientHttpStatusCodePredicate(int statusCode);
+typedef TransientHttpStatusCodePredicate = bool Function(int statusCode);
/// Builds a [FetchStrategy] function that retries up to a certain amount of
/// times for up to a certain amount of time.
@@ -333,11 +334,28 @@
/// those HTTP status codes considered transient by a
/// [transientHttpStatusCodePredicate] function.
class FetchStrategyBuilder {
+ /// Creates a fetch strategy builder.
+ ///
+ /// All parameters must be non-null.
+ const FetchStrategyBuilder({
+ this.timeout = const Duration(seconds: 30),
+ this.totalFetchTimeout = const Duration(minutes: 1),
+ this.maxAttempts = 5,
+ this.initialPauseBetweenRetries = const Duration(seconds: 1),
+ this.exponentialBackoffMultiplier = 2,
+ this.transientHttpStatusCodePredicate = defaultTransientHttpStatusCodePredicate,
+ }) : assert(timeout != null),
+ assert(totalFetchTimeout != null),
+ assert(maxAttempts != null),
+ assert(initialPauseBetweenRetries != null),
+ assert(exponentialBackoffMultiplier != null),
+ assert(transientHttpStatusCodePredicate != null);
+
/// A list of HTTP status codes that can generally be retried.
///
/// You may want to use a different list depending on the needs of your
/// application.
- static const List<int> defaultTransientHttpStatusCodes = const <int>[
+ static const List<int> defaultTransientHttpStatusCodes = <int>[
0, // Network error
408, // Request timeout
500, // Internal server error
@@ -346,23 +364,6 @@
504 // Gateway timeout
];
- /// Creates a fetch strategy builder.
- ///
- /// All parameters must be non-null.
- const FetchStrategyBuilder({
- this.timeout: const Duration(seconds: 30),
- this.totalFetchTimeout: const Duration(minutes: 1),
- this.maxAttempts: 5,
- this.initialPauseBetweenRetries: const Duration(seconds: 1),
- this.exponentialBackoffMultiplier: 2,
- this.transientHttpStatusCodePredicate: defaultTransientHttpStatusCodePredicate,
- }) : assert(timeout != null),
- assert(totalFetchTimeout != null),
- assert(maxAttempts != null),
- assert(initialPauseBetweenRetries != null),
- assert(exponentialBackoffMultiplier != null),
- assert(transientHttpStatusCodePredicate != null);
-
/// Maximum amount of time a single fetch attempt is allowed to take.
final Duration timeout;
@@ -396,7 +397,7 @@
return (Uri uri, FetchFailure failure) async {
if (failure == null) {
// First attempt. Just load.
- return new FetchInstructions.attempt(
+ return FetchInstructions.attempt(
uri: uri,
timeout: timeout,
);
@@ -409,15 +410,15 @@
if (!isRetriableFailure || // retrying will not help
failure.totalDuration > totalFetchTimeout || // taking too long
failure.attemptCount > maxAttempts) { // too many attempts
- return new FetchInstructions.giveUp(uri: uri);
+ return FetchInstructions.giveUp(uri: uri);
}
// Exponential back-off.
final Duration pauseBetweenRetries = initialPauseBetweenRetries * math.pow(exponentialBackoffMultiplier, failure.attemptCount - 1);
- await new Future<Null>.delayed(pauseBetweenRetries);
+ await Future<void>.delayed(pauseBetweenRetries);
// Retry.
- return new FetchInstructions.attempt(
+ return FetchInstructions.attempt(
uri: uri,
timeout: timeout,
);
diff --git a/packages/flutter_image/pubspec.yaml b/packages/flutter_image/pubspec.yaml
index eb48176..b7ffcdd 100644
--- a/packages/flutter_image/pubspec.yaml
+++ b/packages/flutter_image/pubspec.yaml
@@ -1,5 +1,5 @@
name: flutter_image
-version: 2.0.0-dev
+version: 2.0.0
description: >
Image utilities for Flutter: providers, effects, etc
author: Flutter Team <flutter-dev@googlegroups.com>
@@ -10,7 +10,7 @@
sdk: flutter
dev_dependencies:
- quiver: '>=0.24.0 <2.0.0'
+ quiver: '>=0.24.0 <3.0.0'
test: any
flutter_test:
sdk: flutter
diff --git a/packages/flutter_image/test/network_test.dart b/packages/flutter_image/test/network_test.dart
index 8a734ef..46147ae 100644
--- a/packages/flutter_image/test/network_test.dart
+++ b/packages/flutter_image/test/network_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
+import 'dart:io' show HttpOverrides;
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_image/network.dart';
@@ -14,6 +15,9 @@
}
void main() {
+ AutomatedTestWidgetsFlutterBinding();
+ HttpOverrides.global = null;
+
group('NetworkImageWithRetry', () {
setUp(() {
FlutterError.onError = (FlutterErrorDetails error) {
@@ -26,35 +30,37 @@
});
test('loads image from network', () async {
- final NetworkImageWithRetry subject = new NetworkImageWithRetry(
+ final NetworkImageWithRetry subject = NetworkImageWithRetry(
_imageUrl('immediate_success.png'),
);
- subject.load(subject).addListener(expectAsync2((ImageInfo image, bool synchronousCall) {
- expect(image.image.height, 1);
- expect(image.image.width, 1);
- }));
+ subject.load(subject).addListener(
+ ImageStreamListener(expectAsync2((ImageInfo image, bool synchronousCall) {
+ expect(image.image.height, 1);
+ expect(image.image.width, 1);
+ })),
+ );
});
test('retries 6 times then gives up', () async {
final List<FlutterErrorDetails> errorLog = <FlutterErrorDetails>[];
FlutterError.onError = errorLog.add;
- final FakeAsync fakeAsync = new FakeAsync();
+ final FakeAsync fakeAsync = FakeAsync();
final dynamic maxAttemptCountReached = expectAsync0(() {});
int attemptCount = 0;
- Future<Null> onAttempt() async {
+ Future<void> onAttempt() async {
expect(attemptCount, lessThan(7));
if (attemptCount == 6) {
maxAttemptCountReached();
}
- await new Future<Null>.delayed(Duration.ZERO);
+ await Future<void>.delayed(Duration.zero);
fakeAsync.elapse(const Duration(seconds: 60));
attemptCount++;
}
- final NetworkImageWithRetry subject = new NetworkImageWithRetry(
+ final NetworkImageWithRetry subject = NetworkImageWithRetry(
_imageUrl('error.png'),
fetchStrategy: (Uri uri, FetchFailure failure) {
Timer.run(onAttempt);
@@ -64,27 +70,29 @@
},
);
- subject.load(subject).addListener(expectAsync2((ImageInfo image, bool synchronousCall) {
- expect(errorLog.single.exception, const isInstanceOf<FetchFailure>());
- expect(image, null);
- }));
+ subject.load(subject).addListener(
+ ImageStreamListener(expectAsync2((ImageInfo image, bool synchronousCall) {
+ expect(errorLog.single.exception, isInstanceOf<FetchFailure>());
+ expect(image, null);
+ })),
+ );
});
test('gives up immediately on non-retriable errors (HTTP 404)', () async {
final List<FlutterErrorDetails> errorLog = <FlutterErrorDetails>[];
FlutterError.onError = errorLog.add;
- final FakeAsync fakeAsync = new FakeAsync();
+ final FakeAsync fakeAsync = FakeAsync();
int attemptCount = 0;
- Future<Null> onAttempt() async {
+ Future<void> onAttempt() async {
expect(attemptCount, lessThan(2));
- await new Future<Null>.delayed(Duration.ZERO);
+ await Future<void>.delayed(Duration.zero);
fakeAsync.elapse(const Duration(seconds: 60));
attemptCount++;
}
- final NetworkImageWithRetry subject = new NetworkImageWithRetry(
+ final NetworkImageWithRetry subject = NetworkImageWithRetry(
_imageUrl('does_not_exist.png'),
fetchStrategy: (Uri uri, FetchFailure failure) {
Timer.run(onAttempt);
@@ -94,24 +102,26 @@
},
);
- subject.load(subject).addListener(expectAsync2((ImageInfo image, bool synchronousCall) {
- expect(errorLog.single.exception, const isInstanceOf<FetchFailure>());
- expect(image, null);
- }));
+ subject.load(subject).addListener(
+ ImageStreamListener(expectAsync2((ImageInfo image, bool synchronousCall) {
+ expect(errorLog.single.exception, isInstanceOf<FetchFailure>());
+ expect(image, null);
+ })),
+ );
});
test('succeeds on successful retry', () async {
- final NetworkImageWithRetry subject = new NetworkImageWithRetry(
+ final NetworkImageWithRetry subject = NetworkImageWithRetry(
_imageUrl('error.png'),
fetchStrategy: (Uri uri, FetchFailure failure) async {
if (failure == null) {
- return new FetchInstructions.attempt(
+ return FetchInstructions.attempt(
uri: uri,
timeout: const Duration(minutes: 1),
);
} else {
expect(failure.attemptCount, lessThan(2));
- return new FetchInstructions.attempt(
+ return FetchInstructions.attempt(
uri: Uri.parse(_imageUrl('immediate_success.png')),
timeout: const Duration(minutes: 1),
);
@@ -119,10 +129,12 @@
},
);
- subject.load(subject).addListener(expectAsync2((ImageInfo image, bool synchronousCall) {
- expect(image.image.height, 1);
- expect(image.image.width, 1);
- }));
+ subject.load(subject).addListener(
+ ImageStreamListener(expectAsync2((ImageInfo image, bool synchronousCall) {
+ expect(image.image.height, 1);
+ expect(image.image.width, 1);
+ })),
+ );
});
});
}
diff --git a/packages/flutter_image/test/network_test_server.dart b/packages/flutter_image/test/network_test_server.dart
index aa5c212..a6c6eaa 100644
--- a/packages/flutter_image/test/network_test_server.dart
+++ b/packages/flutter_image/test/network_test_server.dart
@@ -7,8 +7,8 @@
const int _kTestServerPort = 11111;
-Future<Null> main() async {
- final HttpServer testServer = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, _kTestServerPort);
+Future<void> main() async {
+ final HttpServer testServer = await HttpServer.bind(InternetAddress.loopbackIPv4, _kTestServerPort);
await for (HttpRequest request in testServer) {
if (request.uri.path.endsWith('/immediate_success.png')) {
request.response.add(_kTransparentImage);
@@ -22,7 +22,7 @@
}
}
-const List<int> _kTransparentImage = const <int>[
+const List<int> _kTransparentImage = <int>[
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44,