Commit 680d581d authored by Hans Muller's avatar Hans Muller Committed by GitHub

Correct handling of assets when no main asset is present (#11564)

parent 5ee8390e
...@@ -150,7 +150,6 @@ class AssetImage extends AssetBundleImageProvider { ...@@ -150,7 +150,6 @@ class AssetImage extends AssetBundleImageProvider {
final SplayTreeMap<double, String> mapping = new SplayTreeMap<double, String>(); final SplayTreeMap<double, String> mapping = new SplayTreeMap<double, String>();
for (String candidate in candidates) for (String candidate in candidates)
mapping[_parseScale(candidate)] = candidate; mapping[_parseScale(candidate)] = candidate;
mapping[_naturalResolution] = main;
// TODO(ianh): implement support for config.locale, config.size, config.platform // TODO(ianh): implement support for config.locale, config.size, config.platform
// (then document this over in the Image.asset docs) // (then document this over in the Image.asset docs)
return _findNearest(mapping, config.devicePixelRatio); return _findNearest(mapping, config.devicePixelRatio);
......
...@@ -34,9 +34,10 @@ class TestByteData implements ByteData { ...@@ -34,9 +34,10 @@ class TestByteData implements ByteData {
dynamic noSuchMethod(Invocation invocation) => null; dynamic noSuchMethod(Invocation invocation) => null;
} }
String testManifest = ''' const String testManifest = '''
{ {
"assets/image.png" : [ "assets/image.png" : [
"assets/image.png",
"assets/1.5x/image.png", "assets/1.5x/image.png",
"assets/2.0x/image.png", "assets/2.0x/image.png",
"assets/3.0x/image.png", "assets/3.0x/image.png",
...@@ -46,6 +47,10 @@ String testManifest = ''' ...@@ -46,6 +47,10 @@ String testManifest = '''
'''; ''';
class TestAssetBundle extends CachingAssetBundle { class TestAssetBundle extends CachingAssetBundle {
TestAssetBundle({ this.manifest: testManifest });
final String manifest;
@override @override
Future<ByteData> load(String key) { Future<ByteData> load(String key) {
ByteData data; ByteData data;
...@@ -53,6 +58,9 @@ class TestAssetBundle extends CachingAssetBundle { ...@@ -53,6 +58,9 @@ class TestAssetBundle extends CachingAssetBundle {
case 'assets/image.png': case 'assets/image.png':
data = new TestByteData(1.0); data = new TestByteData(1.0);
break; break;
case 'assets/1.0x/image.png':
data = new TestByteData(10.0); // see "...with a main asset and a 1.0x asset"
break;
case 'assets/1.5x/image.png': case 'assets/1.5x/image.png':
data = new TestByteData(1.5); data = new TestByteData(1.5);
break; break;
...@@ -72,7 +80,7 @@ class TestAssetBundle extends CachingAssetBundle { ...@@ -72,7 +80,7 @@ class TestAssetBundle extends CachingAssetBundle {
@override @override
Future<String> loadString(String key, { bool cache: true }) { Future<String> loadString(String key, { bool cache: true }) {
if (key == 'AssetManifest.json') if (key == 'AssetManifest.json')
return new SynchronousFuture<String>(testManifest); return new SynchronousFuture<String>(manifest);
return null; return null;
} }
...@@ -101,7 +109,7 @@ class TestAssetImage extends AssetImage { ...@@ -101,7 +109,7 @@ class TestAssetImage extends AssetImage {
} }
} }
Widget buildImageAtRatio(String image, Key key, double ratio, bool inferSize) { Widget buildImageAtRatio(String image, Key key, double ratio, bool inferSize, [AssetBundle bundle]) {
const double windowSize = 500.0; // 500 logical pixels const double windowSize = 500.0; // 500 logical pixels
const double imageSize = 200.0; // 200 logical pixels const double imageSize = 200.0; // 200 logical pixels
...@@ -112,7 +120,7 @@ Widget buildImageAtRatio(String image, Key key, double ratio, bool inferSize) { ...@@ -112,7 +120,7 @@ Widget buildImageAtRatio(String image, Key key, double ratio, bool inferSize) {
padding: const EdgeInsets.all(0.0) padding: const EdgeInsets.all(0.0)
), ),
child: new DefaultAssetBundle( child: new DefaultAssetBundle(
bundle: new TestAssetBundle(), bundle: bundle ?? new TestAssetBundle(),
child: new Center( child: new Center(
child: inferSize ? child: inferSize ?
new Image( new Image(
...@@ -231,4 +239,57 @@ void main() { ...@@ -231,4 +239,57 @@ void main() {
expect(getTestImage(tester, key).scale, 4.0); expect(getTestImage(tester, key).scale, 4.0);
}); });
testWidgets('Image for device pixel ratio 1.0, with no main asset', (WidgetTester tester) async {
const String manifest = '''
{
"assets/image.png" : [
"assets/1.5x/image.png",
"assets/2.0x/image.png",
"assets/3.0x/image.png",
"assets/4.0x/image.png"
]
}
''';
final AssetBundle bundle = new TestAssetBundle(manifest: manifest);
const double ratio = 1.0;
Key key = new GlobalKey();
await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, bundle));
expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
expect(getTestImage(tester, key).scale, 1.5);
key = new GlobalKey();
await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true, bundle));
expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
expect(getTestImage(tester, key).scale, 1.5);
});
testWidgets('Image for device pixel ratio 1.0, with a main asset and a 1.0x asset', (WidgetTester tester) async {
// If both a main asset and a 1.0x asset are specified, then prefer
// the 1.0x asset.
const String manifest = '''
{
"assets/image.png" : [
"assets/image.png",
"assets/1.0x/image.png",
"assets/1.5x/image.png",
"assets/2.0x/image.png",
"assets/3.0x/image.png",
"assets/4.0x/image.png"
]
}
''';
final AssetBundle bundle = new TestAssetBundle(manifest: manifest);
const double ratio = 1.0;
Key key = new GlobalKey();
await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, bundle));
expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
expect(getTestImage(tester, key).scale, 10.0);
key = new GlobalKey();
await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true, bundle));
expect(getRenderImage(tester, key).size, const Size(480.0, 480.0));
expect(getTestImage(tester, key).scale, 10.0);
});
} }
...@@ -98,6 +98,10 @@ class AssetBundle { ...@@ -98,6 +98,10 @@ class AssetBundle {
final PackageMap packageMap = new PackageMap(packagesPath); final PackageMap packageMap = new PackageMap(packagesPath);
// The _assetVariants map contains an entry for each asset listed
// in the pubspec.yaml file's assets and font and sections. The
// value of each image asset is a list of resolution-specific "variants",
// see _AssetDirectoryCache.
final Map<_Asset, List<_Asset>> assetVariants = _parseAssets( final Map<_Asset, List<_Asset>> assetVariants = _parseAssets(
packageMap, packageMap,
manifestDescriptor, manifestDescriptor,
...@@ -112,14 +116,24 @@ class AssetBundle { ...@@ -112,14 +116,24 @@ class AssetBundle {
manifestDescriptor.containsKey('uses-material-design') && manifestDescriptor.containsKey('uses-material-design') &&
manifestDescriptor['uses-material-design']; manifestDescriptor['uses-material-design'];
// Save the contents of each image, image variant, and font
// asset in entries.
for (_Asset asset in assetVariants.keys) { for (_Asset asset in assetVariants.keys) {
if (!asset.assetFileExists && assetVariants[asset].isEmpty) { if (!asset.assetFileExists && assetVariants[asset].isEmpty) {
printStatus('Error detected in pubspec.yaml:', emphasis: true); printStatus('Error detected in pubspec.yaml:', emphasis: true);
printError('No file or variants found for $asset.\n'); printError('No file or variants found for $asset.\n');
return 1; return 1;
} }
if (asset.assetFileExists) // The file name for an asset's "main" entry is whatever appears in
entries[asset.assetEntry] = new DevFSFileContent(asset.assetFile); // the pubspec.yaml file. The main entry's file must always exist for
// font assets. It need not exist for an image if resolution-specific
// variant files exist. An image's main entry is treated the same as a
// "1x" resolution variant and if both exist then the explicit 1x
// variant is preferred.
if (asset.assetFileExists) {
assert(!assetVariants[asset].contains(asset));
assetVariants[asset].insert(0, asset);
}
for (_Asset variant in assetVariants[asset]) { for (_Asset variant in assetVariants[asset]) {
assert(variant.assetFileExists); assert(variant.assetFileExists);
entries[variant.assetEntry] = new DevFSFileContent(variant.assetFile); entries[variant.assetEntry] = new DevFSFileContent(variant.assetFile);
......
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