obtainKey method

  1. @override
Future<AssetBundleImageKey> obtainKey(
  1. 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;
}