mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Tighten asset variant detection criteria to only include device-pixel-ratio variants (#110721)
This commit is contained in:
parent
2853a60133
commit
cb5b5c3459
@ -169,7 +169,6 @@ flutter:
|
|||||||
- packages/flutter_gallery_assets/products/table.png
|
- packages/flutter_gallery_assets/products/table.png
|
||||||
- packages/flutter_gallery_assets/products/teaset.png
|
- packages/flutter_gallery_assets/products/teaset.png
|
||||||
- packages/flutter_gallery_assets/products/top.png
|
- packages/flutter_gallery_assets/products/top.png
|
||||||
- packages/flutter_gallery_assets/people/ali.png
|
|
||||||
- packages/flutter_gallery_assets/people/square/ali.png
|
- packages/flutter_gallery_assets/people/square/ali.png
|
||||||
- packages/flutter_gallery_assets/people/square/peter.png
|
- packages/flutter_gallery_assets/people/square/peter.png
|
||||||
- packages/flutter_gallery_assets/people/square/sandra.png
|
- packages/flutter_gallery_assets/people/square/sandra.png
|
||||||
|
@ -23,6 +23,9 @@ const String defaultManifestPath = 'pubspec.yaml';
|
|||||||
|
|
||||||
const String kFontManifestJson = 'FontManifest.json';
|
const String kFontManifestJson = 'FontManifest.json';
|
||||||
|
|
||||||
|
// Should match '2x', '/1x', '1.5x', etc.
|
||||||
|
final RegExp _assetVariantDirectoryRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
|
||||||
|
|
||||||
/// The effect of adding `uses-material-design: true` to the pubspec is to insert
|
/// The effect of adding `uses-material-design: true` to the pubspec is to insert
|
||||||
/// the following snippet into the asset manifest:
|
/// the following snippet into the asset manifest:
|
||||||
///
|
///
|
||||||
@ -92,7 +95,6 @@ abstract class AssetBundle {
|
|||||||
/// Returns 0 for success; non-zero for failure.
|
/// Returns 0 for success; non-zero for failure.
|
||||||
Future<int> build({
|
Future<int> build({
|
||||||
String manifestPath = defaultManifestPath,
|
String manifestPath = defaultManifestPath,
|
||||||
String? assetDirPath,
|
|
||||||
required String packagesPath,
|
required String packagesPath,
|
||||||
bool deferredComponentsEnabled = false,
|
bool deferredComponentsEnabled = false,
|
||||||
TargetPlatform? targetPlatform,
|
TargetPlatform? targetPlatform,
|
||||||
@ -205,23 +207,22 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
@override
|
@override
|
||||||
Future<int> build({
|
Future<int> build({
|
||||||
String manifestPath = defaultManifestPath,
|
String manifestPath = defaultManifestPath,
|
||||||
String? assetDirPath,
|
FlutterProject? flutterProject,
|
||||||
required String packagesPath,
|
required String packagesPath,
|
||||||
bool deferredComponentsEnabled = false,
|
bool deferredComponentsEnabled = false,
|
||||||
TargetPlatform? targetPlatform,
|
TargetPlatform? targetPlatform,
|
||||||
}) async {
|
}) async {
|
||||||
assetDirPath ??= getAssetBuildDirectory();
|
|
||||||
FlutterProject flutterProject;
|
|
||||||
try {
|
|
||||||
flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
|
|
||||||
} on Exception catch (e) {
|
|
||||||
_logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
|
|
||||||
_logger.printError('$e');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (flutterProject == null) {
|
if (flutterProject == null) {
|
||||||
return 1;
|
try {
|
||||||
|
flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
|
||||||
|
} on Exception catch (e) {
|
||||||
|
_logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
|
||||||
|
_logger.printError('$e');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final FlutterManifest flutterManifest = flutterProject.manifest;
|
final FlutterManifest flutterManifest = flutterProject.manifest;
|
||||||
// If the last build time isn't set before this early return, empty pubspecs will
|
// If the last build time isn't set before this early return, empty pubspecs will
|
||||||
// hang on hot reload, as the incremental dill files will never be copied to the
|
// hang on hot reload, as the incremental dill files will never be copied to the
|
||||||
@ -243,27 +244,14 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
final List<Uri> wildcardDirectories = <Uri>[];
|
final List<Uri> wildcardDirectories = <Uri>[];
|
||||||
|
|
||||||
// The _assetVariants map contains an entry for each asset listed
|
// The _assetVariants map contains an entry for each asset listed
|
||||||
// in the pubspec.yaml file's assets and font and sections. The
|
// in the pubspec.yaml file's assets and font sections. The
|
||||||
// value of each image asset is a list of resolution-specific "variants",
|
// value of each image asset is a list of resolution-specific "variants",
|
||||||
// see _AssetDirectoryCache.
|
// see _AssetDirectoryCache.
|
||||||
final List<String> excludeDirs = <String>[
|
|
||||||
assetDirPath,
|
|
||||||
getBuildDirectory(),
|
|
||||||
if (flutterProject.ios.existsSync())
|
|
||||||
flutterProject.ios.hostAppRoot.path,
|
|
||||||
if (flutterProject.macos.existsSync())
|
|
||||||
flutterProject.macos.managedDirectory.path,
|
|
||||||
if (flutterProject.windows.existsSync())
|
|
||||||
flutterProject.windows.managedDirectory.path,
|
|
||||||
if (flutterProject.linux.existsSync())
|
|
||||||
flutterProject.linux.managedDirectory.path,
|
|
||||||
];
|
|
||||||
final Map<_Asset, List<_Asset>>? assetVariants = _parseAssets(
|
final Map<_Asset, List<_Asset>>? assetVariants = _parseAssets(
|
||||||
packageConfig,
|
packageConfig,
|
||||||
flutterManifest,
|
flutterManifest,
|
||||||
wildcardDirectories,
|
wildcardDirectories,
|
||||||
assetBasePath,
|
assetBasePath,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (assetVariants == null) {
|
if (assetVariants == null) {
|
||||||
@ -277,7 +265,6 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
assetBasePath,
|
assetBasePath,
|
||||||
wildcardDirectories,
|
wildcardDirectories,
|
||||||
flutterProject.directory,
|
flutterProject.directory,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
);
|
);
|
||||||
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
|
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
|
||||||
// Include the assets in the regular set of assets if not using deferred
|
// Include the assets in the regular set of assets if not using deferred
|
||||||
@ -373,8 +360,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
// variant files exist. An image's main entry is treated the same as a
|
// 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
|
// "1x" resolution variant and if both exist then the explicit 1x
|
||||||
// variant is preferred.
|
// variant is preferred.
|
||||||
if (assetFile.existsSync()) {
|
if (assetFile.existsSync() && !variants.contains(asset)) {
|
||||||
assert(!variants.contains(asset));
|
|
||||||
variants.insert(0, asset);
|
variants.insert(0, asset);
|
||||||
}
|
}
|
||||||
for (final _Asset variant in variants) {
|
for (final _Asset variant in variants) {
|
||||||
@ -407,8 +393,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
// variant files exist. An image's main entry is treated the same as a
|
// 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
|
// "1x" resolution variant and if both exist then the explicit 1x
|
||||||
// variant is preferred.
|
// variant is preferred.
|
||||||
if (assetFile.existsSync()) {
|
if (assetFile.existsSync() && !assetsMap[asset]!.contains(asset)) {
|
||||||
assert(!assetsMap[asset]!.contains(asset));
|
|
||||||
assetsMap[asset]!.insert(0, asset);
|
assetsMap[asset]!.insert(0, asset);
|
||||||
}
|
}
|
||||||
for (final _Asset variant in assetsMap[asset]!) {
|
for (final _Asset variant in assetsMap[asset]!) {
|
||||||
@ -606,7 +591,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
}
|
}
|
||||||
for (final DeferredComponent component in components) {
|
for (final DeferredComponent component in components) {
|
||||||
deferredComponentsAssetVariants[component.name] = <_Asset, List<_Asset>>{};
|
deferredComponentsAssetVariants[component.name] = <_Asset, List<_Asset>>{};
|
||||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(<String>[], _fileSystem);
|
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
||||||
for (final Uri assetUri in component.assets) {
|
for (final Uri assetUri in component.assets) {
|
||||||
if (assetUri.path.endsWith('/')) {
|
if (assetUri.path.endsWith('/')) {
|
||||||
wildcardDirectories.add(assetUri);
|
wildcardDirectories.add(assetUri);
|
||||||
@ -617,7 +602,6 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
cache,
|
cache,
|
||||||
deferredComponentsAssetVariants[component.name]!,
|
deferredComponentsAssetVariants[component.name]!,
|
||||||
assetUri,
|
assetUri,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_parseAssetFromFile(
|
_parseAssetFromFile(
|
||||||
@ -728,13 +712,12 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
FlutterManifest flutterManifest,
|
FlutterManifest flutterManifest,
|
||||||
List<Uri> wildcardDirectories,
|
List<Uri> wildcardDirectories,
|
||||||
String assetBase, {
|
String assetBase, {
|
||||||
List<String> excludeDirs = const <String>[],
|
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
}) {
|
}) {
|
||||||
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
||||||
|
|
||||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(excludeDirs, _fileSystem);
|
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
||||||
for (final Uri assetUri in flutterManifest.assets) {
|
for (final Uri assetUri in flutterManifest.assets) {
|
||||||
if (assetUri.path.endsWith('/')) {
|
if (assetUri.path.endsWith('/')) {
|
||||||
wildcardDirectories.add(assetUri);
|
wildcardDirectories.add(assetUri);
|
||||||
@ -745,7 +728,6 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
cache,
|
cache,
|
||||||
result,
|
result,
|
||||||
assetUri,
|
assetUri,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
);
|
);
|
||||||
@ -757,7 +739,6 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
cache,
|
cache,
|
||||||
result,
|
result,
|
||||||
assetUri,
|
assetUri,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
);
|
);
|
||||||
@ -772,7 +753,6 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
cache,
|
cache,
|
||||||
result,
|
result,
|
||||||
shaderUri,
|
shaderUri,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
assetKind: AssetKind.shader,
|
assetKind: AssetKind.shader,
|
||||||
@ -808,7 +788,6 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
_AssetDirectoryCache cache,
|
_AssetDirectoryCache cache,
|
||||||
Map<_Asset, List<_Asset>> result,
|
Map<_Asset, List<_Asset>> result,
|
||||||
Uri assetUri, {
|
Uri assetUri, {
|
||||||
List<String> excludeDirs = const <String>[],
|
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
}) {
|
}) {
|
||||||
@ -820,10 +799,9 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Iterable<File> files = _fileSystem
|
final Iterable<FileSystemEntity> entities = _fileSystem.directory(directoryPath).listSync();
|
||||||
.directory(directoryPath)
|
|
||||||
.listSync()
|
final Iterable<File> files = entities.whereType<File>();
|
||||||
.whereType<File>();
|
|
||||||
for (final File file in files) {
|
for (final File file in files) {
|
||||||
final String relativePath = _fileSystem.path.relative(file.path, from: assetBase);
|
final String relativePath = _fileSystem.path.relative(file.path, from: assetBase);
|
||||||
final Uri uri = Uri.file(relativePath, windows: _platform.isWindows);
|
final Uri uri = Uri.file(relativePath, windows: _platform.isWindows);
|
||||||
@ -839,6 +817,22 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Iterable<Directory> nonVariantSubDirectories = entities
|
||||||
|
.whereType<Directory>()
|
||||||
|
.where((Directory directory) => !_assetVariantDirectoryRegExp.hasMatch(directory.basename));
|
||||||
|
for (final Directory dir in nonVariantSubDirectories) {
|
||||||
|
final String relativePath = _fileSystem.path.relative(dir.path, from: assetBase);
|
||||||
|
final Uri relativePathsUri = Uri.directory(relativePath, windows: _platform.isWindows);
|
||||||
|
|
||||||
|
_parseAssetsFromFolder(packageConfig,
|
||||||
|
flutterManifest,
|
||||||
|
assetBase,
|
||||||
|
cache,
|
||||||
|
result,
|
||||||
|
relativePathsUri
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _parseAssetFromFile(
|
void _parseAssetFromFile(
|
||||||
@ -1011,54 +1005,48 @@ class _Asset {
|
|||||||
|
|
||||||
// Given an assets directory like this:
|
// Given an assets directory like this:
|
||||||
//
|
//
|
||||||
// assets/foo
|
// assets/foo.png
|
||||||
// assets/var1/foo
|
// assets/2x/foo.png
|
||||||
// assets/var2/foo
|
// assets/3.0x/foo.png
|
||||||
// assets/bar
|
// assets/bar/foo.png
|
||||||
|
// assets/bar.png
|
||||||
//
|
//
|
||||||
// variantsFor('assets/foo') => ['/assets/var1/foo', '/assets/var2/foo']
|
// variantsFor('assets/foo.png') => ['/assets/foo.png', '/assets/2x/foo.png', 'assets/3.0x/foo.png']
|
||||||
// variantsFor('assets/bar') => []
|
// variantsFor('assets/bar.png') => ['/assets/bar.png']
|
||||||
|
// variantsFor('assets/bar/foo.png') => ['/assets/bar/foo.png']
|
||||||
class _AssetDirectoryCache {
|
class _AssetDirectoryCache {
|
||||||
_AssetDirectoryCache(Iterable<String> excluded, this._fileSystem)
|
_AssetDirectoryCache(this._fileSystem);
|
||||||
: _excluded = excluded
|
|
||||||
.map<String>(_fileSystem.path.absolute)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final FileSystem _fileSystem;
|
final FileSystem _fileSystem;
|
||||||
final List<String> _excluded;
|
final Map<String, List<String>> _cache = <String, List<String>>{};
|
||||||
final Map<String, Map<String, List<String>>> _cache = <String, Map<String, List<String>>>{};
|
|
||||||
|
|
||||||
List<String> variantsFor(String assetPath) {
|
List<String> variantsFor(String assetPath) {
|
||||||
final String assetName = _fileSystem.path.basename(assetPath);
|
|
||||||
final String directory = _fileSystem.path.dirname(assetPath);
|
final String directory = _fileSystem.path.dirname(assetPath);
|
||||||
|
|
||||||
if (!_fileSystem.directory(directory).existsSync()) {
|
if (!_fileSystem.directory(directory).existsSync()) {
|
||||||
return const <String>[];
|
return const <String>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_cache[directory] == null) {
|
if (_cache.containsKey(assetPath)) {
|
||||||
final List<String> paths = <String>[];
|
return _cache[assetPath]!;
|
||||||
for (final FileSystemEntity entity in _fileSystem.directory(directory).listSync(recursive: true)) {
|
|
||||||
final String path = entity.path;
|
|
||||||
if (_fileSystem.isFileSync(path)
|
|
||||||
&& assetPath != path
|
|
||||||
&& !_excluded.any((String exclude) => _fileSystem.path.isWithin(exclude, path))) {
|
|
||||||
paths.add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, List<String>> variants = <String, List<String>>{};
|
|
||||||
for (final String path in paths) {
|
|
||||||
final String variantName = _fileSystem.path.basename(path);
|
|
||||||
if (directory == _fileSystem.path.dirname(path)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
variants[variantName] ??= <String>[];
|
|
||||||
variants[variantName]!.add(path);
|
|
||||||
}
|
|
||||||
_cache[directory] = variants;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _cache[directory]![assetName] ?? const <String>[];
|
final List<FileSystemEntity> entitiesInDirectory = _fileSystem.directory(directory).listSync();
|
||||||
|
|
||||||
|
final List<String> pathsOfVariants = <String>[
|
||||||
|
// It's possible that the user specifies only explicit variants (e.g. .../1x/asset.png),
|
||||||
|
// so there does not necessarily need to be a file at the given path.
|
||||||
|
if (_fileSystem.file(assetPath).existsSync())
|
||||||
|
assetPath,
|
||||||
|
...entitiesInDirectory
|
||||||
|
.whereType<Directory>()
|
||||||
|
.where((Directory dir) => _assetVariantDirectoryRegExp.hasMatch(dir.basename))
|
||||||
|
.expand((Directory dir) => dir.listSync())
|
||||||
|
.whereType<File>()
|
||||||
|
.map((File file) => file.path),
|
||||||
|
];
|
||||||
|
|
||||||
|
_cache[assetPath] = pathsOfVariants;
|
||||||
|
return pathsOfVariants;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,6 @@ Future<AssetBundle?> buildAssets({
|
|||||||
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
|
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
|
||||||
final int result = await assetBundle.build(
|
final int result = await assetBundle.build(
|
||||||
manifestPath: manifestPath,
|
manifestPath: manifestPath,
|
||||||
assetDirPath: assetDirPath,
|
|
||||||
packagesPath: packagesPath,
|
packagesPath: packagesPath,
|
||||||
targetPlatform: targetPlatform,
|
targetPlatform: targetPlatform,
|
||||||
);
|
);
|
||||||
|
@ -222,11 +222,11 @@ $assetsSection
|
|||||||
assets: <String>['a/foo'],
|
assets: <String>['a/foo'],
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
final List<String> assets = <String>['a/foo', 'a/2x/foo'];
|
||||||
writeAssets('p/p/', assets);
|
writeAssets('p/p/', assets);
|
||||||
|
|
||||||
const String expectedManifest = '{"packages/test_package/a/foo":'
|
const String expectedManifest = '{"packages/test_package/a/foo":'
|
||||||
'["packages/test_package/a/foo","packages/test_package/a/v/foo"]}';
|
'["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
|
||||||
|
|
||||||
await buildAndVerifyAssets(
|
await buildAndVerifyAssets(
|
||||||
assets,
|
assets,
|
||||||
@ -251,11 +251,11 @@ $assetsSection
|
|||||||
'test_package',
|
'test_package',
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
final List<String> assets = <String>['a/foo', 'a/2x/foo'];
|
||||||
writeAssets('p/p/lib/', assets);
|
writeAssets('p/p/lib/', assets);
|
||||||
|
|
||||||
const String expectedManifest = '{"packages/test_package/a/foo":'
|
const String expectedManifest = '{"packages/test_package/a/foo":'
|
||||||
'["packages/test_package/a/foo","packages/test_package/a/v/foo"]}';
|
'["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
|
||||||
|
|
||||||
await buildAndVerifyAssets(
|
await buildAndVerifyAssets(
|
||||||
assets,
|
assets,
|
||||||
@ -344,15 +344,15 @@ $assetsSection
|
|||||||
assets: <String>['a/foo'],
|
assets: <String>['a/foo'],
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
final List<String> assets = <String>['a/foo', 'a/2x/foo'];
|
||||||
writeAssets('p/p/', assets);
|
writeAssets('p/p/', assets);
|
||||||
writeAssets('p2/p/', assets);
|
writeAssets('p2/p/', assets);
|
||||||
|
|
||||||
const String expectedAssetManifest =
|
const String expectedAssetManifest =
|
||||||
'{"packages/test_package/a/foo":'
|
'{"packages/test_package/a/foo":'
|
||||||
'["packages/test_package/a/foo","packages/test_package/a/v/foo"],'
|
'["packages/test_package/a/foo","packages/test_package/a/2x/foo"],'
|
||||||
'"packages/test_package2/a/foo":'
|
'"packages/test_package2/a/foo":'
|
||||||
'["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';
|
'["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
|
||||||
|
|
||||||
await buildAndVerifyAssets(
|
await buildAndVerifyAssets(
|
||||||
assets,
|
assets,
|
||||||
@ -384,15 +384,15 @@ $assetsSection
|
|||||||
'test_package2',
|
'test_package2',
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
final List<String> assets = <String>['a/foo', 'a/2x/foo'];
|
||||||
writeAssets('p/p/lib/', assets);
|
writeAssets('p/p/lib/', assets);
|
||||||
writeAssets('p2/p/lib/', assets);
|
writeAssets('p2/p/lib/', assets);
|
||||||
|
|
||||||
const String expectedAssetManifest =
|
const String expectedAssetManifest =
|
||||||
'{"packages/test_package/a/foo":'
|
'{"packages/test_package/a/foo":'
|
||||||
'["packages/test_package/a/foo","packages/test_package/a/v/foo"],'
|
'["packages/test_package/a/foo","packages/test_package/a/2x/foo"],'
|
||||||
'"packages/test_package2/a/foo":'
|
'"packages/test_package2/a/foo":'
|
||||||
'["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';
|
'["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
|
||||||
|
|
||||||
await buildAndVerifyAssets(
|
await buildAndVerifyAssets(
|
||||||
assets,
|
assets,
|
||||||
@ -421,12 +421,12 @@ $assetsSection
|
|||||||
'test_package2',
|
'test_package2',
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<String> assets = <String>['a/foo', 'a/v/foo'];
|
final List<String> assets = <String>['a/foo', 'a/2x/foo'];
|
||||||
writeAssets('p2/p/lib/', assets);
|
writeAssets('p2/p/lib/', assets);
|
||||||
|
|
||||||
const String expectedAssetManifest =
|
const String expectedAssetManifest =
|
||||||
'{"packages/test_package2/a/foo":'
|
'{"packages/test_package2/a/foo":'
|
||||||
'["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';
|
'["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
|
||||||
|
|
||||||
await buildAndVerifyAssets(
|
await buildAndVerifyAssets(
|
||||||
assets,
|
assets,
|
||||||
@ -553,7 +553,7 @@ $assetsSection
|
|||||||
writePubspecFile('pubspec.yaml', 'test');
|
writePubspecFile('pubspec.yaml', 'test');
|
||||||
writePackagesFile('test_package:p/p/lib/');
|
writePackagesFile('test_package:p/p/lib/');
|
||||||
|
|
||||||
final List<String> assetsOnDisk = <String>['a/foo','a/b/foo'];
|
final List<String> assetsOnDisk = <String>['a/foo','a/2x/foo'];
|
||||||
final List<String> assetOnManifest = <String>['a/',];
|
final List<String> assetOnManifest = <String>['a/',];
|
||||||
|
|
||||||
writePubspecFile(
|
writePubspecFile(
|
||||||
@ -564,7 +564,7 @@ $assetsSection
|
|||||||
|
|
||||||
writeAssets('p/p/', assetsOnDisk);
|
writeAssets('p/p/', assetsOnDisk);
|
||||||
const String expectedAssetManifest =
|
const String expectedAssetManifest =
|
||||||
'{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/b/foo"]}';
|
'{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
|
||||||
|
|
||||||
await buildAndVerifyAssets(
|
await buildAndVerifyAssets(
|
||||||
assetsOnDisk,
|
assetsOnDisk,
|
||||||
@ -580,7 +580,7 @@ $assetsSection
|
|||||||
writePubspecFile('pubspec.yaml', 'test');
|
writePubspecFile('pubspec.yaml', 'test');
|
||||||
writePackagesFile('test_package:p/p/lib/');
|
writePackagesFile('test_package:p/p/lib/');
|
||||||
|
|
||||||
final List<String> assetsOnDisk = <String>['a/foo', 'a/b/foo'];
|
final List<String> assetsOnDisk = <String>['a/foo', 'a/2x/foo'];
|
||||||
final List<String> assetOnManifest = <String>[];
|
final List<String> assetOnManifest = <String>[];
|
||||||
|
|
||||||
writePubspecFile(
|
writePubspecFile(
|
||||||
|
@ -643,7 +643,7 @@ name: example
|
|||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
assets:
|
assets:
|
||||||
- foo.txt
|
- assets/foo.txt
|
||||||
''');
|
''');
|
||||||
globals.fs.file('assets/foo.txt').createSync(recursive: true);
|
globals.fs.file('assets/foo.txt').createSync(recursive: true);
|
||||||
|
|
||||||
|
@ -9,37 +9,43 @@ import 'package:file/memory.dart';
|
|||||||
|
|
||||||
import 'package:flutter_tools/src/asset.dart';
|
import 'package:flutter_tools/src/asset.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
|
import 'package:flutter_tools/src/base/user_messages.dart';
|
||||||
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
|
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/project.dart';
|
||||||
|
|
||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
import '../src/context.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
String fixPath(String path) {
|
|
||||||
// The in-memory file system is strict about slashes on Windows being the
|
Future<Map<String, List<String>>> extractAssetManifestFromBundle(ManifestAssetBundle bundle) async {
|
||||||
// correct way so until https://github.com/google/file.dart/issues/112 is
|
final String manifestJson = utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes());
|
||||||
// fixed we fix them here.
|
final Map<String, dynamic> parsedJson = json.decode(manifestJson) as Map<String, dynamic>;
|
||||||
// TODO(dantup): Remove this function once the above issue is fixed and
|
final Iterable<String> keys = parsedJson.keys;
|
||||||
// rolls into Flutter.
|
final Map<String, List<String>> parsedManifest = <String, List<String>> {
|
||||||
return path.replaceAll('/', globals.fs.path.separator);
|
for (final String key in keys) key: List<String>.from(parsedJson[key] as List<dynamic>),
|
||||||
|
};
|
||||||
|
return parsedManifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
group('AssetBundle asset variants', () {
|
group('AssetBundle asset variants (with POSIX-style paths)', () {
|
||||||
late FileSystem testFileSystem;
|
late final Platform platform;
|
||||||
setUp(() async {
|
late final FileSystem fs;
|
||||||
testFileSystem = MemoryFileSystem(
|
|
||||||
style: globals.platform.isWindows
|
|
||||||
? FileSystemStyle.windows
|
|
||||||
: FileSystemStyle.posix,
|
|
||||||
);
|
|
||||||
testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_variant_test.');
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('main asset and variants', () async {
|
setUpAll(() {
|
||||||
globals.fs.file('pubspec.yaml')
|
platform = FakePlatform();
|
||||||
..createSync()
|
fs = MemoryFileSystem.test();
|
||||||
..writeAsStringSync(
|
Cache.flutterRoot = Cache.defaultFlutterRoot(
|
||||||
|
platform: platform,
|
||||||
|
fileSystem: fs,
|
||||||
|
userMessages: UserMessages()
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.file('.packages').createSync();
|
||||||
|
|
||||||
|
fs.file('pubspec.yaml').writeAsStringSync(
|
||||||
'''
|
'''
|
||||||
name: test
|
name: test
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -47,46 +53,187 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
assets:
|
assets:
|
||||||
- a/b/c/foo
|
- assets/
|
||||||
'''
|
'''
|
||||||
);
|
);
|
||||||
globals.fs.file('.packages').createSync();
|
});
|
||||||
|
|
||||||
|
testWithoutContext('Only images in folders named with device pixel ratios (e.g. 2x, 3.0x) should be considered as variants of other images', () async {
|
||||||
|
const String image = 'assets/image.jpg';
|
||||||
|
const String image2xVariant = 'assets/2x/image.jpg';
|
||||||
|
const String imageNonVariant = 'assets/notAVariant/image.jpg';
|
||||||
|
|
||||||
final List<String> assets = <String>[
|
final List<String> assets = <String>[
|
||||||
'a/b/c/foo',
|
image,
|
||||||
'a/b/c/var1/foo',
|
image2xVariant,
|
||||||
'a/b/c/var2/foo',
|
imageNonVariant
|
||||||
'a/b/c/var3/foo',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for (final String asset in assets) {
|
for (final String asset in assets) {
|
||||||
globals.fs.file(fixPath(asset))
|
final File assetFile = fs.file(asset);
|
||||||
..createSync(recursive: true)
|
assetFile.createSync(recursive: true);
|
||||||
..writeAsStringSync(asset);
|
assetFile.writeAsStringSync(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetBundle bundle = AssetBundleFactory.instance.createBundle();
|
final ManifestAssetBundle bundle = ManifestAssetBundle(
|
||||||
await bundle.build(packagesPath: '.packages');
|
logger: BufferLogger.test(),
|
||||||
|
fileSystem: fs,
|
||||||
|
platform: platform,
|
||||||
|
);
|
||||||
|
|
||||||
|
await bundle.build(
|
||||||
|
packagesPath: '.packages',
|
||||||
|
flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
|
||||||
|
final List<String> variantsForImage = manifest[image]!;
|
||||||
|
|
||||||
|
expect(variantsForImage, contains(image2xVariant));
|
||||||
|
expect(variantsForImage, isNot(contains(imageNonVariant)));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('Asset directories are recursively searched for assets', () async {
|
||||||
|
const String topLevelImage = 'assets/image.jpg';
|
||||||
|
const String secondLevelImage = 'assets/folder/secondLevel.jpg';
|
||||||
|
const String secondLevel2xVariant = 'assets/folder/2x/secondLevel.jpg';
|
||||||
|
|
||||||
|
final List<String> assets = <String>[
|
||||||
|
topLevelImage,
|
||||||
|
secondLevelImage,
|
||||||
|
secondLevel2xVariant
|
||||||
|
];
|
||||||
|
|
||||||
// The main asset file, /a/b/c/foo, and its variants exist.
|
|
||||||
for (final String asset in assets) {
|
for (final String asset in assets) {
|
||||||
expect(bundle.entries.containsKey(asset), true);
|
final File assetFile = fs.file(asset);
|
||||||
expect(utf8.decode(await bundle.entries[asset]!.contentsAsBytes()), asset);
|
assetFile.createSync(recursive: true);
|
||||||
|
assetFile.writeAsStringSync(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
globals.fs.file(fixPath('a/b/c/foo')).deleteSync();
|
final ManifestAssetBundle bundle = ManifestAssetBundle(
|
||||||
bundle = AssetBundleFactory.instance.createBundle();
|
logger: BufferLogger.test(),
|
||||||
await bundle.build(packagesPath: '.packages');
|
fileSystem: fs,
|
||||||
|
platform: platform,
|
||||||
|
);
|
||||||
|
|
||||||
// Now the main asset file, /a/b/c/foo, does not exist. This is OK because
|
await bundle.build(
|
||||||
// the /a/b/c/*/foo variants do exist.
|
packagesPath: '.packages',
|
||||||
expect(bundle.entries.containsKey('a/b/c/foo'), false);
|
flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
|
||||||
for (final String asset in assets.skip(1)) {
|
);
|
||||||
expect(bundle.entries.containsKey(asset), true);
|
|
||||||
expect(utf8.decode(await bundle.entries[asset]!.contentsAsBytes()), asset);
|
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
|
||||||
|
expect(manifest, contains(secondLevelImage));
|
||||||
|
expect(manifest, contains(topLevelImage));
|
||||||
|
expect(manifest[secondLevelImage], hasLength(2));
|
||||||
|
expect(manifest[secondLevelImage], contains(secondLevelImage));
|
||||||
|
expect(manifest[secondLevelImage], contains(secondLevel2xVariant));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
group('AssetBundle asset variants (with Windows-style filepaths)', () {
|
||||||
|
late final Platform platform;
|
||||||
|
late final FileSystem fs;
|
||||||
|
|
||||||
|
String correctPathSeparators(String path) {
|
||||||
|
// The in-memory file system is strict about slashes on Windows being the
|
||||||
|
// correct way. See https://github.com/google/file.dart/issues/112.
|
||||||
|
return path.replaceAll('/', fs.path.separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
platform = FakePlatform(operatingSystem: 'windows');
|
||||||
|
fs = MemoryFileSystem.test(style: FileSystemStyle.windows);
|
||||||
|
Cache.flutterRoot = Cache.defaultFlutterRoot(
|
||||||
|
platform: platform,
|
||||||
|
fileSystem: fs,
|
||||||
|
userMessages: UserMessages()
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.file('.packages').createSync();
|
||||||
|
|
||||||
|
fs.file('pubspec.yaml').writeAsStringSync(
|
||||||
|
'''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/
|
||||||
|
'''
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('Only images in folders named with device pixel ratios (e.g. 2x, 3.0x) should be considered as variants of other images', () async {
|
||||||
|
const String image = 'assets/image.jpg';
|
||||||
|
const String image2xVariant = 'assets/2x/image.jpg';
|
||||||
|
const String imageNonVariant = 'assets/notAVariant/image.jpg';
|
||||||
|
|
||||||
|
final List<String> assets = <String>[
|
||||||
|
image,
|
||||||
|
image2xVariant,
|
||||||
|
imageNonVariant
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final String asset in assets) {
|
||||||
|
final File assetFile = fs.file(correctPathSeparators(asset));
|
||||||
|
assetFile.createSync(recursive: true);
|
||||||
|
assetFile.writeAsStringSync(asset);
|
||||||
}
|
}
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
FileSystem: () => testFileSystem,
|
final ManifestAssetBundle bundle = ManifestAssetBundle(
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
logger: BufferLogger.test(),
|
||||||
|
fileSystem: fs,
|
||||||
|
platform: platform,
|
||||||
|
);
|
||||||
|
|
||||||
|
await bundle.build(
|
||||||
|
packagesPath: '.packages',
|
||||||
|
flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
|
||||||
|
final List<String> variantsForImage = manifest[image]!;
|
||||||
|
|
||||||
|
expect(variantsForImage, contains(image2xVariant));
|
||||||
|
expect(variantsForImage, isNot(contains(imageNonVariant)));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('Asset directories are recursively searched for assets', () async {
|
||||||
|
const String topLevelImage = 'assets/image.jpg';
|
||||||
|
const String secondLevelImage = 'assets/folder/secondLevel.jpg';
|
||||||
|
const String secondLevel2xVariant = 'assets/folder/2x/secondLevel.jpg';
|
||||||
|
|
||||||
|
final List<String> assets = <String>[
|
||||||
|
topLevelImage,
|
||||||
|
secondLevelImage,
|
||||||
|
secondLevel2xVariant
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final String asset in assets) {
|
||||||
|
final File assetFile = fs.file(correctPathSeparators(asset));
|
||||||
|
assetFile.createSync(recursive: true);
|
||||||
|
assetFile.writeAsStringSync(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ManifestAssetBundle bundle = ManifestAssetBundle(
|
||||||
|
logger: BufferLogger.test(),
|
||||||
|
fileSystem: fs,
|
||||||
|
platform: platform,
|
||||||
|
);
|
||||||
|
|
||||||
|
await bundle.build(
|
||||||
|
packagesPath: '.packages',
|
||||||
|
flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
|
||||||
|
expect(manifest, contains(secondLevelImage));
|
||||||
|
expect(manifest, contains(topLevelImage));
|
||||||
|
expect(manifest[secondLevelImage], hasLength(2));
|
||||||
|
expect(manifest[secondLevelImage], contains(secondLevelImage));
|
||||||
|
expect(manifest[secondLevelImage], contains(secondLevel2xVariant));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user