diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index c482b5fee4999..990c3bffc18c6 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -104,35 +104,35 @@ const Color _kDialogColor = CupertinoDynamicColor.withBrightness( // 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), ); -const Color _kActionSheetCancelPressedColor = CupertinoDynamicColor.withBrightness( - color: Color(0xFFECECEC), - darkColor: Color(0xFF49494B), -); +// 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 = Color(0xCAE0E0E0); + +const Color _kActionSheetCancelColor = Color(0xFFFFFFFF); +const Color _kActionSheetCancelPressedColor = Color(0xFFECECEC); // 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/. -const Color _kActionSheetBackgroundColor = CupertinoDynamicColor.withBrightness( - color: Color(0xC7F9F9F9), - darkColor: Color(0xC7252525), -); +// Eyeballed from iOS 17 simulator. +const Color _kActionSheetBackgroundColor = Color(0xC8FCFCFC); // 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 = Color(0x851D1D1D); // 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 = Color(0xD4C9C9C9); // 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 @@ -1115,19 +1115,19 @@ class _ActionSheetButtonBackgroundState extends State<_ActionSheetButtonBackgrou 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 +2269,7 @@ class _CupertinoDialogActionsRenderWidget extends MultiChildRenderObjectWidget { : _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 +2283,7 @@ class _CupertinoDialogActionsRenderWidget extends MultiChildRenderObjectWidget { : _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 84aa8e3239722..27283138e773f 100644 --- a/packages/flutter/test/cupertino/action_sheet_test.dart +++ b/packages/flutter/test/cupertino/action_sheet_test.dart @@ -18,6 +18,69 @@ import 'package:flutter_test/flutter_test.dart'; 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: [ + 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'))); + // 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.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'))); + // 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( @@ -1675,6 +1738,50 @@ Widget createAppWithButtonThatLaunchesActionSheet(Widget actionSheet) { ); } +// 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 { + bool _pressedButton = false; + + @override + Widget build(BuildContext context) { + return CupertinoApp( + theme: widget.theme, + home: Builder(builder: (BuildContext context) => + CupertinoPageScaffold( + child: Center( + child: _pressedButton ? Container() : CupertinoButton( + onPressed: () { + setState(() { + _pressedButton = true; + }); + showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return widget.actionSheet; + }, + ); + }, + child: const Text('Go'), + ), + ), + ), + ), + ); + } +} + Widget boilerplate(Widget child) { return Directionality( textDirection: TextDirection.ltr,