Add outer try block for obtainKey errors. Add docs. (#27953)

* Add outer try block for obtainKey errors. Add docs.

* change to // from ///, and do specific error handling

* Add return statement after error

* Add test
diff --git a/packages/flutter/lib/src/painting/image_provider.dart b/packages/flutter/lib/src/painting/image_provider.dart
index 161517d..1a2845c 100644
--- a/packages/flutter/lib/src/painting/image_provider.dart
+++ b/packages/flutter/lib/src/painting/image_provider.dart
@@ -280,13 +280,28 @@
         }
       );
     }
-    obtainKey(configuration).then<void>((T key) {
+
+    // `obtainKey` can throw both sync and async errors.
+    // `catchError` handles cases where async errors are thrown and the try block is for sync errors.
+    //
+    // `onError` callback on [ImageCache] handles the cases where `obtainKey` is a sync future and `load` throws.
+    Future<T> key;
+    try {
+      key = obtainKey(configuration);
+    } catch (error, stackTrace) {
+      handleError(error, stackTrace);
+      return stream;
+    }
+
+    key.then<void>((T key) {
       obtainedKey = key;
-      final ImageStreamCompleter completer = PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key), onError: handleError);
+      final ImageStreamCompleter completer = PaintingBinding.instance
+          .imageCache.putIfAbsent(key, () => load(key), onError: handleError);
       if (completer != null) {
         stream.setCompleter(completer);
       }
     }).catchError(handleError);
+
     return stream;
   }
 
diff --git a/packages/flutter/test/painting/image_provider_test.dart b/packages/flutter/test/painting/image_provider_test.dart
index 51e698e..b49e5e9 100644
--- a/packages/flutter/test/painting/image_provider_test.dart
+++ b/packages/flutter/test/painting/image_provider_test.dart
@@ -71,4 +71,19 @@
       expect(await caughtError.future, true);
     });
   });
+
+  test('ImageProvide.obtainKey errors will be caught', () async {
+    final ImageProvider imageProvider = ObtainKeyErrorImageProvider();
+    final Completer<bool> caughtError = Completer<bool>();
+    FlutterError.onError = (FlutterErrorDetails details) {
+      caughtError.complete(false);
+    };
+    final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
+    stream.addListener((ImageInfo info, bool syncCall) {
+      caughtError.complete(false);
+    }, onError: (dynamic error, StackTrace stackTrace) {
+      caughtError.complete(true);
+    });
+    expect(await caughtError.future, true);
+  });
 }
diff --git a/packages/flutter/test/painting/mocks_for_image_cache.dart b/packages/flutter/test/painting/mocks_for_image_cache.dart
index 0425500..e0353d3 100644
--- a/packages/flutter/test/painting/mocks_for_image_cache.dart
+++ b/packages/flutter/test/painting/mocks_for_image_cache.dart
@@ -94,4 +94,16 @@
   }
 }
 
+class ObtainKeyErrorImageProvider extends ImageProvider<ObtainKeyErrorImageProvider> {
+  @override
+  ImageStreamCompleter load(ObtainKeyErrorImageProvider key) {
+    throw Error();
+  }
+
+  @override
+  Future<ObtainKeyErrorImageProvider> obtainKey(ImageConfiguration configuration) {
+    throw Error();
+  }
+}
+
 class TestImageStreamCompleter extends ImageStreamCompleter {}