diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 483081e14bc52..bc58e6f8df49e 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -2049,8 +2049,10 @@ class NetworkImage extends StatelessWidget { @override Widget build(BuildContext context) { + ImageResource imageResource = imageCache.load(src, scale: scale); return new RawImageResource( - image: imageCache.load(src, scale: scale), + key: key == null ? new ObjectKey(imageResource) : null, + image: imageResource, width: width, height: height, color: color, @@ -2171,8 +2173,10 @@ class AsyncImage extends StatelessWidget { @override Widget build(BuildContext context) { + ImageResource imageResource = imageCache.loadProvider(provider); return new RawImageResource( - image: imageCache.loadProvider(provider), + key: key == null ? new ObjectKey(imageResource) : null, + image: imageResource, width: width, height: height, color: color, @@ -2272,8 +2276,10 @@ class AssetImage extends StatelessWidget { @override Widget build(BuildContext context) { + ImageResource imageResource = (bundle ?? DefaultAssetBundle.of(context)).loadImage(name); return new RawImageResource( - image: (bundle ?? DefaultAssetBundle.of(context)).loadImage(name), + key: key == null ? new ObjectKey(imageResource) : null, + image: imageResource, width: width, height: height, color: color, diff --git a/packages/flutter/test/widget/image_test.dart b/packages/flutter/test/widget/image_test.dart new file mode 100644 index 0000000000000..a3f0d80a131f7 --- /dev/null +++ b/packages/flutter/test/widget/image_test.dart @@ -0,0 +1,220 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:ui' as ui show Image; + +import 'package:mojo/core.dart' as core; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:test/test.dart'; + +void main() { + test('Verify NetworkImage sets an ObjectKey on its ImageResource if it doesn\'t have a key', () { + testWidgets((WidgetTester tester) { + final String testUrl = 'https://foo.bar/baz1.png'; + tester.pumpWidget( + new NetworkImage( + scale: 1.0, + src: testUrl + ) + ); + + ImageResource imageResource = imageCache.load(testUrl, scale: 1.0); + expect(tester.findElementByKey(new ObjectKey(imageResource)), isNotNull); + }); + }); + + test('Verify NetworkImage doesn\'t set an ObjectKey on its ImageResource if it has a key', () { + testWidgets((WidgetTester tester) { + final String testUrl = 'https://foo.bar/baz2.png'; + tester.pumpWidget( + new NetworkImage( + key: new GlobalKey(), + scale: 1.0, + src: testUrl + ) + ); + + ImageResource imageResource = imageCache.load(testUrl, scale: 1.0); + expect(tester.findElementByKey(new ObjectKey(imageResource)), isNull); + }); + }); + + test('Verify AsyncImage sets an ObjectKey on its ImageResource if it doesn\'t have a key', () { + testWidgets((WidgetTester tester) { + ImageProvider imageProvider = new TestImageProvider(); + tester.pumpWidget(new AsyncImage(provider: imageProvider)); + + ImageResource imageResource = imageCache.loadProvider(imageProvider); + expect(tester.findElementByKey(new ObjectKey(imageResource)), isNotNull); + }); + }); + + test('Verify AsyncImage doesn\'t set an ObjectKey on its ImageResource if it has a key', () { + testWidgets((WidgetTester tester) { + ImageProvider imageProvider = new TestImageProvider(); + tester.pumpWidget( + new AsyncImage( + key: new GlobalKey(), + provider: imageProvider + ) + ); + + ImageResource imageResource = imageCache.loadProvider(imageProvider); + expect(tester.findElementByKey(new ObjectKey(imageResource)), isNull); + }); + }); + + test('Verify AssetImage sets an ObjectKey on its ImageResource if it doesn\'t have a key', () { + testWidgets((WidgetTester tester) { + final String name = 'foo'; + final AssetBundle assetBundle = new TestAssetBundle(); + tester.pumpWidget( + new AssetImage( + name: name, + bundle: assetBundle + ) + ); + + ImageResource imageResource = assetBundle.loadImage(name); + expect(tester.findElementByKey(new ObjectKey(imageResource)), isNotNull); + }); + }); + + test('Verify AssetImage doesn\'t set an ObjectKey on its ImageResource if it has a key', () { + testWidgets((WidgetTester tester) { + final String name = 'foo'; + final AssetBundle assetBundle = new TestAssetBundle(); + tester.pumpWidget( + new AssetImage( + key: new GlobalKey(), + name: name, + bundle: assetBundle + ) + ); + + ImageResource imageResource = assetBundle.loadImage(name); + expect(tester.findElementByKey(new ObjectKey(imageResource)), isNull); + }); + }); + + test('Verify AsyncImage resets its RenderImage when changing providers if it doesn\'t have a key', () { + testWidgets((WidgetTester tester) { + final GlobalKey key = new GlobalKey(); + TestImageProvider imageProvider1 = new TestImageProvider(); + tester.pumpWidget( + new Container( + key: key, + child: new AsyncImage( + provider: imageProvider1 + ) + ) + ); + RenderImage renderImage = key.currentContext.findRenderObject(); + expect(renderImage.image, isNull); + + // An exception will be thrown when we try to draw the image. Catch it. + RenderingExceptionHandler originalRenderingExceptionHandler = debugRenderingExceptionHandler; + debugRenderingExceptionHandler = (_, __, ___, ____) => null; + imageProvider1.complete(); + tester.pump(); + tester.pump(); + debugRenderingExceptionHandler = originalRenderingExceptionHandler; + + renderImage = key.currentContext.findRenderObject(); + expect(renderImage.image, isNotNull); + + TestImageProvider imageProvider2 = new TestImageProvider(); + tester.pumpWidget( + new Container( + key: key, + child: new AsyncImage( + provider: imageProvider2 + ) + ) + ); + + renderImage = key.currentContext.findRenderObject(); + expect(renderImage.image, isNull); + + }); + }); + + test('Verify AsyncImage doesn\'t reset its RenderImage when changing providers if it has a key', () { + testWidgets((WidgetTester tester) { + final GlobalKey key = new GlobalKey(); + TestImageProvider imageProvider1 = new TestImageProvider(); + tester.pumpWidget( + new AsyncImage( + key: key, + provider: imageProvider1 + ) + ); + RenderImage renderImage = key.currentContext.findRenderObject(); + expect(renderImage.image, isNull); + + // An exception will be thrown when we try to draw the image. Catch it. + RenderingExceptionHandler originalRenderingExceptionHandler = debugRenderingExceptionHandler; + debugRenderingExceptionHandler = (_, __, ___, ____) => null; + imageProvider1.complete(); + tester.pump(); + tester.pump(); + debugRenderingExceptionHandler = originalRenderingExceptionHandler; + + renderImage = key.currentContext.findRenderObject(); + expect(renderImage.image, isNotNull); + + TestImageProvider imageProvider2 = new TestImageProvider(); + tester.pumpWidget( + new AsyncImage( + key: key, + provider: imageProvider2 + ) + ); + + renderImage = key.currentContext.findRenderObject(); + expect(renderImage.image, isNotNull); + }); + }); + +} + +class TestImageProvider extends ImageProvider { + final Completer _completer = new Completer(); + + @override + Future loadImage() => _completer.future; + + void complete() { + _completer.complete(new ImageInfo(image:new TestImage())); + } +} + +class TestAssetBundle extends AssetBundle { + final ImageResource _imageResource = new ImageResource(new Completer().future); + + @override + ImageResource loadImage(String key) => _imageResource; + + @override + Future loadString(String key) => new Completer().future; + + @override + Future load(String key) => new Completer().future; +} + +class TestImage extends ui.Image { + @override + int get width => 100; + + @override + int get height => 100; + + @override + void dispose() { + } +}