Reland 4: [CupertinoActionSheet] Match colors to native (#150442)

Relands https://github.com/flutter/flutter/pull/149568 (first attempt) or https://github.com/flutter/flutter/pull/150386 (latest attempt), which was reverted in https://github.com/flutter/flutter/pull/150413 due to a one-pixel difference on the debug banner (in the tilted border of the rotated text).

This attempt removed the debug banner from tests (see https://github.com/flutter/flutter/pull/150442/commits/31cbcb1dc61c9053dd41687fc5c1bdfc64264622).

<img width="949" alt="image" src="https://github.com/flutter/flutter/assets/1596656/78f26f72-b69f-41ee-9134-2e2a9e8e1bdd">
diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart
index c482b5f..15cd34a 100644
--- a/packages/flutter/lib/src/cupertino/dialog.dart
+++ b/packages/flutter/lib/src/cupertino/dialog.dart
@@ -65,8 +65,9 @@
   inherit: false,
   fontSize: 13.0,
   fontWeight: FontWeight.w400,
-  color: _kActionSheetContentTextColor,
   textBaseline: TextBaseline.alphabetic,
+  // The `color` is configured by _kActionSheetContentTextColor to be dynamic on
+  // context.
 );
 
 // Generic constants shared between Dialog and ActionSheet.
@@ -104,35 +105,53 @@
 // Translucent light gray that is painted on top of the blurred backdrop as the
 // background color of a pressed button.
 // Eyeballed from iOS 13 beta simulator.
-const Color _kPressedColor = CupertinoDynamicColor.withBrightness(
+const Color _kDialogPressedColor = CupertinoDynamicColor.withBrightness(
   color: Color(0xFFE1E1E1),
   darkColor: Color(0xFF2E2E2E),
 );
 
+// Translucent light gray that is painted on top of the blurred backdrop as the
+// background color of a pressed button.
+// Eyeballed from iOS 17 simulator.
+const Color _kActionSheetPressedColor = CupertinoDynamicColor.withBrightness(
+  color: Color(0xCAE0E0E0),
+  darkColor: Color(0xC1515151),
+);
+
+const Color _kActionSheetCancelColor = CupertinoDynamicColor.withBrightness(
+  color: Color(0xFFFFFFFF),
+  darkColor: Color(0xFF2C2C2C),
+);
 const Color _kActionSheetCancelPressedColor = CupertinoDynamicColor.withBrightness(
   color: Color(0xFFECECEC),
-  darkColor: Color(0xFF49494B),
+  darkColor: Color(0xFF494949),
 );
 
 // Translucent, very light gray that is painted on top of the blurred backdrop
 // as the action sheet's background color.
 // TODO(LongCatIsLooong): https://github.com/flutter/flutter/issues/39272. Use
 // System Materials once we have them.
-// Extracted from https://developer.apple.com/design/resources/.
+// Eyeballed from iOS 17 simulator.
 const Color _kActionSheetBackgroundColor = CupertinoDynamicColor.withBrightness(
-  color: Color(0xC7F9F9F9),
-  darkColor: Color(0xC7252525),
+  color: Color(0xC8FCFCFC),
+  darkColor: Color(0xBE292929),
 );
 
 // The gray color used for text that appears in the title area.
-// Extracted from https://developer.apple.com/design/resources/.
-const Color _kActionSheetContentTextColor = Color(0xFF8F8F8F);
+// Eyeballed from iOS 17 simulator.
+const Color _kActionSheetContentTextColor = CupertinoDynamicColor.withBrightness(
+  color: Color(0x851D1D1D),
+  darkColor: Color(0x96F1F1F1),
+);
 
 // Translucent gray that is painted on top of the blurred backdrop in the gap
 // areas between the content section and actions section, as well as between
 // buttons.
-// Eye-balled from iOS 13 beta simulator.
-const Color _kActionSheetButtonDividerColor = _kActionSheetContentTextColor;
+// Eyeballed from iOS 17 simulator.
+const Color _kActionSheetButtonDividerColor = CupertinoDynamicColor.withBrightness(
+  color: Color(0xD4C9C9C9),
+  darkColor: Color(0xD57D7D7D),
+);
 
 // The alert dialog layout policy changes depending on whether the user is using
 // a "regular" font size vs a "large" font size. This is a spectrum. There are
@@ -841,6 +860,9 @@
 
   Widget _buildContent(BuildContext context) {
     final List<Widget> content = <Widget>[];
+    final TextStyle textStyle = _kActionSheetContentStyle.copyWith(
+      color: CupertinoDynamicColor.resolve(_kActionSheetContentTextColor, context),
+    );
     if (hasContent) {
       final Widget titleSection = _CupertinoAlertContentSection(
         title: widget.title,
@@ -859,11 +881,11 @@
           top: widget.title == null ? _kActionSheetContentVerticalPadding : 0.0,
         ),
         titleTextStyle: widget.message == null
-            ? _kActionSheetContentStyle
-            : _kActionSheetContentStyle.copyWith(fontWeight: FontWeight.w600),
+            ? textStyle
+            : textStyle.copyWith(fontWeight: FontWeight.w600),
         messageTextStyle: widget.title == null
-            ? _kActionSheetContentStyle.copyWith(fontWeight: FontWeight.w600)
-            : _kActionSheetContentStyle,
+            ? textStyle.copyWith(fontWeight: FontWeight.w600)
+            : textStyle,
         additionalPaddingBetweenTitleAndMessage: const EdgeInsets.only(top: 4.0),
       );
       content.add(Flexible(child: titleSection));
@@ -908,7 +930,7 @@
               hasContent: hasContent,
               contentSection: Builder(builder: _buildContent),
               actions: widget.actions,
-              dividerColor: _kActionSheetButtonDividerColor,
+              dividerColor: CupertinoDynamicColor.resolve(_kActionSheetButtonDividerColor, context),
             ),
           ),
         ),
@@ -1115,19 +1137,19 @@
     BorderRadius? borderRadius;
     if (!widget.isCancel) {
       backgroundColor = isBeingPressed
-        ? _kPressedColor
-        : CupertinoDynamicColor.resolve(_kActionSheetBackgroundColor, context);
+        ? _kActionSheetPressedColor
+        : _kActionSheetBackgroundColor;
     } else {
       backgroundColor = isBeingPressed
-          ? _kActionSheetCancelPressedColor
-        : CupertinoColors.secondarySystemGroupedBackground;
+        ? _kActionSheetCancelPressedColor
+        : _kActionSheetCancelColor;
       borderRadius = const BorderRadius.all(Radius.circular(_kCornerRadius));
     }
     return MetaData(
       metaData: this,
       child: Container(
         decoration: BoxDecoration(
-          color: backgroundColor,
+          color: CupertinoDynamicColor.resolve(backgroundColor, context),
           borderRadius: borderRadius,
         ),
         child: widget.child,
@@ -2269,7 +2291,7 @@
               : _kCupertinoDialogWidth,
       dividerThickness: _dividerThickness,
       dialogColor: CupertinoDynamicColor.resolve(_kDialogColor, context),
-      dialogPressedColor: CupertinoDynamicColor.resolve(_kPressedColor, context),
+      dialogPressedColor: CupertinoDynamicColor.resolve(_kDialogPressedColor, context),
       dividerColor: CupertinoDynamicColor.resolve(CupertinoColors.separator, context),
       hasCancelButton: _hasCancelButton,
     );
@@ -2283,7 +2305,7 @@
             : _kCupertinoDialogWidth
       ..dividerThickness = _dividerThickness
       ..dialogColor = CupertinoDynamicColor.resolve(_kDialogColor, context)
-      ..dialogPressedColor = CupertinoDynamicColor.resolve(_kPressedColor, context)
+      ..dialogPressedColor = CupertinoDynamicColor.resolve(_kDialogPressedColor, context)
       ..dividerColor = CupertinoDynamicColor.resolve(CupertinoColors.separator, context)
       ..hasCancelButton = _hasCancelButton;
   }
diff --git a/packages/flutter/test/cupertino/action_sheet_test.dart b/packages/flutter/test/cupertino/action_sheet_test.dart
index 0f272d8..0479e4e 100644
--- a/packages/flutter/test/cupertino/action_sheet_test.dart
+++ b/packages/flutter/test/cupertino/action_sheet_test.dart
@@ -18,6 +18,71 @@
 import '../widgets/semantics_tester.dart';
 
 void main() {
+  testWidgets('Overall looks correctly under light theme', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      TestScaffoldApp(
+        theme: const CupertinoThemeData(brightness: Brightness.light),
+        actionSheet: CupertinoActionSheet(
+          message: const Text('The title'),
+          actions: <Widget>[
+            CupertinoActionSheetAction(child: const Text('One'), onPressed: () {}),
+            CupertinoActionSheetAction(child: const Text('Two'), onPressed: () {}),
+          ],
+          cancelButton: CupertinoActionSheetAction(child: const Text('Cancel'), onPressed: () {}),
+        ),
+      ),
+    );
+
+    await tester.tap(find.text('Go'));
+    await tester.pumpAndSettle();
+
+    final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('One')));
+    await tester.pumpAndSettle();
+    // This golden file also verifies the structure of an action sheet that
+    // has a message, no title, and no overscroll for any sections (in contrast
+    // to cupertinoActionSheet.dark-theme.png).
+    await expectLater(
+      find.byType(CupertinoApp),
+      matchesGoldenFile('cupertinoActionSheet.overall-light-theme.png'),
+    );
+
+    await gesture.up();
+  });
+
+  testWidgets('Overall looks correctly under dark theme', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      TestScaffoldApp(
+        theme: const CupertinoThemeData(brightness: Brightness.dark),
+        actionSheet: CupertinoActionSheet(
+          title: const Text('The title'),
+          message: const Text('The message'),
+          actions: List<Widget>.generate(20, (int i) =>
+            CupertinoActionSheetAction(
+              onPressed: () {},
+              child: Text('Button $i'),
+            ),
+          ),
+          cancelButton: CupertinoActionSheetAction(child: const Text('Cancel'), onPressed: () {}),
+        ),
+      ),
+    );
+
+    await tester.tap(find.text('Go'));
+    await tester.pumpAndSettle();
+
+    final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('Button 0')));
+    await tester.pumpAndSettle();
+    // This golden file also verifies the structure of an action sheet that
+    // has both a message and a title, and an overscrolled action section (in
+    // contrast to cupertinoActionSheet.light-theme.png).
+    await expectLater(
+      find.byType(CupertinoApp),
+      matchesGoldenFile('cupertinoActionSheet.overall-dark-theme.png'),
+    );
+
+    await gesture.up();
+  });
+
   testWidgets('Verify that a tap on modal barrier dismisses an action sheet', (WidgetTester tester) async {
     await tester.pumpWidget(
       createAppWithButtonThatLaunchesActionSheet(
@@ -1676,6 +1741,54 @@
   );
 }
 
+// Shows an app that has a button with text "Go", and clicking this button
+// displays the `actionSheet` and hides the button.
+//
+// The `theme` will be applied to the app and determines the background.
+class TestScaffoldApp extends StatefulWidget {
+  const TestScaffoldApp({super.key, required this.theme, required this.actionSheet});
+  final CupertinoThemeData theme;
+  final Widget actionSheet;
+
+  @override
+  TestScaffoldAppState createState() => TestScaffoldAppState();
+}
+
+class TestScaffoldAppState extends State<TestScaffoldApp> {
+  bool _pressedButton = false;
+
+  @override
+  Widget build(BuildContext context) {
+    return CupertinoApp(
+      // This CupertinoApp is captured in golden test as a whole. The debug
+      // banner contains tilted text, whose anti-alias might cause false
+      // negative result. https://github.com/flutter/flutter/pull/150442
+      debugShowCheckedModeBanner: false,
+      theme: widget.theme,
+      home: Builder(builder: (BuildContext context) =>
+        CupertinoPageScaffold(
+          child: Center(
+            child: _pressedButton ? Container() : CupertinoButton(
+              onPressed: () {
+                setState(() {
+                  _pressedButton = true;
+                });
+                showCupertinoModalPopup<void>(
+                  context: context,
+                  builder: (BuildContext context) {
+                    return widget.actionSheet;
+                  },
+                );
+              },
+              child: const Text('Go'),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
+
 Widget boilerplate(Widget child) {
   return Directionality(
     textDirection: TextDirection.ltr,