forked from firka/flutter
Use MultiFrameImageStreamProvider in the various image providers. (#12997)
This commit is contained in:
@@ -3,12 +3,15 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show createHttpClient;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../lib/card_collection.dart' as card_collection;
|
||||
import 'mock_image_http.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Card Collection smoke test', (WidgetTester tester) async {
|
||||
createHttpClient = createMockImageHttpClient;
|
||||
card_collection.main(); // builds the app and schedules a frame but doesn't trigger one
|
||||
await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
|
||||
await tester.pump(); // triggers a frame
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show createHttpClient;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../lib/color_testing_demo.dart' as color_testing_demo;
|
||||
import 'mock_image_http.dart';
|
||||
|
||||
void main() {
|
||||
|
||||
testWidgets('Color testing demo smoke test', (WidgetTester tester) async {
|
||||
createHttpClient = createMockImageHttpClient;
|
||||
color_testing_demo.main(); // builds the app and schedules a frame but doesn't trigger one
|
||||
await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
|
||||
await tester.pump(); // triggers a frame
|
||||
|
||||
15
dev/manual_tests/test/mock_image_http.dart
Normal file
15
dev/manual_tests/test/mock_image_http.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart' show ValueGetter;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/testing.dart' as http;
|
||||
|
||||
import '../../../packages/flutter/test/services/image_data.dart';
|
||||
|
||||
// Returns a mock HTTP client that responds with an image to all requests.
|
||||
ValueGetter<http.Client> createMockImageHttpClient = () {
|
||||
return new http.MockClient((http.BaseRequest request) {
|
||||
return new Future<http.Response>.value(
|
||||
new http.Response.bytes(kTransparentImage, 200, request: request)
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -5,7 +5,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io' show File;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui show Image;
|
||||
import 'dart:ui' as ui show instantiateImageCodec, Codec;
|
||||
import 'dart:ui' show Size, Locale, TextDirection, hashValues;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -14,7 +14,6 @@ import 'package:http/http.dart' as http;
|
||||
import 'asset_bundle.dart';
|
||||
import 'http_client.dart';
|
||||
import 'image_cache.dart';
|
||||
import 'image_decoder.dart';
|
||||
import 'image_stream.dart';
|
||||
|
||||
/// Configuration information passed to the [ImageProvider.resolve] method to
|
||||
@@ -365,8 +364,9 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
|
||||
/// image using [loadAsync].
|
||||
@override
|
||||
ImageStreamCompleter load(AssetBundleImageKey key) {
|
||||
return new OneFrameImageStreamCompleter(
|
||||
loadAsync(key),
|
||||
return new MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key),
|
||||
scale: key.scale,
|
||||
informationCollector: (StringBuffer information) {
|
||||
information.writeln('Image provider: $this');
|
||||
information.write('Image key: $key');
|
||||
@@ -379,23 +379,11 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
|
||||
///
|
||||
/// This function is used by [load].
|
||||
@protected
|
||||
Future<ImageInfo> loadAsync(AssetBundleImageKey key) async {
|
||||
Future<ui.Codec> _loadAsync(AssetBundleImageKey key) async {
|
||||
final ByteData data = await key.bundle.load(key.name);
|
||||
if (data == null)
|
||||
throw 'Unable to read data';
|
||||
final ui.Image image = await decodeImage(data);
|
||||
if (image == null)
|
||||
throw 'Unable to decode image data';
|
||||
return new ImageInfo(image: image, scale: key.scale);
|
||||
}
|
||||
|
||||
/// Converts raw image data from a [ByteData] buffer into a decoded
|
||||
/// [ui.Image] which can be passed to a [Canvas].
|
||||
///
|
||||
/// By default, this just uses [decodeImageFromList]. This method could be
|
||||
/// overridden in subclasses (e.g. for testing).
|
||||
Future<ui.Image> decodeImage(ByteData data) {
|
||||
return decodeImageFromList(data.buffer.asUint8List());
|
||||
return await ui.instantiateImageCodec(data.buffer.asUint8List());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,8 +414,9 @@ class NetworkImage extends ImageProvider<NetworkImage> {
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(NetworkImage key) {
|
||||
return new OneFrameImageStreamCompleter(
|
||||
_loadAsync(key),
|
||||
return new MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key),
|
||||
scale: key.scale,
|
||||
informationCollector: (StringBuffer information) {
|
||||
information.writeln('Image provider: $this');
|
||||
information.write('Image key: $key');
|
||||
@@ -437,26 +426,19 @@ class NetworkImage extends ImageProvider<NetworkImage> {
|
||||
|
||||
static final http.Client _httpClient = createHttpClient();
|
||||
|
||||
Future<ImageInfo> _loadAsync(NetworkImage key) async {
|
||||
Future<ui.Codec> _loadAsync(NetworkImage key) async {
|
||||
assert(key == this);
|
||||
|
||||
final Uri resolved = Uri.base.resolve(key.url);
|
||||
final http.Response response = await _httpClient.get(resolved);
|
||||
if (response == null || response.statusCode != 200)
|
||||
return null;
|
||||
throw new Exception('HTTP request failed, statusCode: ${response?.statusCode}, $resolved');
|
||||
|
||||
final Uint8List bytes = response.bodyBytes;
|
||||
if (bytes.lengthInBytes == 0)
|
||||
return null;
|
||||
throw new Exception('NetworkImage is an empty file: $resolved');
|
||||
|
||||
final ui.Image image = await decodeImageFromList(bytes);
|
||||
if (image == null)
|
||||
return null;
|
||||
|
||||
return new ImageInfo(
|
||||
image: image,
|
||||
scale: key.scale,
|
||||
);
|
||||
return await ui.instantiateImageCodec(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -498,29 +480,23 @@ class FileImage extends ImageProvider<FileImage> {
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(FileImage key) {
|
||||
return new OneFrameImageStreamCompleter(
|
||||
_loadAsync(key),
|
||||
return new MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key),
|
||||
scale: key.scale,
|
||||
informationCollector: (StringBuffer information) {
|
||||
information.writeln('Path: ${file?.path}');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Future<ImageInfo> _loadAsync(FileImage key) async {
|
||||
Future<ui.Codec> _loadAsync(FileImage key) async {
|
||||
assert(key == this);
|
||||
|
||||
final Uint8List bytes = await file.readAsBytes();
|
||||
if (bytes.lengthInBytes == 0)
|
||||
return null;
|
||||
|
||||
final ui.Image image = await decodeImageFromList(bytes);
|
||||
if (image == null)
|
||||
return null;
|
||||
|
||||
return new ImageInfo(
|
||||
image: image,
|
||||
scale: key.scale,
|
||||
);
|
||||
return await ui.instantiateImageCodec(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -568,20 +544,16 @@ class MemoryImage extends ImageProvider<MemoryImage> {
|
||||
|
||||
@override
|
||||
ImageStreamCompleter load(MemoryImage key) {
|
||||
return new OneFrameImageStreamCompleter(_loadAsync(key));
|
||||
return new MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key),
|
||||
scale: key.scale
|
||||
);
|
||||
}
|
||||
|
||||
Future<ImageInfo> _loadAsync(MemoryImage key) async {
|
||||
Future<ui.Codec> _loadAsync(MemoryImage key) {
|
||||
assert(key == this);
|
||||
|
||||
final ui.Image image = await decodeImageFromList(bytes);
|
||||
if (image == null)
|
||||
return null;
|
||||
|
||||
return new ImageInfo(
|
||||
image: image,
|
||||
scale: key.scale,
|
||||
);
|
||||
return ui.instantiateImageCodec(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -365,6 +365,8 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
||||
|
||||
void _handleCodecReady(ui.Codec codec) {
|
||||
_codec = codec;
|
||||
assert(_codec != null);
|
||||
|
||||
_decodeNextFrameAndSchedule();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,24 +88,27 @@ class TestAssetBundle extends CachingAssetBundle {
|
||||
String toString() => '${describeIdentity(this)}()';
|
||||
}
|
||||
|
||||
class FakeImageStreamCompleter extends ImageStreamCompleter {
|
||||
FakeImageStreamCompleter(Future<ImageInfo> image) {
|
||||
image.then<Null>(setImage);
|
||||
}
|
||||
}
|
||||
|
||||
class TestAssetImage extends AssetImage {
|
||||
TestAssetImage(String name) : super(name);
|
||||
|
||||
@override
|
||||
Future<ImageInfo> loadAsync(AssetBundleImageKey key) {
|
||||
ImageInfo result;
|
||||
ImageStreamCompleter load(AssetBundleImageKey key) {
|
||||
ImageInfo imageInfo;
|
||||
key.bundle.load(key.name).then<Null>((ByteData data) {
|
||||
decodeImage(data).then<Null>((ui.Image image) {
|
||||
result = new ImageInfo(image: image, scale: key.scale);
|
||||
});
|
||||
final TestByteData testData = data;
|
||||
final ui.Image image = new TestImage(testData.scale);
|
||||
imageInfo = new ImageInfo(image: image, scale: key.scale);
|
||||
});
|
||||
assert(result != null);
|
||||
return new SynchronousFuture<ImageInfo>(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ui.Image> decodeImage(covariant TestByteData data) {
|
||||
return new SynchronousFuture<ui.Image>(new TestImage(data.scale));
|
||||
assert(imageInfo != null);
|
||||
return new FakeImageStreamCompleter(
|
||||
new SynchronousFuture<ImageInfo>(imageInfo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user