resolve method

ImageStream resolve (ImageConfiguration configuration)

Resolves this image provider using the given configuration, returning an ImageStream.

This is the public entry-point of the ImageProvider class hierarchy.

Subclasses should implement obtainKey and load, which are used by this method.

Implementation

ImageStream resolve(ImageConfiguration configuration) {
  assert(configuration != null);
  final ImageStream stream = ImageStream();
  T obtainedKey;
  bool didError = false;
  Future<void> handleError(dynamic exception, StackTrace stack) async {
    if (didError) {
      return;
    }
    didError = true;
    await null; // wait an event turn in case a listener has been added to the image stream.
    final _ErrorImageCompleter imageCompleter = _ErrorImageCompleter();
    stream.setCompleter(imageCompleter);
    imageCompleter.setError(
      exception: exception,
      stack: stack,
      context: 'while resolving an image',
      silent: true, // could be a network error or whatnot
      informationCollector: (StringBuffer information) {
        information.writeln('Image provider: $this');
        information.writeln('Image configuration: $configuration');
        if (obtainedKey != null) {
          information.writeln('Image key: $obtainedKey');
        }
      },
    );
  }

  // If an error is added to a synchronous completer before a listener has been
  // added, it can throw an error both into the zone and up the stack. Thus, it
  // looks like the error has been caught, but it is in fact also bubbling to the
  // zone. Since we cannot prevent all usage of Completer.sync here, or rather
  // that changing them would be too breaking, we instead hook into the same
  // zone mechanism to intercept the uncaught error and deliver it to the
  // image stream's error handler. Note that these errors may be duplicated,
  // hence the need for the `didError` flag.
  final Zone dangerZone = Zone.current.fork(
    specification: ZoneSpecification(
      handleUncaughtError: (Zone zone, ZoneDelegate delegate, Zone parent, Object error, StackTrace stackTrace) {
        handleError(error, stackTrace);
      }
    )
  );
  dangerZone.runGuarded(() {
    Future<T> key;
    try {
      key = obtainKey(configuration);
    } catch (error, stackTrace) {
      handleError(error, stackTrace);
      return;
    }
    key.then<void>((T key) {
      obtainedKey = key;
      final ImageStreamCompleter completer = PaintingBinding.instance
          .imageCache.putIfAbsent(key, () => load(key), onError: handleError);
      if (completer != null) {
        stream.setCompleter(completer);
      }
    }).catchError(handleError);
  });
  return stream;
}