Unverified Commit 35b55d51 authored by xster's avatar xster Committed by GitHub

Add image stream error handling mechanism (#18424)

parent e9c8e36b
...@@ -55,7 +55,7 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si ...@@ -55,7 +55,7 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si
/// Prefetches an image into the image cache. /// Prefetches an image into the image cache.
/// ///
/// Returns a [Future] that will complete when the first image yielded by the /// Returns a [Future] that will complete when the first image yielded by the
/// [ImageProvider] is available. /// [ImageProvider] is available or failed to load.
/// ///
/// If the image is later used by an [Image] or [BoxDecoration] or [FadeInImage], /// 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 /// it will probably be loaded faster. The consumer of the image does not need
...@@ -65,17 +65,38 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si ...@@ -65,17 +65,38 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si
/// The [BuildContext] and [Size] are used to select an image configuration /// The [BuildContext] and [Size] are used to select an image configuration
/// (see [createLocalImageConfiguration]). /// (see [createLocalImageConfiguration]).
/// ///
/// The `onError` argument can be used to manually handle errors while precaching.
///
/// See also: /// See also:
/// ///
/// * [ImageCache], which holds images that may be reused. /// * [ImageCache], which holds images that may be reused.
Future<Null> precacheImage(ImageProvider provider, BuildContext context, { Size size }) { Future<Null> precacheImage(
ImageProvider provider,
BuildContext context, {
Size size,
ImageErrorListener onError,
}) {
final ImageConfiguration config = createLocalImageConfiguration(context, size: size); final ImageConfiguration config = createLocalImageConfiguration(context, size: size);
final Completer<Null> completer = new Completer<Null>(); final Completer<Null> completer = new Completer<Null>();
final ImageStream stream = provider.resolve(config); final ImageStream stream = provider.resolve(config);
void listener(ImageInfo image, bool sync) { void listener(ImageInfo image, bool sync) {
completer.complete(); completer.complete();
} }
stream.addListener(listener); void errorListener(dynamic exception, StackTrace stackTrace) {
completer.complete();
if (onError != null) {
onError(exception, stackTrace);
} else {
FlutterError.reportError(new FlutterErrorDetails(
context: 'image failed to precache',
library: 'image resource service',
exception: exception,
stack: stackTrace,
silent: true,
));
}
}
stream.addListener(listener, onError: errorListener);
completer.future.then((Null _) { stream.removeListener(listener); }); completer.future.then((Null _) { stream.removeListener(listener); });
return completer.future; return completer.future;
} }
......
...@@ -433,6 +433,40 @@ void main() { ...@@ -433,6 +433,40 @@ void main() {
expect(mockCodec.numFramesAsked, 2); expect(mockCodec.numFramesAsked, 2);
await tester.pump(const Duration(milliseconds: 200)); // emit 2nd frame. await tester.pump(const Duration(milliseconds: 200)); // emit 2nd frame.
expect(mockCodec.numFramesAsked, 3); expect(mockCodec.numFramesAsked, 3);
});
testWidgets('error handlers can intercept errors', (WidgetTester tester) async {
final MockCodec mockCodec = new MockCodec();
mockCodec.frameCount = 1;
final Completer<Codec> codecCompleter = new Completer<Codec>();
final ImageStreamCompleter streamUnderTest = new MultiFrameImageStreamCompleter(
codec: codecCompleter.future,
scale: 1.0,
);
dynamic capturedException;
final ImageErrorListener errorListener = (dynamic exception, StackTrace stackTrace) {
capturedException = exception;
};
streamUnderTest.addListener(
(ImageInfo image, bool synchronousCall) {},
onError: errorListener,
);
codecCompleter.complete(mockCodec);
// MultiFrameImageStreamCompleter only sets an error handler for the next
// frame future after the codec future has completed.
// Idling here lets the MultiFrameImageStreamCompleter advance and set the
// error handler for the nextFrame future.
await tester.idle();
mockCodec.failNextFrame('frame completion error');
await tester.idle();
// No exception is passed up.
expect(tester.takeException(), isNull);
expect(capturedException, 'frame completion error');
}); });
} }
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