obtainKey method
- ImageConfiguration configuration
override
Converts an ImageProvider's settings plus an ImageConfiguration to a key that describes the precise image to load.
The type of the key is determined by the subclass. It is a value that unambiguously identifies the image (including its scale) that the loadImage method will fetch. Different ImageProviders given the same constructor arguments and ImageConfiguration objects should return keys that are '==' to each other (possibly by using a class for the key that itself implements ==).
If the result can be determined synchronously, this function should return a SynchronousFuture. This allows image resolution to progress synchronously during a frame rather than delaying image loading.
Implementation
@override
Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
// This function tries to return a SynchronousFuture if possible. We do this
// because otherwise showing an image would always take at least one frame,
// which would be sad. (This code is called from inside build/layout/paint,
// which all happens in one call frame; using native Futures would guarantee
// that we resolve each future in a new call frame, and thus not in this
// build/layout/paint sequence.)
final AssetBundle chosenBundle = bundle ?? configuration.bundle ?? rootBundle;
Completer<AssetBundleImageKey>? completer;
Future<AssetBundleImageKey>? result;
AssetManifest.loadFromAssetBundle(chosenBundle)
.then((AssetManifest manifest) {
final Iterable<AssetMetadata>? candidateVariants = manifest.getAssetVariants(keyName);
final AssetMetadata chosenVariant = _chooseVariant(
keyName,
configuration,
candidateVariants,
);
final AssetBundleImageKey key = AssetBundleImageKey(
bundle: chosenBundle,
name: chosenVariant.key,
scale: chosenVariant.targetDevicePixelRatio ?? _naturalResolution,
);
if (completer != null) {
// We already returned from this function, which means we are in the
// asynchronous mode. Pass the value to the completer. The completer's
// future is what we returned.
completer.complete(key);
} else {
// We haven't yet returned, so we must have been called synchronously
// just after loadStructuredData returned (which means it provided us
// with a SynchronousFuture). Let's return a SynchronousFuture
// ourselves.
result = SynchronousFuture<AssetBundleImageKey>(key);
}
})
.onError((Object error, StackTrace stack) {
// We had an error. (This guarantees we weren't called synchronously.)
// Forward the error to the caller.
assert(completer != null);
assert(result == null);
completer!.completeError(error, stack);
});
if (result != null) {
// The code above ran synchronously, and came up with an answer.
// Return the SynchronousFuture that we created above.
return result!;
}
// The code above hasn't yet run its "then" handler yet. Let's prepare a
// completer for it to use when it does run.
completer = Completer<AssetBundleImageKey>();
return completer.future;
}