Unverified Commit da151c95 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

CupertinoAlertDialog dark mode & CupertinoActionSheet fidelity (#40007)

parent 609a78fd
......@@ -30,23 +30,6 @@ const TextStyle _kActionSheetContentStyle = TextStyle(
textBaseline: TextBaseline.alphabetic,
);
// This decoration is applied to the blurred backdrop to lighten the blurred
// image. Brightening is done to counteract the dark modal barrier that
// appears behind the alert. The overlay blend mode does the brightening.
// The white color doesn't paint any white, it's a placeholder and is going to be
// replaced by the resolved color of _kAlertBlurOverlayColor.
const BoxDecoration _kAlertBlurOverlayDecoration = BoxDecoration(
color: CupertinoColors.white,
backgroundBlendMode: BlendMode.overlay,
);
// Color of the overlay.
// Extracted from https://developer.apple.com/design/resources/.
const Color _kAlertBlurOverlayColor = CupertinoDynamicColor.withBrightness(
color: Color(0x66000000),
darkColor: Color(0x99000000),
);
// 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
......@@ -224,13 +207,9 @@ class CupertinoActionSheet extends StatelessWidget {
Widget build(BuildContext context) {
final List<Widget> children = <Widget>[
Flexible(child: ClipRRect(
borderRadius: BorderRadius.circular(12.0),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: _kBlurAmount, sigmaY: _kBlurAmount),
child: Container(
decoration: _kAlertBlurOverlayDecoration.copyWith(
color: CupertinoDynamicColor.resolve(_kAlertBlurOverlayColor, context),
),
borderRadius: BorderRadius.circular(12.0),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: _kBlurAmount, sigmaY: _kBlurAmount),
child: _CupertinoAlertRenderWidget(
contentSection: Builder(builder: _buildContent),
actionsSection: _buildActions(),
......@@ -238,15 +217,9 @@ class CupertinoActionSheet extends StatelessWidget {
),
),
),
),
if (cancelButton != null) _buildCancelButton(),
];
if (cancelButton != null) {
children.add(
_buildCancelButton(),
);
}
final Orientation orientation = MediaQuery.of(context).orientation;
double actionSheetWidth;
if (orientation == Orientation.portrait) {
......@@ -420,14 +393,14 @@ class _CupertinoAlertRenderWidget extends RenderObjectWidget {
RenderObject createRenderObject(BuildContext context) {
return _RenderCupertinoAlert(
dividerThickness: _kDividerThickness / MediaQuery.of(context).devicePixelRatio,
dividerColor: _kButtonDividerColor,
dividerColor: CupertinoDynamicColor.resolve(_kButtonDividerColor, context),
);
}
@override
void updateRenderObject(BuildContext context, _RenderCupertinoAlert renderObject) {
super.updateRenderObject(context, renderObject);
renderObject.dividerColor = _kButtonDividerColor;
renderObject.dividerColor = CupertinoDynamicColor.resolve(_kButtonDividerColor, context);
}
@override
......@@ -1034,7 +1007,7 @@ class _CupertinoAlertActionsRenderWidget extends MultiChildRenderObjectWidget {
RenderObject createRenderObject(BuildContext context) {
return _RenderCupertinoAlertActions(
dividerThickness: _dividerThickness,
dividerColor: _kButtonDividerColor,
dividerColor: CupertinoDynamicColor.resolve(_kButtonDividerColor, context),
hasCancelButton: _hasCancelButton,
backgroundColor: CupertinoDynamicColor.resolve(_kBackgroundColor, context),
pressedColor: CupertinoDynamicColor.resolve(_kPressedColor, context),
......@@ -1045,7 +1018,7 @@ class _CupertinoAlertActionsRenderWidget extends MultiChildRenderObjectWidget {
void updateRenderObject(BuildContext context, _RenderCupertinoAlertActions renderObject) {
renderObject
..dividerThickness = _dividerThickness
..dividerColor = _kButtonDividerColor
..dividerColor = CupertinoDynamicColor.resolve(_kButtonDividerColor, context)
..hasCancelButton = _hasCancelButton
..backgroundColor = CupertinoDynamicColor.resolve(_kBackgroundColor, context)
..pressedColor = CupertinoDynamicColor.resolve(_kPressedColor, context);
......
......@@ -12,6 +12,8 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/animation.dart' show Curves;
import 'colors.dart';
const double _kBackGestureWidth = 20.0;
const double _kMinFlingVelocity = 1.0; // Screen widths per second.
......@@ -24,7 +26,11 @@ const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds.
const int _kMaxPageBackAnimationTime = 300; // Milliseconds.
// Barrier color for a Cupertino modal barrier.
const Color _kModalBarrierColor = Color(0x6604040F);
// Extracted from https://developer.apple.com/design/resources/.
const Color _kModalBarrierColor = CupertinoDynamicColor.withBrightness(
color: Color(0x33000000),
darkColor: Color(0x7A000000),
);
// The duration of the transition used when a modal popup is shown.
const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335);
......@@ -788,6 +794,7 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> {
_CupertinoModalPopupRoute({
this.builder,
this.barrierLabel,
this.barrierColor,
RouteSettings settings,
}) : super(settings: settings);
......@@ -797,7 +804,7 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> {
final String barrierLabel;
@override
Color get barrierColor => _kModalBarrierColor;
final Color barrierColor;
@override
bool get barrierDismissible => true;
......@@ -879,6 +886,7 @@ Future<T> showCupertinoModalPopup<T>({
_CupertinoModalPopupRoute<T>(
builder: builder,
barrierLabel: 'Dismiss',
barrierColor: CupertinoDynamicColor.resolve(_kModalBarrierColor, context),
),
);
}
......@@ -947,7 +955,7 @@ Future<T> showCupertinoDialog<T>({
return showGeneralDialog(
context: context,
barrierDismissible: false,
barrierColor: _kModalBarrierColor,
barrierColor: CupertinoDynamicColor.resolve(_kModalBarrierColor, context),
// This transition duration was eyeballed comparing with iOS
transitionDuration: const Duration(milliseconds: 250),
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
......
......@@ -7,7 +7,6 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
void main() {
......@@ -113,8 +112,6 @@ void main() {
await tester.tap(find.text('Go'));
await tester.pump();
// Draw the overlay using the light variant.
expect(find.byType(CupertinoActionSheet), paints..rect(color: const Color(0x66000000)));
expect(
actionTextStyle('action').color.value,
const Color.fromARGB(255, 0, 122, 255).value,
......@@ -123,8 +120,6 @@ void main() {
stateSetter(() { brightness = Brightness.dark; });
await tester.pump();
// Draw the overlay using the dark variant.
expect(find.byType(CupertinoActionSheet), paints..rect(color: const Color(0x99000000)));
expect(
actionTextStyle('action').color.value,
const Color.fromARGB(255, 10, 132, 255).value,
......
......@@ -63,6 +63,38 @@ void main() {
expect(widget.style.color.withAlpha(255), CupertinoColors.destructiveRed);
});
testWidgets('Dialog dark theme', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: MediaQuery(
data: const MediaQueryData(platformBrightness: Brightness.dark),
child: CupertinoAlertDialog(
title: const Text('The Title'),
content: const Text('Content'),
actions: <Widget>[
CupertinoDialogAction(child: const Text('Cancel'), isDefaultAction: true, onPressed: () {}),
const CupertinoDialogAction(child: Text('OK')),
],
),
),
),
);
final RichText cancelText = tester.widget<RichText>(
find.descendant(of: find.text('Cancel'), matching: find.byType(RichText)),
);
expect(
cancelText.text.style.color.value,
0xFF0A84FF, // dark elevated color of systemBlue.
);
expect(
find.byType(CupertinoAlertDialog),
paints..rect(color: const Color(0xBF1E1E1E)),
);
});
testWidgets('Has semantic annotations', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(const MaterialApp(home: Material(
......@@ -739,8 +771,8 @@ void main() {
await tester.tap(find.text('Go'));
await tester.pump();
const Color normalButtonBackgroundColor = Color(0xc0ffffff);
const Color pressedButtonBackgroundColor = Color(0x90ffffff);
const Color normalButtonBackgroundColor = Color(0xCCF2F2F2);
const Color pressedButtonBackgroundColor = Color(0xFFE1E1E1);
final RenderBox firstButtonBox = findActionButtonRenderBoxByTitle(tester, 'Option 1');
final RenderBox secondButtonBox = findActionButtonRenderBoxByTitle(tester, 'Option 2');
final RenderBox actionsSectionBox = findScrollableActionsSectionRenderBox(tester);
......
......@@ -743,6 +743,79 @@ void main() {
false,
);
});
testWidgets('ModalPopup overlay dark mode', (WidgetTester tester) async {
StateSetter stateSetter;
Brightness brightness = Brightness.light;
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
stateSetter = setter;
return CupertinoApp(
theme: CupertinoThemeData(brightness: brightness),
home: CupertinoPageScaffold(
child: Builder(builder: (BuildContext context) {
return GestureDetector(
onTap: () async {
await showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => const SizedBox(),
);
},
child: const Text('tap'),
);
}),
),
);
},
),
);
await tester.tap(find.text('tap'));
await tester.pumpAndSettle();
expect(
tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color.value,
0x33000000,
);
stateSetter(() { brightness = Brightness.dark; });
await tester.pump();
// TODO(LongCatIsLooong): The background overlay SHOULD switch to dark color.
expect(
tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color.value,
0x33000000,
);
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: CupertinoPageScaffold(
child: Builder(builder: (BuildContext context) {
return GestureDetector(
onTap: () async {
await showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => const SizedBox(),
);
},
child: const Text('tap'),
);
}),
),
),
);
await tester.tap(find.text('tap'));
await tester.pumpAndSettle();
expect(
tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color.value,
0x7A000000,
);
});
}
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment