Commit c80d2482 authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

An API that prefetches an image into the cache (#11471)

parent 3d5afb5a
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'dart:io' show File; import 'dart:io' show File;
import 'dart:typed_data'; import 'dart:typed_data';
...@@ -44,6 +45,34 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si ...@@ -44,6 +45,34 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si
); );
} }
/// Prefetches an image into the image cache.
///
/// Returns a [Future] that will complete when the first image yielded by the
/// [ImageProvider] is available.
///
/// If the image is later used by an [Image] or [BoxDecoration] or [FadeInImage],
/// it will probably be loaded faster. The consumer of the image does not need
/// to use the same [ImageProvider] instance. The [ImageCache] will find the image
/// as long as both images share the same key.
///
/// The [BuildContext] and [Size] are used to select an image configuration
/// (see [createLocalImageConfiguration]).
///
/// See also:
///
/// * [ImageCache], which holds images that may be reused.
Future<Null> precacheImage(ImageProvider provider, BuildContext context, { Size size }) {
final ImageConfiguration config = createLocalImageConfiguration(context, size: size);
final Completer<Null> completer = new Completer<Null>();
final ImageStream stream = provider.resolve(config);
void listener(ImageInfo image, bool sync) {
completer.complete();
}
stream.addListener(listener);
completer.future.then((Null _) { stream.removeListener(listener); });
return completer.future;
}
/// A widget that displays an image. /// A widget that displays an image.
/// ///
/// Several constructors are provided for the various ways that an image can be /// Several constructors are provided for the various ways that an image can be
......
...@@ -196,7 +196,7 @@ void main() { ...@@ -196,7 +196,7 @@ void main() {
) )
); );
expect(imageProvider._configuration.devicePixelRatio, 5.0); expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 5.0);
// This is the same widget hierarchy as before except that the // This is the same widget hierarchy as before except that the
// two MediaQuery objects have exchanged places. The imageProvider // two MediaQuery objects have exchanged places. The imageProvider
...@@ -222,7 +222,7 @@ void main() { ...@@ -222,7 +222,7 @@ void main() {
) )
); );
expect(imageProvider._configuration.devicePixelRatio, 10.0); expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 10.0);
}); });
testWidgets('Verify ImageProvider configuration inheritance again', (WidgetTester tester) async { testWidgets('Verify ImageProvider configuration inheritance again', (WidgetTester tester) async {
...@@ -259,7 +259,7 @@ void main() { ...@@ -259,7 +259,7 @@ void main() {
) )
); );
expect(imageProvider._configuration.devicePixelRatio, 5.0); expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 5.0);
await tester.pumpWidget( await tester.pumpWidget(
new Row( new Row(
...@@ -287,7 +287,7 @@ void main() { ...@@ -287,7 +287,7 @@ void main() {
) )
); );
expect(imageProvider._configuration.devicePixelRatio, 10.0); expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 10.0);
}); });
testWidgets('Verify Image stops listening to ImageStream', (WidgetTester tester) async { testWidgets('Verify Image stops listening to ImageStream', (WidgetTester tester) async {
...@@ -318,11 +318,33 @@ void main() { ...@@ -318,11 +318,33 @@ void main() {
expect(renderer.color, const Color(0xFF00FF00)); expect(renderer.color, const Color(0xFF00FF00));
expect(renderer.colorBlendMode, BlendMode.clear); expect(renderer.colorBlendMode, BlendMode.clear);
}); });
testWidgets('Precache', (WidgetTester tester) async {
final TestImageProvider provider = new TestImageProvider();
Future<Null> precache;
await tester.pumpWidget(
new Builder(
builder: (BuildContext context) {
precache = precacheImage(provider, context);
return new Container();
}
)
);
provider.complete();
await precache;
expect(provider._lastResolvedConfiguration, isNotNull);
// Check that a second resolve of the same image is synchronous.
final ImageStream stream = provider.resolve(provider._lastResolvedConfiguration);
bool isSync;
stream.addListener((ImageInfo image, bool sync) { isSync = sync; });
expect(isSync, isTrue);
});
} }
class TestImageProvider extends ImageProvider<TestImageProvider> { class TestImageProvider extends ImageProvider<TestImageProvider> {
final Completer<ImageInfo> _completer = new Completer<ImageInfo>(); final Completer<ImageInfo> _completer = new Completer<ImageInfo>();
ImageConfiguration _configuration; ImageConfiguration _lastResolvedConfiguration;
@override @override
Future<TestImageProvider> obtainKey(ImageConfiguration configuration) { Future<TestImageProvider> obtainKey(ImageConfiguration configuration) {
...@@ -331,7 +353,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> { ...@@ -331,7 +353,7 @@ class TestImageProvider extends ImageProvider<TestImageProvider> {
@override @override
ImageStream resolve(ImageConfiguration configuration) { ImageStream resolve(ImageConfiguration configuration) {
_configuration = configuration; _lastResolvedConfiguration = configuration;
return super.resolve(configuration); return super.resolve(configuration);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment