[Material] Add method to get dark mode overlay color without needing BuildContext (#70669)

diff --git a/packages/flutter/lib/src/material/elevation_overlay.dart b/packages/flutter/lib/src/material/elevation_overlay.dart
index cc699c6..cc5f20f 100644
--- a/packages/flutter/lib/src/material/elevation_overlay.dart
+++ b/packages/flutter/lib/src/material/elevation_overlay.dart
@@ -48,7 +48,7 @@
         theme.applyElevationOverlayColor &&
         theme.brightness == Brightness.dark &&
         color.withOpacity(1.0) == theme.colorScheme.surface.withOpacity(1.0)) {
-      return Color.alphaBlend(overlayColor(context, elevation), color);
+      return colorWithOverlay(color, theme.colorScheme.onSurface, elevation);
     }
     return color;
   }
@@ -62,10 +62,26 @@
   ///    specifies the exact overlay values for a given elevation.
   static Color overlayColor(BuildContext context, double elevation) {
     final ThemeData theme = Theme.of(context);
+    return _overlayColor(theme.colorScheme.onSurface, elevation);
+  }
+
+  /// Returns a color blended by laying a semi-transparent overlay (using the
+  /// [overlay] color) on top of a surface (using the [surface] color).
+  ///
+  /// The opacity of the overlay depends on [elevation]. As [elevation]
+  /// increases, the opacity will also increase.
+  ///
+  /// See https://material.io/design/color/dark-theme.html#properties.
+  static Color colorWithOverlay(Color surface, Color overlay, double elevation) {
+    return Color.alphaBlend(_overlayColor(overlay, elevation), surface);
+  }
+
+  /// Applies an opacity to [color] based on [elevation].
+  static Color _overlayColor(Color color, double elevation) {
     // Compute the opacity for the given elevation
     // This formula matches the values in the spec:
     // https://material.io/design/color/dark-theme.html#properties
     final double opacity = (4.5 * math.log(elevation + 1) + 2) / 100.0;
-    return theme.colorScheme.onSurface.withOpacity(opacity);
+    return color.withOpacity(opacity);
   }
 }
diff --git a/packages/flutter/test/material/material_test.dart b/packages/flutter/test/material/material_test.dart
index 1e13244..33c91a7 100644
--- a/packages/flutter/test/material/material_test.dart
+++ b/packages/flutter/test/material/material_test.dart
@@ -362,6 +362,35 @@
       expect(model.color, equals(surfaceColorWithOverlay));
       expect(model.color, isNot(equals(surfaceColor)));
     });
+
+    testWidgets('Expected overlay color can be computed using colorWithOverlay', (WidgetTester tester) async {
+      const Color surfaceColor = Color(0xFF123456);
+      const Color onSurfaceColor = Color(0xFF654321);
+      const double elevation = 8.0;
+
+      final Color surfaceColorWithOverlay =
+        ElevationOverlay.colorWithOverlay(surfaceColor, onSurfaceColor, elevation);
+
+      await tester.pumpWidget(
+        Theme(
+          data: ThemeData(
+            applyElevationOverlayColor: true,
+            colorScheme: const ColorScheme.dark(
+              surface: surfaceColor,
+              onSurface: onSurfaceColor,
+            ),
+          ),
+          child: buildMaterial(
+            color: surfaceColor,
+            elevation: elevation,
+          ),
+        ),
+      );
+
+      final RenderPhysicalShape model = getModel(tester);
+      expect(model.color, equals(surfaceColorWithOverlay));
+      expect(model.color, isNot(equals(surfaceColor)));
+    });
   });
 
   group('Transparency clipping', () {