circleAvatar: foreground Image uses background Image as a fall-back  (#71783)

* foregroundImage property added

* fixed documentaion nits

* test for fallback to background image on foreground image failover

* golden test
diff --git a/packages/flutter/lib/src/material/circle_avatar.dart b/packages/flutter/lib/src/material/circle_avatar.dart
index 3ff4245..d449316 100644
--- a/packages/flutter/lib/src/material/circle_avatar.dart
+++ b/packages/flutter/lib/src/material/circle_avatar.dart
@@ -17,8 +17,13 @@
 /// such an image, the user's initials. A given user's initials should
 /// always be paired with the same background color, for consistency.
 ///
+/// If [foregroundImage] fails then [backgroundImage] is used. If
+/// [backgroundImage] fails too, [backgroundColor] is used.
+///
 /// The [onBackgroundImageError] parameter must be null if the [backgroundImage]
 /// is null.
+/// The [onForegroundImageError] parameter must be null if the [foregroundImage]
+/// is null.
 ///
 /// {@tool snippet}
 ///
@@ -60,13 +65,16 @@
     this.child,
     this.backgroundColor,
     this.backgroundImage,
+    this.foregroundImage,
     this.onBackgroundImageError,
+    this.onForegroundImageError,
     this.foregroundColor,
     this.radius,
     this.minRadius,
     this.maxRadius,
   }) : assert(radius == null || (minRadius == null && maxRadius == null)),
        assert(backgroundImage != null || onBackgroundImageError == null),
+       assert(foregroundImage != null || onForegroundImageError== null),
        super(key: key);
 
   /// The widget below this widget in the tree.
@@ -95,13 +103,24 @@
   /// The background image of the circle. Changing the background
   /// image will cause the avatar to animate to the new image.
   ///
+  /// Typically used as a fallback image for [foregroundImage].
+  ///
   /// If the [CircleAvatar] is to have the user's initials, use [child] instead.
   final ImageProvider? backgroundImage;
 
+  /// The foreground image of the circle.
+  ///
+  /// Typically used as profile image. For fallback use [backgroundImage].
+  final ImageProvider? foregroundImage;
+
   /// An optional error callback for errors emitted when loading
   /// [backgroundImage].
   final ImageErrorListener? onBackgroundImageError;
 
+  /// An optional error callback for errors emitted when loading
+  /// [foregroundImage].
+  final ImageErrorListener? onForegroundImageError;
+
   /// The size of the avatar, expressed as the radius (half the diameter).
   ///
   /// If [radius] is specified, then neither [minRadius] nor [maxRadius] may be
@@ -217,6 +236,16 @@
           : null,
         shape: BoxShape.circle,
       ),
+      foregroundDecoration: foregroundImage != null
+          ? BoxDecoration(
+              image: DecorationImage(
+                image: foregroundImage!,
+                onError: onForegroundImageError,
+                fit: BoxFit.cover,
+              ),
+              shape: BoxShape.circle,
+            )
+          : null,
       child: child == null
           ? null
           : Center(
diff --git a/packages/flutter/test/material/circle_avatar_test.dart b/packages/flutter/test/material/circle_avatar_test.dart
index cb4d31c..a49de4d 100644
--- a/packages/flutter/test/material/circle_avatar_test.dart
+++ b/packages/flutter/test/material/circle_avatar_test.dart
@@ -9,6 +9,7 @@
 import 'package:flutter_test/flutter_test.dart';
 
 import '../image_data.dart';
+import '../painting/mocks_for_image_cache.dart';
 
 void main() {
   testWidgets('CircleAvatar with dark background color', (WidgetTester tester) async {
@@ -72,6 +73,51 @@
     expect(decoration.image!.fit, equals(BoxFit.cover));
   });
 
+  testWidgets('CircleAvatar with image foreground', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      wrap(
+        child: CircleAvatar(
+          foregroundImage: MemoryImage(Uint8List.fromList(kBlueRectPng)),
+          radius: 50.0,
+        ),
+      ),
+    );
+
+    final RenderConstrainedBox box = tester.renderObject(find.byType(CircleAvatar));
+    expect(box.size, equals(const Size(100.0, 100.0)));
+    final RenderDecoratedBox child = box.child! as RenderDecoratedBox;
+    final BoxDecoration decoration = child.decoration as BoxDecoration;
+    expect(decoration.image!.fit, equals(BoxFit.cover));
+  });
+
+  testWidgets('CircleAvatar backgroundImage is used as a fallback for foregroundImage', (WidgetTester tester) async {
+    final ErrorImageProvider errorImage = ErrorImageProvider();
+    bool caughtForegroundImageError = false;
+    await tester.pumpWidget(
+      wrap(
+        child: RepaintBoundary(
+          child: CircleAvatar(
+          foregroundImage: errorImage,
+          backgroundImage: MemoryImage(Uint8List.fromList(kBlueRectPng)),
+          radius: 50.0,
+          onForegroundImageError: (_,__) => caughtForegroundImageError = true,
+          ),
+        ),
+      ),
+    );
+
+    expect(caughtForegroundImageError, true);
+    final RenderConstrainedBox box = tester.renderObject(find.byType(CircleAvatar));
+    expect(box.size, equals(const Size(100.0, 100.0)));
+    final RenderDecoratedBox child = box.child! as RenderDecoratedBox;
+    final BoxDecoration decoration = child.decoration as BoxDecoration;
+    expect(decoration.image!.fit, equals(BoxFit.cover));
+    await expectLater(
+      find.byType(CircleAvatar),
+      matchesGoldenFile('circle_avatar.fallback.png'),
+    );
+  });
+
   testWidgets('CircleAvatar with foreground color', (WidgetTester tester) async {
     final Color foregroundColor = Colors.red.shade100;
     await tester.pumpWidget(