mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Bundle assets used in packages (#11751)
This commit is contained in:
parent
baf3b45e0d
commit
02c10b78d2
@ -589,27 +589,94 @@ class MemoryImage extends ImageProvider<MemoryImage> {
|
||||
|
||||
/// Fetches an image from an [AssetBundle], associating it with the given scale.
|
||||
///
|
||||
/// This implementation requires an explicit final [name] and [scale] on
|
||||
/// This implementation requires an explicit final [assetName] and [scale] on
|
||||
/// construction, and ignores the device pixel ratio and size in the
|
||||
/// configuration passed into [resolve]. For a resolution-aware variant that
|
||||
/// uses the configuration to pick an appropriate image based on the device
|
||||
/// pixel ratio and size, see [AssetImage].
|
||||
///
|
||||
/// ## Fetching assets
|
||||
///
|
||||
/// When fetching an image provided by the app itself, use the [assetName]
|
||||
/// argument to name the asset to choose. For instance, consider a directory
|
||||
/// `icons` with an image `heart.png`. First, the [pubspec.yaml] of the project
|
||||
/// should specify its assets in the `flutter` section:
|
||||
///
|
||||
/// ```yaml
|
||||
/// flutter:
|
||||
/// assets:
|
||||
/// - icons/heart.png
|
||||
/// ```
|
||||
///
|
||||
/// Then, to fetch the image and associate it with scale `1.5`, use
|
||||
///
|
||||
/// ```dart
|
||||
/// new AssetImage('icons/heart.png', scale: 1.5)
|
||||
/// ```
|
||||
///
|
||||
///## Assets in packages
|
||||
///
|
||||
/// To fetch an asset from a package, the [package] argument must be provided.
|
||||
/// For instance, suppose the structure above is inside a package called
|
||||
/// `my_icons`. Then to fetch the image, use:
|
||||
///
|
||||
/// ```dart
|
||||
/// new AssetImage('icons/heart.png', scale: 1.5, package: 'my_icons')
|
||||
/// ```
|
||||
///
|
||||
/// Assets used by the package itself should also be fetched using the [package]
|
||||
/// argument as above.
|
||||
///
|
||||
/// If the desired asset is specified in the [pubspec.yaml] of the package, it
|
||||
/// is bundled automatically with the app. In particular, assets used by the
|
||||
/// package itself must be specified in its [pubspec.yaml].
|
||||
///
|
||||
/// A package can also choose to have assets in its 'lib/' folder that are not
|
||||
/// specified in its [pubspec.yaml]. In this case for those images to be
|
||||
/// bundled, the app has to specify which ones to include. For instance a
|
||||
/// package named `fancy_backgrounds` could have:
|
||||
///
|
||||
/// ```
|
||||
/// lib/backgrounds/background1.png
|
||||
/// lib/backgrounds/background2.png
|
||||
/// lib/backgrounds/background3.png
|
||||
///```
|
||||
///
|
||||
/// To include, say the first image, the [pubspec.yaml] of the app should specify
|
||||
/// it in the `assets` section:
|
||||
///
|
||||
/// ```yaml
|
||||
/// assets:
|
||||
/// - packages/fancy_backgrounds/backgrounds/background1.png
|
||||
/// ```
|
||||
///
|
||||
/// Note that the `lib/` is implied, so it should not be included in the asset
|
||||
/// path.
|
||||
///
|
||||
class ExactAssetImage extends AssetBundleImageProvider {
|
||||
/// Creates an object that fetches the given image from an asset bundle.
|
||||
///
|
||||
/// The [name] and [scale] arguments must not be null. The [scale] arguments
|
||||
/// The [assetName] and [scale] arguments must not be null. The [scale] arguments
|
||||
/// defaults to 1.0. The [bundle] argument may be null, in which case the
|
||||
/// bundle provided in the [ImageConfiguration] passed to the [resolve] call
|
||||
/// will be used instead.
|
||||
const ExactAssetImage(this.name, {
|
||||
///
|
||||
/// The [package] argument must be non-null when fetching an asset that is
|
||||
/// included in a package. See the documentation for the [ExactAssetImage] class
|
||||
/// itself for details.
|
||||
const ExactAssetImage(this.assetName, {
|
||||
this.scale: 1.0,
|
||||
this.bundle
|
||||
}) : assert(name != null),
|
||||
this.bundle,
|
||||
this.package,
|
||||
}) : assert(assetName != null),
|
||||
assert(scale != null);
|
||||
|
||||
/// The name of the asset.
|
||||
final String assetName;
|
||||
|
||||
/// The key to use to obtain the resource from the [bundle]. This is the
|
||||
/// argument passed to [AssetBundle.load].
|
||||
final String name;
|
||||
String get keyName => package == null ? assetName : 'packages/$package/$assetName';
|
||||
|
||||
/// The scale to place in the [ImageInfo] object of the image.
|
||||
final double scale;
|
||||
@ -621,14 +688,18 @@ class ExactAssetImage extends AssetBundleImageProvider {
|
||||
/// that is also null, the [rootBundle] is used.
|
||||
///
|
||||
/// The image is obtained by calling [AssetBundle.load] on the given [bundle]
|
||||
/// using the key given by [name].
|
||||
/// using the key given by [keyName].
|
||||
final AssetBundle bundle;
|
||||
|
||||
/// The name of the package from which the image is included. See the
|
||||
/// documentation for the [ExactAssetImage] class itself for details.
|
||||
final String package;
|
||||
|
||||
@override
|
||||
Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
|
||||
return new SynchronousFuture<AssetBundleImageKey>(new AssetBundleImageKey(
|
||||
bundle: bundle ?? configuration.bundle ?? rootBundle,
|
||||
name: name,
|
||||
name: keyName,
|
||||
scale: scale
|
||||
));
|
||||
}
|
||||
@ -638,14 +709,14 @@ class ExactAssetImage extends AssetBundleImageProvider {
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
final ExactAssetImage typedOther = other;
|
||||
return name == typedOther.name
|
||||
return keyName == typedOther.keyName
|
||||
&& scale == typedOther.scale
|
||||
&& bundle == typedOther.bundle;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(name, scale, bundle);
|
||||
int get hashCode => hashValues(keyName, scale, bundle);
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType(name: "$name", scale: $scale, bundle: $bundle)';
|
||||
String toString() => '$runtimeType(name: "$keyName", scale: $scale, bundle: $bundle)';
|
||||
}
|
||||
|
@ -55,16 +55,84 @@ const String _kAssetManifestFileName = 'AssetManifest.json';
|
||||
/// icons/1.5x/heart.png
|
||||
/// icons/2.0x/heart.png
|
||||
/// ```
|
||||
///
|
||||
/// ## Fetching assets
|
||||
///
|
||||
/// When fetching an image provided by the app itself, use the [assetName]
|
||||
/// argument to name the asset to choose. For instance, consider the structure
|
||||
/// above. First, the [pubspec.yaml] of the project should specify its assets in
|
||||
/// the `flutter` section:
|
||||
///
|
||||
/// ```yaml
|
||||
/// flutter:
|
||||
/// assets:
|
||||
/// - icons/heart.png
|
||||
/// ```
|
||||
///
|
||||
/// Then, to fetch the image, use
|
||||
/// ```dart
|
||||
/// new AssetImage('icons/heart.png')
|
||||
/// ```
|
||||
///
|
||||
/// ## Assets in packages
|
||||
///
|
||||
/// To fetch an asset from a package, the [package] argument must be provided.
|
||||
/// For instance, suppose the structure above is inside a package called
|
||||
/// `my_icons`. Then to fetch the image, use:
|
||||
///
|
||||
/// ```dart
|
||||
/// new AssetImage('icons/heart.png', package: 'my_icons')
|
||||
/// ```
|
||||
///
|
||||
/// Assets used by the package itself should also be fetched using the [package]
|
||||
/// argument as above.
|
||||
///
|
||||
/// If the desired asset is specified in the [pubspec.yaml] of the package, it
|
||||
/// is bundled automatically with the app. In particular, assets used by the
|
||||
/// package itself must be specified in its [pubspec.yaml].
|
||||
///
|
||||
/// A package can also choose to have assets in its 'lib/' folder that are not
|
||||
/// specified in its [pubspec.yaml]. In this case for those images to be
|
||||
/// bundled, the app has to specify which ones to include. For instance a
|
||||
/// package named `fancy_backgrounds` could have:
|
||||
///
|
||||
/// ```
|
||||
/// lib/backgrounds/background1.png
|
||||
/// lib/backgrounds/background2.png
|
||||
/// lib/backgrounds/background3.png
|
||||
///```
|
||||
///
|
||||
/// To include, say the first image, the [pubspec.yaml] of the app should specify
|
||||
/// it in the `assets` section:
|
||||
///
|
||||
/// ```yaml
|
||||
/// assets:
|
||||
/// - packages/fancy_backgrounds/backgrounds/background1.png
|
||||
/// ```
|
||||
///
|
||||
/// Note that the `lib/` is implied, so it should not be included in the asset
|
||||
/// path.
|
||||
///
|
||||
class AssetImage extends AssetBundleImageProvider {
|
||||
/// Creates an object that fetches an image from an asset bundle.
|
||||
///
|
||||
/// The [name] argument must not be null. It should name the main asset from
|
||||
/// the set of images to chose from.
|
||||
const AssetImage(this.name, { this.bundle }) : assert(name != null);
|
||||
|
||||
/// The name of the main asset from the set of images to chose from. See the
|
||||
/// The [assetName] argument must not be null. It should name the main asset
|
||||
/// from the set of images to choose from. The [package] argument must be
|
||||
/// non-null when fetching an asset that is included in package. See the
|
||||
/// documentation for the [AssetImage] class itself for details.
|
||||
final String name;
|
||||
const AssetImage(this.assetName, {
|
||||
this.bundle,
|
||||
this.package,
|
||||
}) : assert(assetName != null);
|
||||
|
||||
/// The name of the main asset from the set of images to choose from. See the
|
||||
/// documentation for the [AssetImage] class itself for details.
|
||||
final String assetName;
|
||||
|
||||
/// The name used to generate the key to obtain the asset. For local assets
|
||||
/// this is [assetName], and for assets from packages the [assetName] is
|
||||
/// prefixed 'packages/<package_name>/'.
|
||||
String get keyName => package == null ? assetName : 'packages/$package/$assetName';
|
||||
|
||||
/// The bundle from which the image will be obtained.
|
||||
///
|
||||
@ -73,9 +141,15 @@ class AssetImage extends AssetBundleImageProvider {
|
||||
/// that is also null, the [rootBundle] is used.
|
||||
///
|
||||
/// The image is obtained by calling [AssetBundle.load] on the given [bundle]
|
||||
/// using the key given by [name].
|
||||
/// using the key given by [keyName].
|
||||
final AssetBundle bundle;
|
||||
|
||||
/// The name of the package from which the image is included. See the
|
||||
/// documentation for the [AssetImage] class itself for details.
|
||||
final String package;
|
||||
|
||||
|
||||
|
||||
// We assume the main asset is designed for a device pixel ratio of 1.0
|
||||
static const double _naturalResolution = 1.0;
|
||||
|
||||
@ -93,9 +167,9 @@ class AssetImage extends AssetBundleImageProvider {
|
||||
chosenBundle.loadStructuredData<Map<String, List<String>>>(_kAssetManifestFileName, _manifestParser).then<Null>(
|
||||
(Map<String, List<String>> manifest) {
|
||||
final String chosenName = _chooseVariant(
|
||||
name,
|
||||
keyName,
|
||||
configuration,
|
||||
manifest == null ? null : manifest[name]
|
||||
manifest == null ? null : manifest[keyName]
|
||||
);
|
||||
final double chosenScale = _parseScale(chosenName);
|
||||
final AssetBundleImageKey key = new AssetBundleImageKey(
|
||||
@ -185,13 +259,13 @@ class AssetImage extends AssetBundleImageProvider {
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
final AssetImage typedOther = other;
|
||||
return name == typedOther.name
|
||||
return keyName == typedOther.keyName
|
||||
&& bundle == typedOther.bundle;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(name, bundle);
|
||||
int get hashCode => hashValues(keyName, bundle);
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType(bundle: $bundle, name: "$name")';
|
||||
String toString() => '$runtimeType(bundle: $bundle, name: "$keyName")';
|
||||
}
|
||||
|
@ -113,7 +113,8 @@ class Image extends StatefulWidget {
|
||||
this.alignment,
|
||||
this.repeat: ImageRepeat.noRepeat,
|
||||
this.centerSlice,
|
||||
this.gaplessPlayback: false
|
||||
this.gaplessPlayback: false,
|
||||
this.package,
|
||||
}) : assert(image != null),
|
||||
super(key: key);
|
||||
|
||||
@ -131,7 +132,8 @@ class Image extends StatefulWidget {
|
||||
this.alignment,
|
||||
this.repeat: ImageRepeat.noRepeat,
|
||||
this.centerSlice,
|
||||
this.gaplessPlayback: false
|
||||
this.gaplessPlayback: false,
|
||||
this.package,
|
||||
}) : image = new NetworkImage(src, scale: scale),
|
||||
super(key: key);
|
||||
|
||||
@ -152,13 +154,18 @@ class Image extends StatefulWidget {
|
||||
this.alignment,
|
||||
this.repeat: ImageRepeat.noRepeat,
|
||||
this.centerSlice,
|
||||
this.gaplessPlayback: false
|
||||
this.gaplessPlayback: false,
|
||||
this.package,
|
||||
}) : image = new FileImage(file, scale: scale),
|
||||
super(key: key);
|
||||
|
||||
/// Creates a widget that displays an [ImageStream] obtained from an asset
|
||||
/// bundle. The key for the image is given by the `name` argument.
|
||||
///
|
||||
/// The `package` argument must be non-null when displaying an image from a
|
||||
/// package and null otherwise. See the `Assets in packages` section for
|
||||
/// details.
|
||||
///
|
||||
/// If the `bundle` argument is omitted or null, then the
|
||||
/// [DefaultAssetBundle] will be used.
|
||||
///
|
||||
@ -210,6 +217,49 @@ class Image extends StatefulWidget {
|
||||
/// be present in the manifest). If it is omitted, then on a device with a 1.0
|
||||
/// device pixel ratio, the `images/2x/cat.png` image would be used instead.
|
||||
///
|
||||
///
|
||||
/// ## Assets in packages
|
||||
///
|
||||
/// To create the widget with an asset from a package, the [package] argument
|
||||
/// must be provided. For instance, suppose a package called `my_icons` has
|
||||
/// `icons/heart.png` .
|
||||
///
|
||||
/// Then to display the image, use:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Image.asset('icons/heart.png', package: 'my_icons')
|
||||
/// ```
|
||||
///
|
||||
/// Assets used by the package itself should also be displayed using the
|
||||
/// [package] argument as above.
|
||||
///
|
||||
/// If the desired asset is specified in the [pubspec.yaml] of the package, it
|
||||
/// is bundled automatically with the app. In particular, assets used by the
|
||||
/// package itself must be specified in its [pubspec.yaml].
|
||||
///
|
||||
/// A package can also choose to have assets in its 'lib/' folder that are not
|
||||
/// specified in its [pubspec.yaml]. In this case for those images to be
|
||||
/// bundled, the app has to specify which ones to include. For instance a
|
||||
/// package named `fancy_backgrounds` could have:
|
||||
///
|
||||
/// ```
|
||||
/// lib/backgrounds/background1.png
|
||||
/// lib/backgrounds/background2.png
|
||||
/// lib/backgrounds/background3.png
|
||||
///```
|
||||
///
|
||||
/// To include, say the first image, the [pubspec.yaml] of the app should
|
||||
/// specify it in the assets section:
|
||||
///
|
||||
/// ```yaml
|
||||
/// assets:
|
||||
/// - packages/fancy_backgrounds/backgrounds/background1.png
|
||||
/// ```
|
||||
///
|
||||
/// Note that the `lib/` is implied, so it should not be included in the asset
|
||||
/// path.
|
||||
///
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [AssetImage], which is used to implement the behavior when the scale is
|
||||
@ -230,10 +280,12 @@ class Image extends StatefulWidget {
|
||||
this.alignment,
|
||||
this.repeat: ImageRepeat.noRepeat,
|
||||
this.centerSlice,
|
||||
this.gaplessPlayback: false
|
||||
}) : image = scale != null ? new ExactAssetImage(name, bundle: bundle, scale: scale)
|
||||
: new AssetImage(name, bundle: bundle),
|
||||
super(key: key);
|
||||
this.gaplessPlayback: false,
|
||||
this.package,
|
||||
}) : image = scale != null
|
||||
? new ExactAssetImage(name, bundle: bundle, scale: scale, package: package)
|
||||
: new AssetImage(name, bundle: bundle, package: package),
|
||||
super(key: key);
|
||||
|
||||
/// Creates a widget that displays an [ImageStream] obtained from a [Uint8List].
|
||||
///
|
||||
@ -249,7 +301,8 @@ class Image extends StatefulWidget {
|
||||
this.alignment,
|
||||
this.repeat: ImageRepeat.noRepeat,
|
||||
this.centerSlice,
|
||||
this.gaplessPlayback: false
|
||||
this.gaplessPlayback: false,
|
||||
this.package,
|
||||
}) : image = new MemoryImage(bytes, scale: scale),
|
||||
super(key: key);
|
||||
|
||||
@ -310,6 +363,10 @@ class Image extends StatefulWidget {
|
||||
/// (false), when the image provider changes.
|
||||
final bool gaplessPlayback;
|
||||
|
||||
/// The name of the package from which the image is included. See the
|
||||
/// documentation for the [Image.asset] constructor for details.
|
||||
final String package;
|
||||
|
||||
@override
|
||||
_ImageState createState() => new _ImageState();
|
||||
|
||||
|
47
packages/flutter/test/widgets/image_package_asset_test.dart
Normal file
47
packages/flutter/test/widgets/image_package_asset_test.dart
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017 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 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('AssetImage from package', () {
|
||||
final AssetImage image = const AssetImage(
|
||||
'assets/image.png',
|
||||
package: 'test_package',
|
||||
);
|
||||
expect(image.keyName, 'packages/test_package/assets/image.png');
|
||||
});
|
||||
|
||||
test('ExactAssetImage from package', () {
|
||||
final ExactAssetImage image = const ExactAssetImage(
|
||||
'assets/image.png',
|
||||
scale: 1.5,
|
||||
package: 'test_package',
|
||||
);
|
||||
expect(image.keyName, 'packages/test_package/assets/image.png');
|
||||
});
|
||||
|
||||
test('Image.asset from package', () {
|
||||
final Image imageWidget = new Image.asset(
|
||||
'assets/image.png',
|
||||
package: 'test_package',
|
||||
);
|
||||
assert(imageWidget.image is AssetImage);
|
||||
final AssetImage assetImage = imageWidget.image;
|
||||
expect(assetImage.keyName, 'packages/test_package/assets/image.png');
|
||||
});
|
||||
|
||||
test('Image.asset from package', () {
|
||||
final Image imageWidget = new Image.asset(
|
||||
'assets/image.png',
|
||||
scale: 1.5,
|
||||
package: 'test_package',
|
||||
);
|
||||
assert(imageWidget.image is ExactAssetImage);
|
||||
final ExactAssetImage asssetImage = imageWidget.image;
|
||||
expect(asssetImage.keyName, 'packages/test_package/assets/image.png');
|
||||
});
|
||||
}
|
@ -86,11 +86,12 @@ class AssetBundle {
|
||||
return 0;
|
||||
}
|
||||
if (manifest != null) {
|
||||
final int result = await _validateFlutterManifest(manifest);
|
||||
if (result != 0)
|
||||
return result;
|
||||
final int result = await _validateFlutterManifest(manifest);
|
||||
if (result != 0)
|
||||
return result;
|
||||
}
|
||||
Map<String, dynamic> manifestDescriptor = manifest;
|
||||
final String appName = manifestDescriptor['name'];
|
||||
manifestDescriptor = manifestDescriptor['flutter'] ?? <String, dynamic>{};
|
||||
final String assetBasePath = fs.path.dirname(fs.path.absolute(manifestPath));
|
||||
|
||||
@ -116,6 +117,33 @@ class AssetBundle {
|
||||
manifestDescriptor.containsKey('uses-material-design') &&
|
||||
manifestDescriptor['uses-material-design'];
|
||||
|
||||
// Add assets from packages.
|
||||
for (String packageName in packageMap.map.keys) {
|
||||
final Uri package = packageMap.map[packageName];
|
||||
if (package != null && package.scheme == 'file') {
|
||||
final String packageManifestPath = package.resolve('../pubspec.yaml').path;
|
||||
final Object packageManifest = _loadFlutterManifest(packageManifestPath);
|
||||
if (packageManifest == null)
|
||||
continue;
|
||||
final int result = await _validateFlutterManifest(packageManifest);
|
||||
if (result == 0) {
|
||||
final Map<String, dynamic> packageManifestDescriptor = packageManifest;
|
||||
// Skip the app itself.
|
||||
if (packageManifestDescriptor['name'] == appName)
|
||||
continue;
|
||||
if (packageManifestDescriptor.containsKey('flutter')) {
|
||||
final String packageBasePath = fs.path.dirname(packageManifestPath);
|
||||
assetVariants.addAll(_parseAssets(
|
||||
packageMap,
|
||||
packageManifestDescriptor['flutter'],
|
||||
packageBasePath,
|
||||
packageKey: packageName,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the contents of each image, image variant, and font
|
||||
// asset in entries.
|
||||
for (_Asset asset in assetVariants.keys) {
|
||||
@ -203,6 +231,27 @@ class _Asset {
|
||||
|
||||
@override
|
||||
String toString() => 'asset: $assetEntry';
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(other, this))
|
||||
return true;
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
final _Asset otherAsset = other;
|
||||
return otherAsset.base == base
|
||||
&& otherAsset.assetEntry == assetEntry
|
||||
&& otherAsset.relativePath == relativePath
|
||||
&& otherAsset.source == source;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return base.hashCode
|
||||
^assetEntry.hashCode
|
||||
^relativePath.hashCode
|
||||
^ source.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _readMaterialFontsManifest() {
|
||||
@ -312,8 +361,8 @@ DevFSContent _createAssetManifest(Map<_Asset, List<_Asset>> assetVariants) {
|
||||
for (_Asset main in assetVariants.keys) {
|
||||
final List<String> variants = <String>[];
|
||||
for (_Asset variant in assetVariants[main])
|
||||
variants.add(variant.relativePath);
|
||||
json[main.relativePath] = variants;
|
||||
variants.add(variant.assetEntry);
|
||||
json[main.assetEntry] = variants;
|
||||
}
|
||||
return new DevFSStringContent(JSON.encode(json));
|
||||
}
|
||||
@ -384,7 +433,8 @@ Map<_Asset, List<_Asset>> _parseAssets(
|
||||
PackageMap packageMap,
|
||||
Map<String, dynamic> manifestDescriptor,
|
||||
String assetBase, {
|
||||
List<String> excludeDirs: const <String>[]
|
||||
List<String> excludeDirs: const <String>[],
|
||||
String packageKey
|
||||
}) {
|
||||
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
||||
|
||||
@ -394,7 +444,9 @@ Map<_Asset, List<_Asset>> _parseAssets(
|
||||
if (manifestDescriptor.containsKey('assets')) {
|
||||
final _AssetDirectoryCache cache = new _AssetDirectoryCache(excludeDirs);
|
||||
for (String assetName in manifestDescriptor['assets']) {
|
||||
final _Asset asset = _resolveAsset(packageMap, assetBase, assetName);
|
||||
final _Asset asset = packageKey != null
|
||||
? _resolvePackageAsset(assetBase, packageKey, assetName)
|
||||
: _resolveAsset(packageMap, assetBase, assetName);
|
||||
final List<_Asset> variants = <_Asset>[];
|
||||
|
||||
for (String path in cache.variantsFor(asset.assetFile.path)) {
|
||||
@ -435,10 +487,22 @@ Map<_Asset, List<_Asset>> _parseAssets(
|
||||
return result;
|
||||
}
|
||||
|
||||
_Asset _resolvePackageAsset(
|
||||
String assetBase,
|
||||
String packageName,
|
||||
String asset,
|
||||
) {
|
||||
return new _Asset(
|
||||
base: assetBase,
|
||||
assetEntry: 'packages/$packageName/$asset',
|
||||
relativePath: asset,
|
||||
);
|
||||
}
|
||||
|
||||
_Asset _resolveAsset(
|
||||
PackageMap packageMap,
|
||||
String assetBase,
|
||||
String asset
|
||||
String asset,
|
||||
) {
|
||||
if (asset.startsWith('packages/') && !fs.isFileSync(fs.path.join(assetBase, asset))) {
|
||||
// Convert packages/flutter_gallery_assets/clouds-0.png to clouds-0.png.
|
||||
|
225
packages/flutter_tools/test/asset_bundle_package_test.dart
Normal file
225
packages/flutter_tools/test/asset_bundle_package_test.dart
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright 2017 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:convert';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
|
||||
import 'package:flutter_tools/src/asset.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'src/common.dart';
|
||||
import 'src/context.dart';
|
||||
|
||||
void main() {
|
||||
void writePubspecFile(String path, String name, {List<String> assets}) {
|
||||
String assetsSection;
|
||||
if (assets == null) {
|
||||
assetsSection = '';
|
||||
} else {
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
buffer.write('''
|
||||
flutter:
|
||||
assets:
|
||||
''');
|
||||
|
||||
for (String asset in assets) {
|
||||
buffer.write('''
|
||||
- $asset
|
||||
''');
|
||||
}
|
||||
assetsSection = buffer.toString();
|
||||
}
|
||||
|
||||
fs.file(path)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
name: $name
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
$assetsSection
|
||||
''');
|
||||
}
|
||||
|
||||
void establishFlutterRoot() {
|
||||
// Setting flutterRoot here so that it picks up the MemoryFileSystem's
|
||||
// path separator.
|
||||
Cache.flutterRoot = getFlutterRoot();
|
||||
}
|
||||
|
||||
void writePackagesFile(String packages) {
|
||||
fs.file(".packages")
|
||||
..createSync()
|
||||
..writeAsStringSync(packages);
|
||||
}
|
||||
|
||||
Future<Null> buildAndVerifyAssets(
|
||||
List<String> assets,
|
||||
List<String> packages,
|
||||
String expectedAssetManifest,
|
||||
) async {
|
||||
final AssetBundle bundle = new AssetBundle();
|
||||
await bundle.build(manifestPath: 'pubspec.yaml');
|
||||
|
||||
for (String packageName in packages) {
|
||||
for (String asset in assets) {
|
||||
final String entryKey = 'packages/$packageName/$asset';
|
||||
expect(bundle.entries.containsKey(entryKey), true);
|
||||
expect(
|
||||
UTF8.decode(await bundle.entries[entryKey].contentsAsBytes()),
|
||||
asset,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
expect(
|
||||
UTF8.decode(await bundle.entries['AssetManifest.json'].contentsAsBytes()),
|
||||
expectedAssetManifest,
|
||||
);
|
||||
}
|
||||
|
||||
void writeAssets(String path, List<String> assets) {
|
||||
for (String asset in assets) {
|
||||
fs.file('$path$asset')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(asset);
|
||||
}
|
||||
}
|
||||
|
||||
group('AssetBundle assets from package', () {
|
||||
testUsingContext('One package with no assets', () async {
|
||||
establishFlutterRoot();
|
||||
|
||||
writePubspecFile('pubspec.yaml', 'test');
|
||||
writePackagesFile('test_package:p/p/lib/');
|
||||
writePubspecFile('p/p/pubspec.yaml', 'test_package');
|
||||
|
||||
final AssetBundle bundle = new AssetBundle();
|
||||
await bundle.build(manifestPath: 'pubspec.yaml');
|
||||
expect(bundle.entries.length, 2); // LICENSE, AssetManifest
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => new MemoryFileSystem(),
|
||||
});
|
||||
|
||||
testUsingContext('One package with one asset', () async {
|
||||
establishFlutterRoot();
|
||||
|
||||
writePubspecFile('pubspec.yaml', 'test');
|
||||
writePackagesFile('test_package:p/p/lib/');
|
||||
|
||||
final List<String> assets = <String>['a/foo'];
|
||||
writePubspecFile(
|
||||
'p/p/pubspec.yaml',
|
||||
'test_package',
|
||||
assets: assets,
|
||||
);
|
||||
|
||||
writeAssets('p/p/', assets);
|
||||
|
||||
final String expectedAssetManifest = '{"packages/test_package/a/foo":'
|
||||
'["packages/test_package/a/foo"]}';
|
||||
await buildAndVerifyAssets(
|
||||
assets,
|
||||
<String>['test_package'],
|
||||
expectedAssetManifest,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => new MemoryFileSystem(),
|
||||
});
|
||||
|
||||
testUsingContext('One package with asset variants', () async {
|
||||
establishFlutterRoot();
|
||||
|
||||
writePubspecFile('pubspec.yaml', 'test');
|
||||
writePackagesFile('test_package:p/p/lib/');
|
||||
writePubspecFile(
|
||||
'p/p/pubspec.yaml',
|
||||
'test_package',
|
||||
assets: <String>['a/foo'],
|
||||
);
|
||||
|
||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
||||
writeAssets('p/p/', assets);
|
||||
|
||||
final String expectedManifest = '{"packages/test_package/a/foo":'
|
||||
'["packages/test_package/a/foo","packages/test_package/a/v/foo"]}';
|
||||
|
||||
await buildAndVerifyAssets(
|
||||
assets,
|
||||
<String>['test_package'],
|
||||
expectedManifest,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => new MemoryFileSystem(),
|
||||
});
|
||||
|
||||
testUsingContext('One package with two assets', () async {
|
||||
establishFlutterRoot();
|
||||
|
||||
writePubspecFile('pubspec.yaml', 'test');
|
||||
writePackagesFile('test_package:p/p/lib/');
|
||||
|
||||
final List<String> assets = <String>['a/foo', 'a/bar'];
|
||||
writePubspecFile(
|
||||
'p/p/pubspec.yaml',
|
||||
'test_package',
|
||||
assets: assets,
|
||||
);
|
||||
|
||||
writeAssets('p/p/', assets);
|
||||
final String expectedAssetManifest =
|
||||
'{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
|
||||
'"packages/test_package/a/bar":["packages/test_package/a/bar"]}';
|
||||
|
||||
await buildAndVerifyAssets(
|
||||
assets,
|
||||
<String>['test_package'],
|
||||
expectedAssetManifest,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => new MemoryFileSystem(),
|
||||
});
|
||||
|
||||
testUsingContext('Two packages with assets', () async {
|
||||
establishFlutterRoot();
|
||||
|
||||
writePubspecFile('pubspec.yaml', 'test');
|
||||
writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
|
||||
writePubspecFile(
|
||||
'p/p/pubspec.yaml',
|
||||
'test_package',
|
||||
assets: <String>['a/foo'],
|
||||
);
|
||||
writePubspecFile(
|
||||
'p2/p/pubspec.yaml',
|
||||
'test_package2',
|
||||
assets: <String>['a/foo'],
|
||||
);
|
||||
|
||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
||||
writeAssets('p/p/', assets);
|
||||
writeAssets('p2/p/', assets);
|
||||
|
||||
final String expectedAssetManifest =
|
||||
'{"packages/test_package/a/foo":'
|
||||
'["packages/test_package/a/foo","packages/test_package/a/v/foo"],'
|
||||
'"packages/test_package2/a/foo":'
|
||||
'["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';
|
||||
|
||||
await buildAndVerifyAssets(
|
||||
assets,
|
||||
<String>['test_package', 'test_package2'],
|
||||
expectedAssetManifest,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => new MemoryFileSystem(),
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user