Support for elevation based dark theme overlay color in the Material widget (#35560)
Added support for a semi-transparent white overlay color for `Material` widgets to indicate their elevation in a dart theme. A new `ThemeData.applyElevationOverlayColor` flag was added to control this behavior, which is off by default for backwards compatibility reasons.
This commit is contained in:
parent
5501a1c1e7
commit
e17f8d367d
@ -1,11 +1,13 @@
|
|||||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'colors.dart';
|
||||||
import 'constants.dart';
|
import 'constants.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
@ -200,15 +202,23 @@ class Material extends StatefulWidget {
|
|||||||
/// {@template flutter.material.material.elevation}
|
/// {@template flutter.material.material.elevation}
|
||||||
/// The z-coordinate at which to place this material relative to its parent.
|
/// The z-coordinate at which to place this material relative to its parent.
|
||||||
///
|
///
|
||||||
/// This controls the size of the shadow below the material.
|
/// This controls the size of the shadow below the material and the opacity
|
||||||
|
/// of the elevation overlay color if it is applied.
|
||||||
///
|
///
|
||||||
/// If this is non-zero, the contents of the material are clipped, because the
|
/// If this is non-zero, the contents of the material are clipped, because the
|
||||||
/// widget conceptually defines an independent printed piece of material.
|
/// widget conceptually defines an independent printed piece of material.
|
||||||
///
|
///
|
||||||
/// Defaults to 0. Changing this value will cause the shadow to animate over
|
/// Defaults to 0. Changing this value will cause the shadow and the elevation
|
||||||
/// [animationDuration].
|
/// overlay to animate over [animationDuration].
|
||||||
///
|
///
|
||||||
/// The value is non-negative.
|
/// The value is non-negative.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.applyElevationOverlayColor] which controls the whether
|
||||||
|
/// an overlay color will be applied to indicate elevation.
|
||||||
|
/// * [color] which may have an elevation overlay applied.
|
||||||
|
///
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
final double elevation;
|
final double elevation;
|
||||||
|
|
||||||
@ -217,6 +227,11 @@ class Material extends StatefulWidget {
|
|||||||
/// Must be opaque. To create a transparent piece of material, use
|
/// Must be opaque. To create a transparent piece of material, use
|
||||||
/// [MaterialType.transparency].
|
/// [MaterialType.transparency].
|
||||||
///
|
///
|
||||||
|
/// To support dark themes, if the surrounding
|
||||||
|
/// [ThemeData.applyElevationOverlayColor] is [true] and
|
||||||
|
/// this color is [ThemeData.colorScheme.surface] then a semi-transparent
|
||||||
|
/// white will be composited on top this color to indicate the elevation.
|
||||||
|
///
|
||||||
/// By default, the color is derived from the [type] of material.
|
/// By default, the color is derived from the [type] of material.
|
||||||
final Color color;
|
final Color color;
|
||||||
|
|
||||||
@ -252,7 +267,7 @@ class Material extends StatefulWidget {
|
|||||||
final Clip clipBehavior;
|
final Clip clipBehavior;
|
||||||
|
|
||||||
/// Defines the duration of animated changes for [shape], [elevation],
|
/// Defines the duration of animated changes for [shape], [elevation],
|
||||||
/// and [shadowColor].
|
/// [shadowColor] and the elevation overlay if it is applied.
|
||||||
///
|
///
|
||||||
/// The default value is [kThemeChangeDuration].
|
/// The default value is [kThemeChangeDuration].
|
||||||
final Duration animationDuration;
|
final Duration animationDuration;
|
||||||
@ -301,20 +316,43 @@ class Material extends StatefulWidget {
|
|||||||
static const double defaultSplashRadius = 35.0;
|
static const double defaultSplashRadius = 35.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply a semi-transparent white on surface colors to
|
||||||
|
// indicate the level of elevation.
|
||||||
|
Color _elevationOverlayColor(BuildContext context, Color background, double elevation) {
|
||||||
|
final ThemeData theme = Theme.of(context);
|
||||||
|
if (elevation > 0.0 &&
|
||||||
|
theme.applyElevationOverlayColor &&
|
||||||
|
background == theme.colorScheme.surface) {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
final Color overlay = Colors.white.withOpacity(opacity);
|
||||||
|
return Color.alphaBlend(overlay, background);
|
||||||
|
}
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
||||||
final GlobalKey _inkFeatureRenderer = GlobalKey(debugLabel: 'ink renderer');
|
final GlobalKey _inkFeatureRenderer = GlobalKey(debugLabel: 'ink renderer');
|
||||||
|
|
||||||
Color _getBackgroundColor(BuildContext context) {
|
Color _getBackgroundColor(BuildContext context) {
|
||||||
if (widget.color != null)
|
final ThemeData theme = Theme.of(context);
|
||||||
return widget.color;
|
Color color = widget.color;
|
||||||
switch (widget.type) {
|
if (color == null) {
|
||||||
case MaterialType.canvas:
|
switch (widget.type) {
|
||||||
return Theme.of(context).canvasColor;
|
case MaterialType.canvas:
|
||||||
case MaterialType.card:
|
color = theme.canvasColor;
|
||||||
return Theme.of(context).cardColor;
|
break;
|
||||||
default:
|
case MaterialType.card:
|
||||||
return null;
|
color = theme.cardColor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -366,7 +404,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
|||||||
clipBehavior: widget.clipBehavior,
|
clipBehavior: widget.clipBehavior,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.zero,
|
||||||
elevation: widget.elevation,
|
elevation: widget.elevation,
|
||||||
color: backgroundColor,
|
color: _elevationOverlayColor(context, backgroundColor, widget.elevation),
|
||||||
shadowColor: widget.shadowColor,
|
shadowColor: widget.shadowColor,
|
||||||
animateColor: false,
|
animateColor: false,
|
||||||
child: contents,
|
child: contents,
|
||||||
@ -711,6 +749,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ShapeBorder shape = _border.evaluate(animation);
|
final ShapeBorder shape = _border.evaluate(animation);
|
||||||
|
final double elevation = _elevation.evaluate(animation);
|
||||||
return PhysicalShape(
|
return PhysicalShape(
|
||||||
child: _ShapeBorderPaint(
|
child: _ShapeBorderPaint(
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
@ -722,8 +761,8 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
|
|||||||
textDirection: Directionality.of(context),
|
textDirection: Directionality.of(context),
|
||||||
),
|
),
|
||||||
clipBehavior: widget.clipBehavior,
|
clipBehavior: widget.clipBehavior,
|
||||||
elevation: _elevation.evaluate(animation),
|
elevation: elevation,
|
||||||
color: widget.color,
|
color: _elevationOverlayColor(context, widget.color, elevation),
|
||||||
shadowColor: _shadowColor.evaluate(animation),
|
shadowColor: _shadowColor.evaluate(animation),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
ChipThemeData chipTheme,
|
ChipThemeData chipTheme,
|
||||||
TargetPlatform platform,
|
TargetPlatform platform,
|
||||||
MaterialTapTargetSize materialTapTargetSize,
|
MaterialTapTargetSize materialTapTargetSize,
|
||||||
|
bool applyElevationOverlayColor,
|
||||||
PageTransitionsTheme pageTransitionsTheme,
|
PageTransitionsTheme pageTransitionsTheme,
|
||||||
AppBarTheme appBarTheme,
|
AppBarTheme appBarTheme,
|
||||||
BottomAppBarTheme bottomAppBarTheme,
|
BottomAppBarTheme bottomAppBarTheme,
|
||||||
@ -228,6 +229,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
final TextTheme defaultAccentTextTheme = accentIsDark ? typography.white : typography.black;
|
final TextTheme defaultAccentTextTheme = accentIsDark ? typography.white : typography.black;
|
||||||
accentTextTheme = defaultAccentTextTheme.merge(accentTextTheme);
|
accentTextTheme = defaultAccentTextTheme.merge(accentTextTheme);
|
||||||
materialTapTargetSize ??= MaterialTapTargetSize.padded;
|
materialTapTargetSize ??= MaterialTapTargetSize.padded;
|
||||||
|
applyElevationOverlayColor ??= false;
|
||||||
if (fontFamily != null) {
|
if (fontFamily != null) {
|
||||||
textTheme = textTheme.apply(fontFamily: fontFamily);
|
textTheme = textTheme.apply(fontFamily: fontFamily);
|
||||||
primaryTextTheme = primaryTextTheme.apply(fontFamily: fontFamily);
|
primaryTextTheme = primaryTextTheme.apply(fontFamily: fontFamily);
|
||||||
@ -315,6 +317,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
chipTheme: chipTheme,
|
chipTheme: chipTheme,
|
||||||
platform: platform,
|
platform: platform,
|
||||||
materialTapTargetSize: materialTapTargetSize,
|
materialTapTargetSize: materialTapTargetSize,
|
||||||
|
applyElevationOverlayColor: applyElevationOverlayColor,
|
||||||
pageTransitionsTheme: pageTransitionsTheme,
|
pageTransitionsTheme: pageTransitionsTheme,
|
||||||
appBarTheme: appBarTheme,
|
appBarTheme: appBarTheme,
|
||||||
bottomAppBarTheme: bottomAppBarTheme,
|
bottomAppBarTheme: bottomAppBarTheme,
|
||||||
@ -384,6 +387,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
@required this.chipTheme,
|
@required this.chipTheme,
|
||||||
@required this.platform,
|
@required this.platform,
|
||||||
@required this.materialTapTargetSize,
|
@required this.materialTapTargetSize,
|
||||||
|
@required this.applyElevationOverlayColor,
|
||||||
@required this.pageTransitionsTheme,
|
@required this.pageTransitionsTheme,
|
||||||
@required this.appBarTheme,
|
@required this.appBarTheme,
|
||||||
@required this.bottomAppBarTheme,
|
@required this.bottomAppBarTheme,
|
||||||
@ -679,6 +683,38 @@ class ThemeData extends Diagnosticable {
|
|||||||
/// Configures the hit test size of certain Material widgets.
|
/// Configures the hit test size of certain Material widgets.
|
||||||
final MaterialTapTargetSize materialTapTargetSize;
|
final MaterialTapTargetSize materialTapTargetSize;
|
||||||
|
|
||||||
|
/// Apply a semi-transparent white overlay on Material surfaces to indicate
|
||||||
|
/// elevation for dark themes.
|
||||||
|
///
|
||||||
|
/// Material drop shadows can be difficult to see in a dark theme, so the
|
||||||
|
/// elevation of a surface should be portrayed with an "overlay" in addition
|
||||||
|
/// to the shadow. As the elevation of the component increases, the white
|
||||||
|
/// overlay increases in opacity. [applyElevationOverlayColor] turns the
|
||||||
|
/// application of this overlay on or off.
|
||||||
|
///
|
||||||
|
/// If [true] a semi-transparent white overlay will be applied to the color
|
||||||
|
/// of [Material] widgets when their [Material.color] is [colorScheme.surface].
|
||||||
|
/// The level of transparency is based on [Material.elevation] as per the
|
||||||
|
/// Material Dark theme specification.
|
||||||
|
///
|
||||||
|
/// If [false] the surface color will be used unmodified.
|
||||||
|
///
|
||||||
|
/// Defaults to [false].
|
||||||
|
///
|
||||||
|
/// Note: this setting is here to maintain backwards compatibility with
|
||||||
|
/// apps that were built before the Material Dark theme specification
|
||||||
|
/// was published. New apps should set this to [true] for any themes
|
||||||
|
/// where [brightness] is [Brightness.dark].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [Material.elevation], which effects how transparent the white overlay is.
|
||||||
|
/// * [Material.color], the white color overlay will only be applied of the
|
||||||
|
/// material's color is [colorScheme.surface].
|
||||||
|
/// * <https://material.io/design/color/dark-theme.html>, which specifies how
|
||||||
|
/// the overlay should be applied.
|
||||||
|
final bool applyElevationOverlayColor;
|
||||||
|
|
||||||
/// Default [MaterialPageRoute] transitions per [TargetPlatform].
|
/// Default [MaterialPageRoute] transitions per [TargetPlatform].
|
||||||
///
|
///
|
||||||
/// [MaterialPageRoute.buildTransitions] delegates to a [PageTransitionsBuilder]
|
/// [MaterialPageRoute.buildTransitions] delegates to a [PageTransitionsBuilder]
|
||||||
@ -779,6 +815,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
ChipThemeData chipTheme,
|
ChipThemeData chipTheme,
|
||||||
TargetPlatform platform,
|
TargetPlatform platform,
|
||||||
MaterialTapTargetSize materialTapTargetSize,
|
MaterialTapTargetSize materialTapTargetSize,
|
||||||
|
bool applyElevationOverlayColor,
|
||||||
PageTransitionsTheme pageTransitionsTheme,
|
PageTransitionsTheme pageTransitionsTheme,
|
||||||
AppBarTheme appBarTheme,
|
AppBarTheme appBarTheme,
|
||||||
BottomAppBarTheme bottomAppBarTheme,
|
BottomAppBarTheme bottomAppBarTheme,
|
||||||
@ -837,6 +874,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
chipTheme: chipTheme ?? this.chipTheme,
|
chipTheme: chipTheme ?? this.chipTheme,
|
||||||
platform: platform ?? this.platform,
|
platform: platform ?? this.platform,
|
||||||
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
|
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
|
||||||
|
applyElevationOverlayColor: applyElevationOverlayColor ?? this.applyElevationOverlayColor,
|
||||||
pageTransitionsTheme: pageTransitionsTheme ?? this.pageTransitionsTheme,
|
pageTransitionsTheme: pageTransitionsTheme ?? this.pageTransitionsTheme,
|
||||||
appBarTheme: appBarTheme ?? this.appBarTheme,
|
appBarTheme: appBarTheme ?? this.appBarTheme,
|
||||||
bottomAppBarTheme: bottomAppBarTheme ?? this.bottomAppBarTheme,
|
bottomAppBarTheme: bottomAppBarTheme ?? this.bottomAppBarTheme,
|
||||||
@ -973,6 +1011,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t),
|
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t),
|
||||||
platform: t < 0.5 ? a.platform : b.platform,
|
platform: t < 0.5 ? a.platform : b.platform,
|
||||||
materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize,
|
materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize,
|
||||||
|
applyElevationOverlayColor: t < 0.5 ? a.applyElevationOverlayColor : b.applyElevationOverlayColor,
|
||||||
pageTransitionsTheme: t < 0.5 ? a.pageTransitionsTheme : b.pageTransitionsTheme,
|
pageTransitionsTheme: t < 0.5 ? a.pageTransitionsTheme : b.pageTransitionsTheme,
|
||||||
appBarTheme: AppBarTheme.lerp(a.appBarTheme, b.appBarTheme, t),
|
appBarTheme: AppBarTheme.lerp(a.appBarTheme, b.appBarTheme, t),
|
||||||
bottomAppBarTheme: BottomAppBarTheme.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t),
|
bottomAppBarTheme: BottomAppBarTheme.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t),
|
||||||
@ -1037,6 +1076,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
(otherData.chipTheme == chipTheme) &&
|
(otherData.chipTheme == chipTheme) &&
|
||||||
(otherData.platform == platform) &&
|
(otherData.platform == platform) &&
|
||||||
(otherData.materialTapTargetSize == materialTapTargetSize) &&
|
(otherData.materialTapTargetSize == materialTapTargetSize) &&
|
||||||
|
(otherData.applyElevationOverlayColor == applyElevationOverlayColor) &&
|
||||||
(otherData.pageTransitionsTheme == pageTransitionsTheme) &&
|
(otherData.pageTransitionsTheme == pageTransitionsTheme) &&
|
||||||
(otherData.appBarTheme == appBarTheme) &&
|
(otherData.appBarTheme == appBarTheme) &&
|
||||||
(otherData.bottomAppBarTheme == bottomAppBarTheme) &&
|
(otherData.bottomAppBarTheme == bottomAppBarTheme) &&
|
||||||
@ -1100,6 +1140,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
chipTheme,
|
chipTheme,
|
||||||
platform,
|
platform,
|
||||||
materialTapTargetSize,
|
materialTapTargetSize,
|
||||||
|
applyElevationOverlayColor,
|
||||||
pageTransitionsTheme,
|
pageTransitionsTheme,
|
||||||
appBarTheme,
|
appBarTheme,
|
||||||
bottomAppBarTheme,
|
bottomAppBarTheme,
|
||||||
@ -1160,6 +1201,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
properties.add(DiagnosticsProperty<CardTheme>('cardTheme', cardTheme));
|
properties.add(DiagnosticsProperty<CardTheme>('cardTheme', cardTheme));
|
||||||
properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme));
|
properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme));
|
||||||
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize));
|
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize));
|
||||||
|
properties.add(DiagnosticsProperty<bool>('applyElevationOverlayColor', applyElevationOverlayColor));
|
||||||
properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme));
|
properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme));
|
||||||
properties.add(DiagnosticsProperty<AppBarTheme>('appBarTheme', appBarTheme, defaultValue: defaultData.appBarTheme));
|
properties.add(DiagnosticsProperty<AppBarTheme>('appBarTheme', appBarTheme, defaultValue: defaultData.appBarTheme));
|
||||||
properties.add(DiagnosticsProperty<BottomAppBarTheme>('bottomAppBarTheme', bottomAppBarTheme, defaultValue: defaultData.bottomAppBarTheme));
|
properties.add(DiagnosticsProperty<BottomAppBarTheme>('bottomAppBarTheme', bottomAppBarTheme, defaultValue: defaultData.bottomAppBarTheme));
|
||||||
|
@ -21,12 +21,14 @@ class NotifyMaterial extends StatelessWidget {
|
|||||||
Widget buildMaterial({
|
Widget buildMaterial({
|
||||||
double elevation = 0.0,
|
double elevation = 0.0,
|
||||||
Color shadowColor = const Color(0xFF00FF00),
|
Color shadowColor = const Color(0xFF00FF00),
|
||||||
|
Color color = const Color(0xFF0000FF),
|
||||||
}) {
|
}) {
|
||||||
return Center(
|
return Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
width: 100.0,
|
width: 100.0,
|
||||||
child: Material(
|
child: Material(
|
||||||
|
color: color,
|
||||||
shadowColor: shadowColor,
|
shadowColor: shadowColor,
|
||||||
elevation: elevation,
|
elevation: elevation,
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
@ -35,7 +37,7 @@ Widget buildMaterial({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderPhysicalShape getShadow(WidgetTester tester) {
|
RenderPhysicalShape getModel(WidgetTester tester) {
|
||||||
return tester.renderObject(find.byType(PhysicalShape));
|
return tester.renderObject(find.byType(PhysicalShape));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +57,12 @@ class PaintRecorder extends CustomPainter {
|
|||||||
bool shouldRepaint(PaintRecorder oldDelegate) => false;
|
bool shouldRepaint(PaintRecorder oldDelegate) => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ElevationColor {
|
||||||
|
const ElevationColor(this.elevation, this.color);
|
||||||
|
final double elevation;
|
||||||
|
final Color color;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('default Material debugFillProperties', (WidgetTester tester) async {
|
testWidgets('default Material debugFillProperties', (WidgetTester tester) async {
|
||||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
@ -163,23 +171,23 @@ void main() {
|
|||||||
// a kThemeChangeDuration time interval.
|
// a kThemeChangeDuration time interval.
|
||||||
|
|
||||||
await tester.pumpWidget(buildMaterial(elevation: 0.0));
|
await tester.pumpWidget(buildMaterial(elevation: 0.0));
|
||||||
final RenderPhysicalShape modelA = getShadow(tester);
|
final RenderPhysicalShape modelA = getModel(tester);
|
||||||
expect(modelA.elevation, equals(0.0));
|
expect(modelA.elevation, equals(0.0));
|
||||||
|
|
||||||
await tester.pumpWidget(buildMaterial(elevation: 9.0));
|
await tester.pumpWidget(buildMaterial(elevation: 9.0));
|
||||||
final RenderPhysicalShape modelB = getShadow(tester);
|
final RenderPhysicalShape modelB = getModel(tester);
|
||||||
expect(modelB.elevation, equals(0.0));
|
expect(modelB.elevation, equals(0.0));
|
||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 1));
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
final RenderPhysicalShape modelC = getShadow(tester);
|
final RenderPhysicalShape modelC = getModel(tester);
|
||||||
expect(modelC.elevation, closeTo(0.0, 0.001));
|
expect(modelC.elevation, closeTo(0.0, 0.001));
|
||||||
|
|
||||||
await tester.pump(kThemeChangeDuration ~/ 2);
|
await tester.pump(kThemeChangeDuration ~/ 2);
|
||||||
final RenderPhysicalShape modelD = getShadow(tester);
|
final RenderPhysicalShape modelD = getModel(tester);
|
||||||
expect(modelD.elevation, isNot(closeTo(0.0, 0.001)));
|
expect(modelD.elevation, isNot(closeTo(0.0, 0.001)));
|
||||||
|
|
||||||
await tester.pump(kThemeChangeDuration);
|
await tester.pump(kThemeChangeDuration);
|
||||||
final RenderPhysicalShape modelE = getShadow(tester);
|
final RenderPhysicalShape modelE = getModel(tester);
|
||||||
expect(modelE.elevation, equals(9.0));
|
expect(modelE.elevation, equals(9.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,26 +196,96 @@ void main() {
|
|||||||
// a kThemeChangeDuration time interval.
|
// a kThemeChangeDuration time interval.
|
||||||
|
|
||||||
await tester.pumpWidget(buildMaterial(shadowColor: const Color(0xFF00FF00)));
|
await tester.pumpWidget(buildMaterial(shadowColor: const Color(0xFF00FF00)));
|
||||||
final RenderPhysicalShape modelA = getShadow(tester);
|
final RenderPhysicalShape modelA = getModel(tester);
|
||||||
expect(modelA.shadowColor, equals(const Color(0xFF00FF00)));
|
expect(modelA.shadowColor, equals(const Color(0xFF00FF00)));
|
||||||
|
|
||||||
await tester.pumpWidget(buildMaterial(shadowColor: const Color(0xFFFF0000)));
|
await tester.pumpWidget(buildMaterial(shadowColor: const Color(0xFFFF0000)));
|
||||||
final RenderPhysicalShape modelB = getShadow(tester);
|
final RenderPhysicalShape modelB = getModel(tester);
|
||||||
expect(modelB.shadowColor, equals(const Color(0xFF00FF00)));
|
expect(modelB.shadowColor, equals(const Color(0xFF00FF00)));
|
||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 1));
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
final RenderPhysicalShape modelC = getShadow(tester);
|
final RenderPhysicalShape modelC = getModel(tester);
|
||||||
expect(modelC.shadowColor, within<Color>(distance: 1, from: const Color(0xFF00FF00)));
|
expect(modelC.shadowColor, within<Color>(distance: 1, from: const Color(0xFF00FF00)));
|
||||||
|
|
||||||
await tester.pump(kThemeChangeDuration ~/ 2);
|
await tester.pump(kThemeChangeDuration ~/ 2);
|
||||||
final RenderPhysicalShape modelD = getShadow(tester);
|
final RenderPhysicalShape modelD = getModel(tester);
|
||||||
expect(modelD.shadowColor, isNot(within<Color>(distance: 1, from: const Color(0xFF00FF00))));
|
expect(modelD.shadowColor, isNot(within<Color>(distance: 1, from: const Color(0xFF00FF00))));
|
||||||
|
|
||||||
await tester.pump(kThemeChangeDuration);
|
await tester.pump(kThemeChangeDuration);
|
||||||
final RenderPhysicalShape modelE = getShadow(tester);
|
final RenderPhysicalShape modelE = getModel(tester);
|
||||||
expect(modelE.shadowColor, equals(const Color(0xFFFF0000)));
|
expect(modelE.shadowColor, equals(const Color(0xFFFF0000)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Elevation Overlay', () {
|
||||||
|
|
||||||
|
testWidgets('applyElevationOverlayColor set to false does not change surface color', (WidgetTester tester) async {
|
||||||
|
const Color surfaceColor = Color(0xFF121212);
|
||||||
|
await tester.pumpWidget(Theme(
|
||||||
|
data: ThemeData(
|
||||||
|
applyElevationOverlayColor: false,
|
||||||
|
colorScheme: const ColorScheme.dark().copyWith(surface: surfaceColor),
|
||||||
|
),
|
||||||
|
child: buildMaterial(color: surfaceColor, elevation: 8.0))
|
||||||
|
);
|
||||||
|
final RenderPhysicalShape model = getModel(tester);
|
||||||
|
expect(model.color, equals(surfaceColor));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('applyElevationOverlayColor set to true overlays a transparent white on surface color', (WidgetTester tester) async {
|
||||||
|
// The colors we should get with a base surface color of 0xFF121212 for
|
||||||
|
// a given elevation
|
||||||
|
const List<ElevationColor> elevationColors = <ElevationColor>[
|
||||||
|
ElevationColor(0.0, Color(0xFF121212)),
|
||||||
|
ElevationColor(1.0, Color(0xFF1E1E1E)),
|
||||||
|
ElevationColor(2.0, Color(0xFF222222)),
|
||||||
|
ElevationColor(3.0, Color(0xFF252525)),
|
||||||
|
ElevationColor(4.0, Color(0xFF282828)),
|
||||||
|
ElevationColor(6.0, Color(0xFF2B2B2B)),
|
||||||
|
ElevationColor(8.0, Color(0xFF2D2D2D)),
|
||||||
|
ElevationColor(12.0, Color(0xFF323232)),
|
||||||
|
ElevationColor(16.0, Color(0xFF353535)),
|
||||||
|
ElevationColor(24.0, Color(0xFF393939)),
|
||||||
|
];
|
||||||
|
const Color surfaceColor = Color(0xFF121212);
|
||||||
|
|
||||||
|
for (ElevationColor test in elevationColors) {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Theme(
|
||||||
|
data: ThemeData(
|
||||||
|
applyElevationOverlayColor: true,
|
||||||
|
colorScheme: const ColorScheme.dark().copyWith(surface: surfaceColor),
|
||||||
|
),
|
||||||
|
child: buildMaterial(
|
||||||
|
color: surfaceColor,
|
||||||
|
elevation: test.elevation,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle(); // wait for the elevation animation to finish
|
||||||
|
final RenderPhysicalShape model = getModel(tester);
|
||||||
|
expect(model.color, equals(test.color));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('overlay will only apply to materials using colorScheme.surface', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Theme(
|
||||||
|
data: ThemeData(
|
||||||
|
applyElevationOverlayColor: true,
|
||||||
|
colorScheme: const ColorScheme.dark().copyWith(surface: const Color(0xFF121212)),
|
||||||
|
),
|
||||||
|
child: buildMaterial(
|
||||||
|
color: Colors.cyan,
|
||||||
|
elevation: 8.0
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final RenderPhysicalShape model = getModel(tester);
|
||||||
|
expect(model.color, equals(Colors.cyan));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
group('Transparency clipping', () {
|
group('Transparency clipping', () {
|
||||||
testWidgets('No clip by default', (WidgetTester tester) async {
|
testWidgets('No clip by default', (WidgetTester tester) async {
|
||||||
final GlobalKey materialKey = GlobalKey();
|
final GlobalKey materialKey = GlobalKey();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user