/
asset_manifest.dart
153 lines (134 loc) · 6.06 KB
/
asset_manifest.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2014 The Flutter 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:convert';
import 'package:flutter/foundation.dart';
import 'asset_bundle.dart';
import 'message_codecs.dart';
// We use .bin as the extension since it is well-known to represent
// data in some arbitrary binary format.
const String _kAssetManifestFilename = 'AssetManifest.bin';
// We use the same bin file for the web, but re-encoded as JSON(base64(bytes))
// so it can be downloaded by even the dumbest of browsers.
// See https://github.com/flutter/flutter/issues/128456
const String _kAssetManifestWebFilename = 'AssetManifest.bin.json';
/// Contains details about available assets and their variants.
/// See [Resolution-aware image assets](https://docs.flutter.dev/ui/assets-and-images#resolution-aware)
/// to learn about asset variants and how to declare them.
abstract class AssetManifest {
/// Loads asset manifest data from an [AssetBundle] object and creates an
/// [AssetManifest] object from that data.
static Future<AssetManifest> loadFromAssetBundle(AssetBundle bundle) {
// The AssetManifest file contains binary data.
//
// On the web, the build process wraps this binary data in json+base64 so
// it can be transmitted over the network without special configuration
// (see #131382).
if (kIsWeb) {
// On the web, the AssetManifest is downloaded as a String, then
// json+base64-decoded to get to the binary data.
return bundle.loadStructuredData(_kAssetManifestWebFilename, (String jsonData) async {
// Decode the manifest JSON file to the underlying BIN, and convert to ByteData.
final ByteData message = ByteData.sublistView(base64.decode(json.decode(jsonData) as String));
// Now we can keep operating as usual.
return _AssetManifestBin.fromStandardMessageCodecMessage(message);
});
}
// On every other platform, the binary file contents are used directly.
return bundle.loadStructuredBinaryData(_kAssetManifestFilename, _AssetManifestBin.fromStandardMessageCodecMessage);
}
/// Lists the keys of all main assets. This does not include assets
/// that are variants of other assets.
///
/// The logical key maps to the path of an asset specified in the pubspec.yaml
/// file at build time.
///
/// See [Specifying assets](https://docs.flutter.dev/development/ui/assets-and-images#specifying-assets)
/// and [Loading assets](https://docs.flutter.dev/development/ui/assets-and-images#loading-assets)
/// for more information.
List<String> listAssets();
/// Retrieves metadata about an asset and its variants. Returns null if the
/// key was not found in the asset manifest.
///
/// This method considers a main asset to be a variant of itself. The returned
/// list will include it if it exists.
List<AssetMetadata>? getAssetVariants(String key);
}
// Lazily parses the binary asset manifest into a data structure that's easier to work
// with.
//
// The binary asset manifest is a map of asset keys to a list of objects
// representing the asset's variants.
//
// The entries with each variant object are:
// - "asset": the location of this variant to load it from.
// - "dpr": The device-pixel-ratio that the asset is best-suited for.
//
// New fields could be added to this object schema to support new asset variation
// features, such as themes, locale/region support, reading directions, and so on.
class _AssetManifestBin implements AssetManifest {
_AssetManifestBin(Map<Object?, Object?> standardMessageData): _data = standardMessageData;
factory _AssetManifestBin.fromStandardMessageCodecMessage(ByteData message) {
final dynamic data = const StandardMessageCodec().decodeMessage(message);
return _AssetManifestBin(data as Map<Object?, Object?>);
}
final Map<Object?, Object?> _data;
final Map<String, List<AssetMetadata>> _typeCastedData = <String, List<AssetMetadata>>{};
@override
List<AssetMetadata>? getAssetVariants(String key) {
// We lazily delay typecasting to prevent a performance hiccup when parsing
// large asset manifests. This is important to keep an app's first asset
// load fast.
if (!_typeCastedData.containsKey(key)) {
final Object? variantData = _data[key];
if (variantData == null) {
return null;
}
_typeCastedData[key] = ((_data[key] ?? <Object?>[]) as Iterable<Object?>)
.cast<Map<Object?, Object?>>()
.map((Map<Object?, Object?> data) {
final String asset = data['asset']! as String;
final Object? dpr = data['dpr'];
return AssetMetadata(
key: data['asset']! as String,
targetDevicePixelRatio: dpr as double?,
main: key == asset,
);
})
.toList();
_data.remove(key);
}
return _typeCastedData[key]!;
}
@override
List<String> listAssets() {
return <String>[..._data.keys.cast<String>(), ..._typeCastedData.keys];
}
}
/// Contains information about an asset.
@immutable
class AssetMetadata {
/// Creates an object containing information about an asset.
const AssetMetadata({
required this.key,
required this.targetDevicePixelRatio,
required this.main,
});
/// The device pixel ratio that this asset is most ideal for. This is determined
/// by the name of the parent folder of the asset file. For example, if the
/// parent folder is named "3.0x", the target device pixel ratio of that
/// asset will be interpreted as 3.
///
/// This will be null if the parent folder name is not a ratio value followed
/// by an "x".
///
/// See [Resolution-aware image assets](https://docs.flutter.dev/development/ui/assets-and-images#resolution-aware)
/// for more information.
final double? targetDevicePixelRatio;
/// The asset's key, which is the path to the asset specified in the pubspec.yaml
/// file at build time.
final String key;
/// Whether or not this is a main asset. In other words, this is true if
/// this asset is not a variant of another asset.
final bool main;
}