Unverified Commit 33ed6a35 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Fixing elevation issues with Material 3 (#110624)

* Added support for surfaceTintColor and shadowColor to the Dialog widgets.
* Updated the defaults for Material.shadowColor and Material.surfaceTint to allow turning off the features with a transparent color.
* Added support for shadowColor and surfaceTintColor for Drawer widget.
parent 1b4b480a
......@@ -31,10 +31,10 @@ class _${blockName}DefaultsM3 extends ChipThemeData {
Color? get backgroundColor => ${componentColor("$tokenGroup$variant.container")};
@override
Color? get shadowColor => ${color("$tokenGroup.container.shadow-color")};
Color? get shadowColor => ${colorOrTransparent("$tokenGroup.container.shadow-color")};
@override
@override Color? get surfaceTintColor => ${color("$tokenGroup.container.surface-tint-layer.color")};
Color? get surfaceTintColor => ${colorOrTransparent("$tokenGroup.container.surface-tint-layer.color")};
@override
Color? get selectedColor => ${componentColor("$tokenGroup$variant.selected.container")};
......
......@@ -34,7 +34,10 @@ class _${blockName}DefaultsM3 extends AppBarTheme {
Color? get foregroundColor => ${color('md.comp.top-app-bar.small.headline.color')};
@override
Color? get surfaceTintColor => ${componentColor('md.comp.top-app-bar.small.container.surface-tint-layer')};
Color? get shadowColor => ${colorOrTransparent('md.comp.top-app-bar.small.container.shadow-color')};
@override
Color? get surfaceTintColor => ${colorOrTransparent('md.comp.top-app-bar.small.container.surface-tint-layer.color')};
@override
IconThemeData? get iconTheme => IconThemeData(
......
......@@ -24,7 +24,7 @@ class ButtonTemplate extends TokenTemplate {
}
return '''
ButtonStyleButton.allOrNull<Color>(Colors.transparent)''';
const MaterialStatePropertyAll<Color>(Colors.transparent)''';
}
String _elevation() {
......@@ -49,7 +49,15 @@ class ButtonTemplate extends TokenTemplate {
}
return '''
ButtonStyleButton.allOrNull<double>(0.0)''';
const MaterialStatePropertyAll<double>(0.0)''';
}
String _elevationColor(String token) {
if (tokens.containsKey(token)) {
return 'MaterialStatePropertyAll<Color>(${color(token)})';
} else {
return 'const MaterialStatePropertyAll<Color>(Colors.transparent)';
}
}
@override
......@@ -96,34 +104,30 @@ class _${blockName}DefaultsM3 extends ButtonStyle {
return null;
});
${tokens.containsKey("$tokenGroup.container.shadow-color") ? '''
@override
MaterialStateProperty<Color>? get shadowColor =>
ButtonStyleButton.allOrNull<Color>(${color("$tokenGroup.container.shadow-color")});''' : '''
// No default shadow color'''}
${_elevationColor("$tokenGroup.container.shadow-color")};
${tokens.containsKey("$tokenGroup.container.surface-tint-layer.color") ? '''
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
ButtonStyleButton.allOrNull<Color>(${color("$tokenGroup.container.surface-tint-layer.color")});''' : '''
// No default surface tint color'''}
${_elevationColor("$tokenGroup.container.surface-tint-layer.color")};
@override
MaterialStateProperty<double>? get elevation =>${_elevation()};
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, ${tokens["$tokenGroup.container.height"]}));
const MaterialStatePropertyAll<Size>(Size(64.0, ${tokens["$tokenGroup.container.height"]}));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
${tokens.containsKey("$tokenGroup.outline.color") ? '''
@override
......@@ -138,7 +142,7 @@ ${tokens.containsKey("$tokenGroup.outline.color") ? '''
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(${shape("$tokenGroup.container")});
const MaterialStatePropertyAll<OutlinedBorder>(${shape("$tokenGroup.container", '')});
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -25,10 +25,10 @@ class _${blockName}DefaultsM3 extends CardTheme {
Color? get color => ${componentColor("md.comp.elevated-card.container")};
@override
Color? get shadowColor => ${color("md.comp.elevated-card.container.shadow-color")};
Color? get shadowColor => ${colorOrTransparent("md.comp.elevated-card.container.shadow-color")};
@override
Color? get surfaceTintColor => ${color("md.comp.elevated-card.container.surface-tint-layer.color")};
Color? get surfaceTintColor => ${colorOrTransparent("md.comp.elevated-card.container.surface-tint-layer.color")};
}
''';
}
......@@ -27,9 +27,14 @@ class _${blockName}DefaultsM3 extends DialogTheme {
@override
Color? get iconColor => _colors.secondary;
// TODO(darrenaustin): overlay should be handled by Material widget: https://github.com/flutter/flutter/issues/9160
@override
Color? get backgroundColor => ElevationOverlay.colorWithOverlay(${componentColor("md.comp.dialog.container")}, _colors.primary, ${elevation("md.comp.dialog.container")});
Color? get backgroundColor => ${componentColor("md.comp.dialog.container")};
@override
Color? get shadowColor => ${colorOrTransparent("md.comp.dialog.container.shadow-color")};
@override
Color? get surfaceTintColor => ${colorOrTransparent("md.comp.dialog.container.surface-tint-layer.color")};
@override
TextStyle? get titleTextStyle => ${textStyle("md.comp.dialog.headline")};
......
......@@ -31,10 +31,10 @@ class _${blockName}DefaultsM3 extends ChipThemeData {
Color? get backgroundColor => ${componentColor("$tokenGroup$variant.container")};
@override
Color? get shadowColor => ${color("$tokenGroup.container.shadow-color")};
Color? get shadowColor => ${colorOrTransparent("$tokenGroup.container.shadow-color")};
@override
@override Color? get surfaceTintColor => ${color("$tokenGroup.container.surface-tint-layer.color")};
Color? get surfaceTintColor => ${colorOrTransparent("$tokenGroup.container.surface-tint-layer.color")};
@override
Color? get selectedColor => isEnabled
......
......@@ -9,6 +9,15 @@ class IconButtonTemplate extends TokenTemplate {
super.colorSchemePrefix = '_colors.',
});
String _elevationColor(String token) {
if (tokens.containsKey(token)) {
return 'MaterialStatePropertyAll<Color>(${color(token)})';
} else {
return 'const MaterialStatePropertyAll<Color>(Colors.transparent)';
}
}
@override
String generate() => '''
class _${blockName}DefaultsM3 extends ButtonStyle {
......@@ -26,7 +35,7 @@ class _${blockName}DefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
const MaterialStatePropertyAll<Color?>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
......@@ -66,37 +75,41 @@ class _${blockName}DefaultsM3 extends ButtonStyle {
return null;
});
// No default shadow color
@override
MaterialStateProperty<double>? get elevation =>
const MaterialStatePropertyAll<double>(0.0);
// No default surface tint color
@override
MaterialStateProperty<Color>? get shadowColor =>
${_elevationColor("md.comp.icon-button.container.shadow-color")};
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
MaterialStateProperty<Color>? get surfaceTintColor =>
${_elevationColor("md.comp.icon-button.container.surface-tint-layer.color")};
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(const EdgeInsets.all(8.0));
const MaterialStatePropertyAll<EdgeInsetsGeometry>(EdgeInsets.all(8.0));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(${tokens["md.comp.icon-button.state-layer.size"]}, ${tokens["md.comp.icon-button.state-layer.size"]}));
const MaterialStatePropertyAll<Size>(Size(${tokens["md.comp.icon-button.state-layer.size"]}, ${tokens["md.comp.icon-button.state-layer.size"]}));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
@override
MaterialStateProperty<double>? get iconSize =>
ButtonStyleButton.allOrNull<double>(${tokens["md.comp.icon-button.icon.size"]});
const MaterialStatePropertyAll<double>(${tokens["md.comp.icon-button.icon.size"]});
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(${shape("md.comp.icon-button.state-layer")});
const MaterialStatePropertyAll<OutlinedBorder>(${shape("md.comp.icon-button.state-layer", "")});
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -30,10 +30,10 @@ class _${blockName}DefaultsM3 extends ChipThemeData {
Color? get backgroundColor => ${componentColor("$tokenGroup$variant.container")};
@override
Color? get shadowColor => ${color("$tokenGroup.container.shadow-color")};
Color? get shadowColor => ${colorOrTransparent("$tokenGroup.container.shadow-color")};
@override
@override Color? get surfaceTintColor => ${color("$tokenGroup.container.surface-tint-layer.color")};
Color? get surfaceTintColor => ${colorOrTransparent("$tokenGroup.container.surface-tint-layer.color")};
@override
Color? get selectedColor => ${componentColor("$tokenGroup$variant.selected.container")};
......
......@@ -26,7 +26,9 @@ class _${blockName}DefaultsM3 extends NavigationBarThemeData {
@override Color? get backgroundColor => ${componentColor("md.comp.navigation-bar.container")};
@override Color? get surfaceTintColor => ${color("md.comp.navigation-bar.container.surface-tint-layer.color")};
@override Color? get shadowColor => ${colorOrTransparent("md.comp.navigation-bar.container.shadow-color")};
@override Color? get surfaceTintColor => ${colorOrTransparent("md.comp.navigation-bar.container.surface-tint-layer.color")};
@override MaterialStateProperty<IconThemeData?>? get iconTheme {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
......
......@@ -97,16 +97,28 @@ abstract class TokenTemplate {
/// If there is a value for the given token, this will return
/// the value prepended with [colorSchemePrefix].
///
/// Otherwise it will return 'null'.
/// Otherwise it will return [defaultValue].
///
/// See also:
/// * [componentColor], that provides support for an optional opacity.
String color(String colorToken) {
String color(String colorToken, [String defaultValue = 'null']) {
return tokens.containsKey(colorToken)
? '$colorSchemePrefix${tokens[colorToken]}'
: 'null';
: defaultValue;
}
/// Generate a [ColorScheme] color name for the given token or a transparent
/// color if there is no value for the token.
///
/// If there is a value for the given token, this will return
/// the value prepended with [colorSchemePrefix].
///
/// Otherwise it will return 'Colors.transparent'.
///
/// See also:
/// * [componentColor], that provides support for an optional opacity.
String? colorOrTransparent(String token) => color(token, 'Colors.transparent');
/// Generate a [ColorScheme] color name for the given component's color
/// with opacity if available.
///
......@@ -154,18 +166,18 @@ abstract class TokenTemplate {
/// Currently supports family:
/// - "SHAPE_FAMILY_ROUNDED_CORNERS" which maps to [RoundedRectangleBorder].
/// - "SHAPE_FAMILY_CIRCULAR" which maps to a [StadiumBorder].
String shape(String componentToken) {
String shape(String componentToken, [String prefix = 'const ']) {
final Map<String, dynamic> shape = tokens[tokens['$componentToken.shape']!]! as Map<String, dynamic>;
switch (shape['family']) {
case 'SHAPE_FAMILY_ROUNDED_CORNERS':
return 'const RoundedRectangleBorder(borderRadius: '
return '${prefix}RoundedRectangleBorder(borderRadius: '
'BorderRadius.only('
'topLeft: Radius.circular(${shape['topLeft']}), '
'topRight: Radius.circular(${shape['topRight']}), '
'bottomLeft: Radius.circular(${shape['bottomLeft']}), '
'bottomRight: Radius.circular(${shape['bottomRight']})))';
case 'SHAPE_FAMILY_CIRCULAR':
return 'const StadiumBorder()';
return '${prefix}StadiumBorder()';
}
print('Unsupported shape family type: ${shape['family']} for $componentToken');
return '';
......
......@@ -16,7 +16,7 @@ void main() {
expect(find.widgetWithText(AppBar, 'AppBar Demo'), findsOneWidget);
Material appbarMaterial = _getAppBarMaterial(tester);
expect(appbarMaterial.shadowColor, null);
expect(appbarMaterial.shadowColor, Colors.transparent);
expect(appbarMaterial.elevation, 0);
await tester.drag(find.text('Item 4'), _kOffset, touchSlopY: 0, warnIfMissed: false);
......
......@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import 'chip.dart';
import 'chip_theme.dart';
import 'colors.dart';
import 'debug.dart';
import 'theme.dart';
import 'theme_data.dart';
......@@ -196,10 +197,10 @@ class _ActionChipDefaultsM3 extends ChipThemeData {
Color? get backgroundColor => null;
@override
Color? get shadowColor => null;
Color? get shadowColor => Colors.transparent;
@override
@override Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color? get selectedColor => null;
......
......@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
import 'app_bar_theme.dart';
import 'back_button.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
import 'debug.dart';
import 'flexible_space_bar.dart';
......@@ -2370,6 +2371,9 @@ class _AppBarDefaultsM3 extends AppBarTheme {
@override
Color? get foregroundColor => _colors.onSurface;
@override
Color? get shadowColor => Colors.transparent;
@override
Color? get surfaceTintColor => _colors.surfaceTint;
......
......@@ -213,7 +213,7 @@ class _FilterChipDefaultsM3 extends ChipThemeData {
Color? get shadowColor => Theme.of(context).colorScheme.shadow;
@override
@override Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color? get selectedColor => isEnabled
......
......@@ -11,7 +11,6 @@ import 'color_scheme.dart';
import 'colors.dart';
import 'debug.dart';
import 'dialog_theme.dart';
import 'elevation_overlay.dart';
import 'ink_well.dart';
import 'material.dart';
import 'material_localizations.dart';
......@@ -46,6 +45,8 @@ class Dialog extends StatelessWidget {
super.key,
this.backgroundColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.insetAnimationDuration = const Duration(milliseconds: 100),
this.insetAnimationCurve = Curves.decelerate,
this.insetPadding = _defaultInsetPadding,
......@@ -53,7 +54,8 @@ class Dialog extends StatelessWidget {
this.shape,
this.alignment,
this.child,
}) : assert(clipBehavior != null);
}) : assert(clipBehavior != null),
assert(elevation == null || elevation >= 0.0);
/// {@template flutter.material.dialog.backgroundColor}
/// The background color of the surface of this [Dialog].
......@@ -67,12 +69,63 @@ class Dialog extends StatelessWidget {
/// {@template flutter.material.dialog.elevation}
/// The z-coordinate of this [Dialog].
///
/// If null then [DialogTheme.elevation] is used, and if that's null then the
/// dialog's elevation is 24.0.
/// Controls how far above the parent the dialog will appear. Elevation is
/// represented by a drop shadow if [shadowColor] is non null,
/// and a surface tint overlay on the background color if [surfaceTintColor] is
/// non null.
///
/// If null then [DialogTheme.elevation] is used, and if that is null then
/// the elevation will match the Material Design specification for Dialogs.
///
/// See also:
/// * [Material.elevation], which describes how [elevation] effects the
/// drop shadow or surface tint overlay.
/// * [shadowColor], color of the drop shadow used to indicate the elevation.
/// * [surfaceTintColor], color of an overlay on top of the background
/// color used to indicate the elevation.
/// * <https://m3.material.io/components/dialogs/overview>, the Material
/// Design specification for dialogs.
/// {@endtemplate}
/// {@macro flutter.material.material.elevation}
final double? elevation;
/// {@template flutter.material.dialog.shadowColor}
/// The color used to paint a drop shadow under the dialog's [Material],
/// which reflects the dialog's [elevation].
///
/// If null and [ThemeData.useMaterial3] is true then no drop shadow will
/// be rendered.
///
/// If null and [ThemeData.useMaterial3] is false then it will default to
/// [ThemeData.shadowColor].
///
/// See also:
/// * [Material.shadowColor], which describes how the drop shadow is painted.
/// * [elevation], which affects how the drop shadow is painted.
/// * [surfaceTintColor], which can be used to indicate elevation through
/// tinting the background color.
/// {@endtemplate}
final Color? shadowColor;
/// {@template flutter.material.dialog.surfaceTintColor}
/// The color used as a surface tint overlay on the dialog's background color,
/// which reflects the dialog's [elevation].
///
/// If [ThemeData.useMaterial3] is false property has no effect.
///
/// If null and [ThemeData.useMaterial3] is true then [ThemeData]'s
/// [ColorScheme.surfaceTint] will be used.
///
/// To disable this feature, set [surfaceTintColor] to [Colors.transparent].
///
/// See also:
/// * [Material.surfaceTintColor], which describes how the surface tint will
/// be applied to the background color of the dialog.
/// * [elevation], which affects the opacity of the surface tint.
/// * [shadowColor], which can be used to indicate elevation through
/// a drop shadow.
/// {@endtemplate}
final Color? surfaceTintColor;
/// {@template flutter.material.dialog.insetAnimationDuration}
/// The duration of the animation to show when the system keyboard intrudes
/// into the space that the dialog is placed in.
......@@ -155,6 +208,8 @@ class Dialog extends StatelessWidget {
child: Material(
color: backgroundColor ?? dialogTheme.backgroundColor ?? Theme.of(context).dialogBackgroundColor,
elevation: elevation ?? dialogTheme.elevation ?? defaults.elevation!,
shadowColor: shadowColor ?? dialogTheme.shadowColor ?? defaults.shadowColor,
surfaceTintColor: surfaceTintColor ?? dialogTheme.surfaceTintColor ?? defaults.surfaceTintColor,
shape: shape ?? dialogTheme.shape ?? defaults.shape!,
type: MaterialType.card,
clipBehavior: clipBehavior,
......@@ -280,6 +335,8 @@ class AlertDialog extends StatelessWidget {
this.buttonPadding,
this.backgroundColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.semanticLabel,
this.insetPadding = _defaultInsetPadding,
this.clipBehavior = Clip.none,
......@@ -478,9 +535,14 @@ class AlertDialog extends StatelessWidget {
final Color? backgroundColor;
/// {@macro flutter.material.dialog.elevation}
/// {@macro flutter.material.material.elevation}
final double? elevation;
/// {@macro flutter.material.dialog.shadowColor}
final Color? shadowColor;
/// {@macro flutter.material.dialog.surfaceTintColor}
final Color? surfaceTintColor;
/// The semantic label of the dialog used by accessibility frameworks to
/// announce screen transitions when the dialog is opened and closed.
///
......@@ -695,6 +757,8 @@ class AlertDialog extends StatelessWidget {
return Dialog(
backgroundColor: backgroundColor,
elevation: elevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
insetPadding: insetPadding,
clipBehavior: clipBehavior,
shape: shape,
......@@ -860,6 +924,8 @@ class SimpleDialog extends StatelessWidget {
this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),
this.backgroundColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.semanticLabel,
this.insetPadding = _defaultInsetPadding,
this.clipBehavior = Clip.none,
......@@ -915,9 +981,14 @@ class SimpleDialog extends StatelessWidget {
final Color? backgroundColor;
/// {@macro flutter.material.dialog.elevation}
/// {@macro flutter.material.material.elevation}
final double? elevation;
/// {@macro flutter.material.dialog.shadowColor}
final Color? shadowColor;
/// {@macro flutter.material.dialog.surfaceTintColor}
final Color? surfaceTintColor;
/// The semantic label of the dialog used by accessibility frameworks to
/// announce screen transitions when the dialog is opened and closed.
///
......@@ -1031,6 +1102,8 @@ class SimpleDialog extends StatelessWidget {
return Dialog(
backgroundColor: backgroundColor,
elevation: elevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
insetPadding: insetPadding,
clipBehavior: clipBehavior,
shape: shape,
......@@ -1296,6 +1369,9 @@ class _DialogDefaultsM2 extends DialogTheme {
@override
Color? get backgroundColor => Theme.of(context).dialogBackgroundColor;
@override
Color? get shadowColor => Theme.of(context).shadowColor;
@override
TextStyle? get titleTextStyle => _textTheme.titleLarge;
......@@ -1330,9 +1406,14 @@ class _DialogDefaultsM3 extends DialogTheme {
@override
Color? get iconColor => _colors.secondary;
// TODO(darrenaustin): overlay should be handled by Material widget: https://github.com/flutter/flutter/issues/9160
@override
Color? get backgroundColor => ElevationOverlay.colorWithOverlay(_colors.surface, _colors.primary, 6.0);
Color? get backgroundColor => _colors.surface;
@override
Color? get shadowColor => Colors.transparent;
@override
Color? get surfaceTintColor => _colors.surfaceTint;
@override
TextStyle? get titleTextStyle => _textTheme.headlineSmall;
......
......@@ -30,6 +30,8 @@ class DialogTheme with Diagnosticable {
const DialogTheme({
this.backgroundColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.shape,
this.alignment,
this.iconColor,
......@@ -44,6 +46,12 @@ class DialogTheme with Diagnosticable {
/// Overrides the default value for [Dialog.elevation].
final double? elevation;
/// Overrides the default value for [Dialog.shadowColor].
final Color? shadowColor;
/// Overrides the default value for [Dialog.surfaceTintColor].
final Color? surfaceTintColor;
/// Overrides the default value for [Dialog.shape].
final ShapeBorder? shape;
......@@ -69,6 +77,8 @@ class DialogTheme with Diagnosticable {
DialogTheme copyWith({
Color? backgroundColor,
double? elevation,
Color? shadowColor,
Color? surfaceTintColor,
ShapeBorder? shape,
AlignmentGeometry? alignment,
Color? iconColor,
......@@ -79,6 +89,8 @@ class DialogTheme with Diagnosticable {
return DialogTheme(
backgroundColor: backgroundColor ?? this.backgroundColor,
elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
shape: shape ?? this.shape,
alignment: alignment ?? this.alignment,
iconColor: iconColor ?? this.iconColor,
......@@ -103,6 +115,8 @@ class DialogTheme with Diagnosticable {
return DialogTheme(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
alignment: AlignmentGeometry.lerp(a?.alignment, b?.alignment, t),
iconColor: Color.lerp(a?.iconColor, b?.iconColor, t),
......@@ -126,6 +140,8 @@ class DialogTheme with Diagnosticable {
return other is DialogTheme
&& other.backgroundColor == backgroundColor
&& other.elevation == elevation
&& other.shadowColor == shadowColor
&& other.surfaceTintColor == surfaceTintColor
&& other.shape == shape
&& other.alignment == alignment
&& other.iconColor == iconColor
......@@ -139,6 +155,8 @@ class DialogTheme with Diagnosticable {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor));
properties.add(DoubleProperty('elevation', elevation));
properties.add(ColorProperty('shadowColor', shadowColor));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, defaultValue: null));
properties.add(ColorProperty('iconColor', iconColor));
......
......@@ -147,6 +147,8 @@ class Drawer extends StatelessWidget {
super.key,
this.backgroundColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.shape,
this.width,
this.child,
......@@ -168,6 +170,40 @@ class Drawer extends StatelessWidget {
/// is also null, then it defaults to 16.0.
final double? elevation;
/// The color used to paint a drop shadow under the drawer's [Material],
/// which reflects the drawer's [elevation].
///
/// If null and [ThemeData.useMaterial3] is true then no drop shadow will
/// be rendered.
///
/// If null and [ThemeData.useMaterial3] is false then it will default to
/// [ThemeData.shadowColor].
///
/// See also:
/// * [Material.shadowColor], which describes how the drop shadow is painted.
/// * [elevation], which affects how the drop shadow is painted.
/// * [surfaceTintColor], which can be used to indicate elevation through
/// tinting the background color.
final Color? shadowColor;
/// The color used as a surface tint overlay on the drawer's background color,
/// which reflects the drawer's [elevation].
///
/// If [ThemeData.useMaterial3] is false property has no effect.
///
/// If null and [ThemeData.useMaterial3] is true then [ThemeData]'s
/// [ColorScheme.surfaceTint] will be used.
///
/// To disable this feature, set [surfaceTintColor] to [Colors.transparent].
///
/// See also:
/// * [Material.surfaceTintColor], which describes how the surface tint will
/// be applied to the background color of the drawer.
/// * [elevation], which affects the opacity of the surface tint.
/// * [shadowColor], which can be used to indicate elevation through
/// a drop shadow.
final Color? surfaceTintColor;
/// The shape of the drawer.
///
/// Defines the drawer's [Material.shape].
......@@ -189,7 +225,7 @@ class Drawer extends StatelessWidget {
/// {@macro flutter.widgets.ProxyWidget.child}
final Widget? child;
/// The semantic label of the dialog used by accessibility frameworks to
/// The semantic label of the drawer used by accessibility frameworks to
/// announce screen transitions when the drawer is opened and closed.
///
/// If this label is not provided, it will default to
......@@ -216,6 +252,7 @@ class Drawer extends StatelessWidget {
case TargetPlatform.windows:
label = semanticLabel ?? MaterialLocalizations.of(context).drawerLabel;
}
final bool useMaterial3 = Theme.of(context).useMaterial3;
return Semantics(
scopesRoute: true,
namesRoute: true,
......@@ -226,6 +263,8 @@ class Drawer extends StatelessWidget {
child: Material(
color: backgroundColor ?? drawerTheme.backgroundColor,
elevation: elevation ?? drawerTheme.elevation ?? 16.0,
shadowColor: shadowColor ?? drawerTheme.shadowColor ?? (useMaterial3 ? Colors.transparent : Theme.of(context).shadowColor),
surfaceTintColor: surfaceTintColor ?? drawerTheme.surfaceTintColor ?? (useMaterial3 ? Theme.of(context).colorScheme.surfaceTint : null),
shape: shape ?? drawerTheme.shape,
child: child,
),
......
......@@ -38,6 +38,8 @@ class DrawerThemeData with Diagnosticable {
this.backgroundColor,
this.scrimColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.shape,
this.width,
});
......@@ -51,6 +53,12 @@ class DrawerThemeData with Diagnosticable {
/// Overrides the default value of [Drawer.elevation].
final double? elevation;
/// Overrides the default value for [Drawer.shadowColor].
final Color? shadowColor;
/// Overrides the default value for [Drawer.surfaceTintColor].
final Color? surfaceTintColor;
/// Overrides the default value of [Drawer.shape].
final ShapeBorder? shape;
......@@ -63,6 +71,8 @@ class DrawerThemeData with Diagnosticable {
Color? backgroundColor,
Color? scrimColor,
double? elevation,
Color? shadowColor,
Color? surfaceTintColor,
ShapeBorder? shape,
double? width,
}) {
......@@ -70,6 +80,8 @@ class DrawerThemeData with Diagnosticable {
backgroundColor: backgroundColor ?? this.backgroundColor,
scrimColor: scrimColor ?? this.scrimColor,
elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
shape: shape ?? this.shape,
width: width ?? this.width,
);
......@@ -89,6 +101,8 @@ class DrawerThemeData with Diagnosticable {
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
scrimColor: Color.lerp(a?.scrimColor, b?.scrimColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
width: lerpDouble(a?.width, b?.width, t),
);
......@@ -99,6 +113,8 @@ class DrawerThemeData with Diagnosticable {
backgroundColor,
scrimColor,
elevation,
shadowColor,
surfaceTintColor,
shape,
width,
);
......@@ -115,6 +131,8 @@ class DrawerThemeData with Diagnosticable {
&& other.backgroundColor == backgroundColor
&& other.scrimColor == scrimColor
&& other.elevation == elevation
&& other.shadowColor == shadowColor
&& other.surfaceTintColor == surfaceTintColor
&& other.shape == shape
&& other.width == width;
}
......@@ -125,6 +143,8 @@ class DrawerThemeData with Diagnosticable {
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(ColorProperty('scrimColor', scrimColor, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DoubleProperty('width', width, defaultValue: null));
}
......
......@@ -582,11 +582,11 @@ class _ElevatedButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color>? get shadowColor =>
ButtonStyleButton.allOrNull<Color>(_colors.shadow);
MaterialStatePropertyAll<Color>(_colors.shadow);
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
ButtonStyleButton.allOrNull<Color>(_colors.surfaceTint);
MaterialStatePropertyAll<Color>(_colors.surfaceTint);
@override
MaterialStateProperty<double>? get elevation =>
......@@ -608,23 +608,23 @@ class _ElevatedButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -6,6 +6,7 @@ import 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'theme.dart';
/// A utility class for dealing with the overlay color needed
......@@ -23,11 +24,12 @@ class ElevationOverlay {
/// elevated. The amount of opacity will vary with the elevation as described
/// in: https://m3.material.io/styles/color/the-color-system/color-roles.
///
/// If [surfaceTint] is not null then the returned color will be the given
/// [color] with the [surfaceTint] of the appropriate opacity applies to it.
/// Otherwise it will just return [color] unmodified.
/// If [surfaceTint] is not null and not completely transparent ([Color.alpha]
/// is 0), then the returned color will be the given [color] with the
/// [surfaceTint] of the appropriate opacity applied to it. Otherwise it will
/// just return [color] unmodified.
static Color applySurfaceTint(Color color, Color? surfaceTint, double elevation) {
if (surfaceTint != null) {
if (surfaceTint != null && surfaceTint != Colors.transparent) {
return Color.alphaBlend(surfaceTint.withOpacity(_surfaceTintOpacityForElevation(elevation)), color);
}
return color;
......
......@@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
import 'button_style.dart';
import 'button_style_button.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
import 'filled_button_theme.dart';
import 'ink_well.dart';
......@@ -551,9 +552,11 @@ class _FilledButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color>? get shadowColor =>
ButtonStyleButton.allOrNull<Color>(_colors.shadow);
MaterialStatePropertyAll<Color>(_colors.shadow);
// No default surface tint color
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
......@@ -575,23 +578,23 @@ class _FilledButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......@@ -673,9 +676,11 @@ class _FilledTonalButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color>? get shadowColor =>
ButtonStyleButton.allOrNull<Color>(_colors.shadow);
MaterialStatePropertyAll<Color>(_colors.shadow);
// No default surface tint color
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
......@@ -697,23 +702,23 @@ class _FilledTonalButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -222,7 +222,7 @@ class _FilterChipDefaultsM3 extends ChipThemeData {
Color? get shadowColor => Theme.of(context).colorScheme.shadow;
@override
@override Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color? get selectedColor => isEnabled
......
......@@ -979,7 +979,7 @@ class _IconButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
const MaterialStatePropertyAll<Color?>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
......@@ -1019,37 +1019,41 @@ class _IconButtonDefaultsM3 extends ButtonStyle {
return null;
});
// No default shadow color
@override
MaterialStateProperty<double>? get elevation =>
const MaterialStatePropertyAll<double>(0.0);
// No default surface tint color
@override
MaterialStateProperty<Color>? get shadowColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(const EdgeInsets.all(8.0));
const MaterialStatePropertyAll<EdgeInsetsGeometry>(EdgeInsets.all(8.0));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(40.0, 40.0));
const MaterialStatePropertyAll<Size>(Size(40.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
@override
MaterialStateProperty<double>? get iconSize =>
ButtonStyleButton.allOrNull<double>(24.0);
const MaterialStatePropertyAll<double>(24.0);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import 'chip.dart';
import 'chip_theme.dart';
import 'colors.dart';
import 'debug.dart';
import 'icons.dart';
import 'theme.dart';
......@@ -268,10 +269,10 @@ class _InputChipDefaultsM3 extends ChipThemeData {
Color? get backgroundColor => null;
@override
Color? get shadowColor => null;
Color? get shadowColor => Colors.transparent;
@override
@override Color? get surfaceTintColor => null;
Color? get surfaceTintColor => Colors.transparent;
@override
Color? get selectedColor => Theme.of(context).colorScheme.secondaryContainer;
......
......@@ -258,12 +258,12 @@ class Material extends StatefulWidget {
/// The color to paint the shadow below the material.
///
/// When [ThemeData.useMaterial3] is true, and this is null, then no drop
/// shadow will be rendered for this material. If it is non-null, then this
/// color will be used to render a drop shadow below the material.
/// If null and [ThemeData.useMaterial3] is true then [ThemeData]'s
/// [ColorScheme.shadow] will be used. If [ThemeData.useMaterial3] is false
/// then [ThemeData.shadowColor] will be used.
///
/// When [ThemeData.useMaterial3] is false, and this is null, then
/// [ThemeData.shadowColor] is used, which defaults to fully opaque black.
/// To remove the drop shadow when [elevation] is greater than 0, set
/// [shadowColor] to [Colors.transparent].
///
/// See also:
/// * [ThemeData.useMaterial3], which determines the default value for this
......@@ -282,9 +282,9 @@ class Material extends StatefulWidget {
///
/// If [ThemeData.useMaterial3] is false, then this property is not used.
///
/// If [ThemeData.useMaterial3] is true and [surfaceTintColor] is not null,
/// then it will be used to overlay the base [color] with an opacity based
/// on the [elevation].
/// If [ThemeData.useMaterial3] is true and [surfaceTintColor] is not null and
/// not [Colors.transparent], then it will be used to overlay the base [color]
/// with an opacity based on the [elevation].
///
/// Otherwise, no surface tint will be applied.
///
......@@ -404,7 +404,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final Color? backgroundColor = _getBackgroundColor(context);
final Color? modelShadowColor = widget.shadowColor ?? (theme.useMaterial3 ? null : theme.shadowColor);
final Color modelShadowColor = widget.shadowColor ?? (theme.useMaterial3 ? theme.colorScheme.shadow : theme.shadowColor);
// If no shadow color is specified, use 0 for elevation in the model so a drop shadow won't be painted.
final double modelElevation = modelShadowColor != null ? widget.elevation : 0;
assert(
......@@ -458,7 +458,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
clipBehavior: widget.clipBehavior,
elevation: modelElevation,
color: color,
shadowColor: modelShadowColor ?? const Color(0x00000000),
shadowColor: modelShadowColor,
animateColor: false,
child: contents,
);
......
......@@ -67,8 +67,9 @@ class NavigationBar extends StatelessWidget {
required this.destinations,
this.onDestinationSelected,
this.backgroundColor,
this.surfaceTintColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.height,
this.labelBehavior,
}) : assert(destinations != null && destinations.length >= 2),
......@@ -109,6 +110,22 @@ class NavigationBar extends StatelessWidget {
/// and [ColorScheme.onSurface] using an [ElevationOverlay].
final Color? backgroundColor;
/// The elevation of the [NavigationBar] itself.
///
/// If null, [NavigationBarThemeData.elevation] is used. If that
/// is also null, then if [ThemeData.useMaterial3] is true then it will
/// be 3.0 otherwise 0.0.
final double? elevation;
/// The color used for the drop shadow to indicate elevation.
///
/// If null, [NavigationBarThemeData.shadowColor] is used. If that
/// is also null, the default value is [Colors.transparent] which
/// indicates that no drop shadow will be displayed.
///
/// See [Material.shadowColor] for more details on drop shadows.
final Color? shadowColor;
/// The color used as an overlay on [backgroundColor] to indicate elevation.
///
/// If null, [NavigationBarThemeData.surfaceTintColor] is used. If that
......@@ -118,13 +135,6 @@ class NavigationBar extends StatelessWidget {
/// overlay is applied.
final Color? surfaceTintColor;
/// The elevation of the [NavigationBar] itself.
///
/// If null, [NavigationBarThemeData.elevation] is used. If that
/// is also null, then if [ThemeData.useMaterial3] is true then it will
/// be 3.0 otherwise 0.0.
final double? elevation;
/// The height of the [NavigationBar] itself.
///
/// If this is used in [Scaffold.bottomNavigationBar] and the scaffold is
......@@ -171,8 +181,9 @@ class NavigationBar extends StatelessWidget {
color: backgroundColor
?? navigationBarTheme.backgroundColor
?? defaults.backgroundColor!,
surfaceTintColor: surfaceTintColor ?? navigationBarTheme.surfaceTintColor ?? defaults.surfaceTintColor,
elevation: elevation ?? navigationBarTheme.elevation ?? defaults.elevation!,
shadowColor: shadowColor ?? navigationBarTheme.shadowColor ?? defaults.shadowColor,
surfaceTintColor: surfaceTintColor ?? navigationBarTheme.surfaceTintColor ?? defaults.surfaceTintColor,
child: SafeArea(
child: SizedBox(
height: effectiveHeight,
......@@ -1250,6 +1261,8 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
@override Color? get backgroundColor => _colors.surface;
@override Color? get shadowColor => Colors.transparent;
@override Color? get surfaceTintColor => _colors.surfaceTint;
@override MaterialStateProperty<IconThemeData?>? get iconTheme {
......
......@@ -44,8 +44,9 @@ class NavigationBarThemeData with Diagnosticable {
const NavigationBarThemeData({
this.height,
this.backgroundColor,
this.surfaceTintColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.indicatorColor,
this.indicatorShape,
this.labelTextStyle,
......@@ -59,12 +60,15 @@ class NavigationBarThemeData with Diagnosticable {
/// Overrides the default value of [NavigationBar.backgroundColor].
final Color? backgroundColor;
/// Overrides the default value of [NavigationBar.surfaceTintColor].
final Color? surfaceTintColor;
/// Overrides the default value of [NavigationBar.elevation].
final double? elevation;
/// Overrides the default value of [NavigationBar.shadowColor].
final Color? shadowColor;
/// Overrides the default value of [NavigationBar.surfaceTintColor].
final Color? surfaceTintColor;
/// Overrides the default value of [NavigationBar]'s selection indicator.
final Color? indicatorColor;
......@@ -92,8 +96,9 @@ class NavigationBarThemeData with Diagnosticable {
NavigationBarThemeData copyWith({
double? height,
Color? backgroundColor,
Color? surfaceTintColor,
double? elevation,
Color? shadowColor,
Color? surfaceTintColor,
Color? indicatorColor,
ShapeBorder? indicatorShape,
MaterialStateProperty<TextStyle?>? labelTextStyle,
......@@ -103,8 +108,9 @@ class NavigationBarThemeData with Diagnosticable {
return NavigationBarThemeData(
height: height ?? this.height,
backgroundColor: backgroundColor ?? this.backgroundColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
indicatorColor: indicatorColor ?? this.indicatorColor,
indicatorShape: indicatorShape ?? this.indicatorShape,
labelTextStyle: labelTextStyle ?? this.labelTextStyle,
......@@ -126,8 +132,9 @@ class NavigationBarThemeData with Diagnosticable {
return NavigationBarThemeData(
height: lerpDouble(a?.height, b?.height, t),
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t),
labelTextStyle: MaterialStateProperty.lerp<TextStyle?>(a?.labelTextStyle, b?.labelTextStyle, t, TextStyle.lerp),
......@@ -140,8 +147,9 @@ class NavigationBarThemeData with Diagnosticable {
int get hashCode => Object.hash(
height,
backgroundColor,
surfaceTintColor,
elevation,
shadowColor,
surfaceTintColor,
indicatorColor,
indicatorShape,
labelTextStyle,
......@@ -160,8 +168,9 @@ class NavigationBarThemeData with Diagnosticable {
return other is NavigationBarThemeData
&& other.height == height
&& other.backgroundColor == backgroundColor
&& other.surfaceTintColor == surfaceTintColor
&& other.elevation == elevation
&& other.shadowColor == shadowColor
&& other.surfaceTintColor == surfaceTintColor
&& other.indicatorColor == indicatorColor
&& other.indicatorShape == indicatorShape
&& other.labelTextStyle == labelTextStyle
......@@ -174,8 +183,9 @@ class NavigationBarThemeData with Diagnosticable {
super.debugFillProperties(properties);
properties.add(DoubleProperty('height', height, defaultValue: null));
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('indicatorShape', indicatorShape, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('labelTextStyle', labelTextStyle, defaultValue: null));
......
......@@ -470,7 +470,7 @@ class _OutlinedButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
......@@ -496,27 +496,31 @@ class _OutlinedButtonDefaultsM3 extends ButtonStyle {
return null;
});
// No default shadow color
@override
MaterialStateProperty<Color>? get shadowColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
// No default surface tint color
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
const MaterialStatePropertyAll<double>(0.0);
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
@override
MaterialStateProperty<BorderSide>? get side =>
......@@ -529,7 +533,7 @@ class _OutlinedButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -514,7 +514,7 @@ class _TextButtonDefaultsM3 extends ButtonStyle {
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
......@@ -540,33 +540,37 @@ class _TextButtonDefaultsM3 extends ButtonStyle {
return null;
});
// No default shadow color
@override
MaterialStateProperty<Color>? get shadowColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
// No default surface tint color
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
const MaterialStatePropertyAll<double>(0.0);
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
const MaterialStatePropertyAll<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
......
......@@ -140,9 +140,13 @@ void main() {
testWidgets('Custom dialog elevation', (WidgetTester tester) async {
const double customElevation = 12.0;
const Color shadowColor = Color(0xFF000001);
const Color surfaceTintColor = Color(0xFF000002);
const AlertDialog dialog = AlertDialog(
actions: <Widget>[ ],
elevation: customElevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
);
await tester.pumpWidget(_buildAppWithDialog(dialog));
......@@ -151,6 +155,8 @@ void main() {
final Material materialWidget = _getMaterialFromDialog(tester);
expect(materialWidget.elevation, customElevation);
expect(materialWidget.shadowColor, shadowColor);
expect(materialWidget.surfaceTintColor, surfaceTintColor);
});
testWidgets('Custom Title Text Style', (WidgetTester tester) async {
......
......@@ -51,6 +51,8 @@ void main() {
const DialogTheme(
backgroundColor: Color(0xff123456),
elevation: 8.0,
shadowColor: Color(0xff000001),
surfaceTintColor: Color(0xff000002),
alignment: Alignment.bottomLeft,
iconColor: Color(0xff654321),
titleTextStyle: TextStyle(color: Color(0xffffffff)),
......@@ -63,6 +65,8 @@ void main() {
expect(description, <String>[
'backgroundColor: Color(0xff123456)',
'elevation: 8.0',
'shadowColor: Color(0xff000001)',
'surfaceTintColor: Color(0xff000002)',
'alignment: Alignment.bottomLeft',
'iconColor: Color(0xff654321)',
'titleTextStyle: TextStyle(inherit: true, color: Color(0xffffffff))',
......@@ -89,11 +93,19 @@ void main() {
testWidgets('Custom dialog elevation', (WidgetTester tester) async {
const double customElevation = 12.0;
const Color shadowColor = Color(0xFF000001);
const Color surfaceTintColor = Color(0xFF000002);
const AlertDialog dialog = AlertDialog(
title: Text('Title'),
actions: <Widget>[ ],
);
final ThemeData theme = ThemeData(dialogTheme: const DialogTheme(elevation: customElevation));
final ThemeData theme = ThemeData(
dialogTheme: const DialogTheme(
elevation: customElevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
),
);
await tester.pumpWidget(
_appWithDialog(tester, dialog, theme: theme),
......@@ -103,6 +115,8 @@ void main() {
final Material materialWidget = _getMaterialFromDialog(tester);
expect(materialWidget.elevation, customElevation);
expect(materialWidget.shadowColor, shadowColor);
expect(materialWidget.surfaceTintColor, surfaceTintColor);
});
testWidgets('Custom dialog shape', (WidgetTester tester) async {
......
......@@ -30,6 +30,8 @@ void main() {
backgroundColor: Color(0x00000099),
scrimColor: Color(0x00000098),
elevation: 5.0,
shadowColor: Color(0x00000097),
surfaceTintColor: Color(0x00000096),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))),
width: 200.0,
).debugFillProperties(builder);
......@@ -43,6 +45,8 @@ void main() {
'backgroundColor: Color(0x00000099)',
'scrimColor: Color(0x00000098)',
'elevation: 5.0',
'shadowColor: Color(0x00000097)',
'surfaceTintColor: Color(0x00000096)',
'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))',
'width: 200.0',
]);
......@@ -50,6 +54,7 @@ void main() {
testWidgets('Default values are used when no Drawer or DrawerThemeData properties are specified', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final bool useMaterial3 = ThemeData().useMaterial3;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
......@@ -63,6 +68,8 @@ void main() {
expect(_drawerMaterial(tester).color, null);
expect(_drawerMaterial(tester).elevation, 16.0);
expect(_drawerMaterial(tester).shadowColor, useMaterial3 ? Colors.transparent : ThemeData().shadowColor);
expect(_drawerMaterial(tester).surfaceTintColor, useMaterial3 ? ThemeData().colorScheme.surfaceTint : null);
expect(_drawerMaterial(tester).shape, null);
expect(_scrim(tester).color, Colors.black54);
expect(_drawerRenderBox(tester).size.width, 304.0);
......@@ -72,6 +79,8 @@ void main() {
const Color backgroundColor = Color(0x00000001);
const Color scrimColor = Color(0x00000002);
const double elevation = 7.0;
const Color shadowColor = Color(0x00000003);
const Color surfaceTintColor = Color(0x00000004);
const RoundedRectangleBorder shape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
const double width = 200.0;
......@@ -83,6 +92,8 @@ void main() {
backgroundColor: backgroundColor,
scrimColor: scrimColor,
elevation: elevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
shape: shape,
width: width,
),
......@@ -98,6 +109,8 @@ void main() {
expect(_drawerMaterial(tester).color, backgroundColor);
expect(_drawerMaterial(tester).elevation, elevation);
expect(_drawerMaterial(tester).shadowColor, shadowColor);
expect(_drawerMaterial(tester).surfaceTintColor, surfaceTintColor);
expect(_drawerMaterial(tester).shape, shape);
expect(_scrim(tester).color, scrimColor);
expect(_drawerRenderBox(tester).size.width, width);
......@@ -107,6 +120,8 @@ void main() {
const Color backgroundColor = Color(0x00000001);
const Color scrimColor = Color(0x00000002);
const double elevation = 7.0;
const Color shadowColor = Color(0x00000003);
const Color surfaceTintColor = Color(0x00000004);
const RoundedRectangleBorder shape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
const double width = 200.0;
......@@ -115,8 +130,8 @@ void main() {
MaterialApp(
theme: ThemeData(
drawerTheme: const DrawerThemeData(
backgroundColor: Color(0x00000003),
scrimColor: Color(0x00000004),
backgroundColor: Color(0x00000005),
scrimColor: Color(0x00000006),
elevation: 13.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(29.0))),
width: 400.0,
......@@ -128,6 +143,8 @@ void main() {
drawer: const Drawer(
backgroundColor: backgroundColor,
elevation: elevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
shape: shape,
width: width,
),
......@@ -139,6 +156,8 @@ void main() {
expect(_drawerMaterial(tester).color, backgroundColor);
expect(_drawerMaterial(tester).elevation, elevation);
expect(_drawerMaterial(tester).shadowColor, shadowColor);
expect(_drawerMaterial(tester).surfaceTintColor, surfaceTintColor);
expect(_drawerMaterial(tester).shape, shape);
expect(_scrim(tester).color, scrimColor);
expect(_drawerRenderBox(tester).size.width, width);
......@@ -148,6 +167,8 @@ void main() {
const Color backgroundColor = Color(0x00000001);
const Color scrimColor = Color(0x00000002);
const double elevation = 7.0;
const Color shadowColor = Color(0x00000003);
const Color surfaceTintColor = Color(0x00000004);
const RoundedRectangleBorder shape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
const double width = 200.0;
......@@ -156,9 +177,11 @@ void main() {
MaterialApp(
theme: ThemeData(
drawerTheme: const DrawerThemeData(
backgroundColor: Color(0x00000003),
scrimColor: Color(0x00000004),
backgroundColor: Color(0x00000005),
scrimColor: Color(0x00000006),
elevation: 13.0,
shadowColor: Color(0x00000007),
surfaceTintColor: Color(0x00000007),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(29.0))),
width: 400.0
),
......@@ -168,6 +191,8 @@ void main() {
backgroundColor: backgroundColor,
scrimColor: scrimColor,
elevation: elevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
shape: shape,
width: width,
),
......@@ -183,6 +208,8 @@ void main() {
expect(_drawerMaterial(tester).color, backgroundColor);
expect(_drawerMaterial(tester).elevation, elevation);
expect(_drawerMaterial(tester).shadowColor, shadowColor);
expect(_drawerMaterial(tester).surfaceTintColor, surfaceTintColor);
expect(_drawerMaterial(tester).shape, shape);
expect(_scrim(tester).color, scrimColor);
expect(_drawerRenderBox(tester).size.width, width);
......
......@@ -1078,7 +1078,7 @@ void main() {
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shadowColor, null);
expect(material.shadowColor, Colors.transparent);
expect(material.shape, const StadiumBorder());
expect(material.textStyle, null);
expect(material.type, MaterialType.button);
......@@ -1102,7 +1102,7 @@ void main() {
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shadowColor, null);
expect(material.shadowColor, Colors.transparent);
expect(material.shape, const StadiumBorder());
expect(material.textStyle, null);
expect(material.type, MaterialType.button);
......@@ -1127,7 +1127,7 @@ void main() {
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shadowColor, null);
expect(material.shadowColor, Colors.transparent);
expect(material.shape, const StadiumBorder());
expect(material.textStyle, null);
expect(material.type, MaterialType.button);
......
......@@ -32,7 +32,7 @@ void main() {
expect(material.borderRadius, null);
expect(material.color, Colors.transparent);
expect(material.elevation, 0.0);
expect(material.shadowColor, null);
expect(material.shadowColor, Colors.transparent);
expect(material.shape, const StadiumBorder());
expect(material.textStyle, null);
expect(material.type, MaterialType.button);
......@@ -221,12 +221,12 @@ void main() {
await tester.pumpWidget(buildFrame());
Material material = tester.widget<Material>(buttonMaterialFinder);
expect(material.shadowColor, null); //default
expect(material.shadowColor, Colors.transparent); //default
await tester.pumpWidget(buildFrame(overallShadowColor: shadowColor));
await tester.pumpAndSettle(); // theme animation
material = tester.widget<Material>(buttonMaterialFinder);
expect(material.shadowColor, null);
expect(material.shadowColor, Colors.transparent);
await tester.pumpWidget(buildFrame(themeShadowColor: shadowColor));
await tester.pumpAndSettle(); // theme animation
......
......@@ -189,6 +189,58 @@ void main() {
expect(log, isEmpty);
});
testWidgets('Shadow color defaults', (WidgetTester tester) async {
Widget buildWithShadow(Color? shadowColor) {
return Center(
child: SizedBox(
height: 100.0,
width: 100.0,
child: Material(
shadowColor: shadowColor,
elevation: 10,
shape: const CircleBorder(),
),
)
);
}
// Default M2 shadow color
await tester.pumpWidget(
Theme(
data: ThemeData(
useMaterial3: false,
),
child: buildWithShadow(null),
)
);
await tester.pumpAndSettle();
expect(getModel(tester).shadowColor, ThemeData().shadowColor);
// Default M3 shadow color
await tester.pumpWidget(
Theme(
data: ThemeData(
useMaterial3: true,
),
child: buildWithShadow(null),
)
);
await tester.pumpAndSettle();
expect(getModel(tester).shadowColor, ThemeData().colorScheme.shadow);
// Drop shadow can be turned off with a transparent color.
await tester.pumpWidget(
Theme(
data: ThemeData(
useMaterial3: true,
),
child: buildWithShadow(Colors.transparent),
)
);
await tester.pumpAndSettle();
expect(getModel(tester).shadowColor, Colors.transparent);
});
testWidgets('Shadows animate smoothly', (WidgetTester tester) async {
// This code verifies that the PhysicalModel's elevation animates over
// a kThemeChangeDuration time interval.
......@@ -304,6 +356,23 @@ void main() {
final RenderPhysicalShape noTintModel = getModel(tester);
expect(noTintModel.color, equals(baseColor));
// With transparent surfaceTintColor, it should not apply an overlay
await tester.pumpWidget(
Theme(
data: ThemeData(
useMaterial3: true,
),
child: buildMaterial(
color: baseColor,
surfaceTintColor: Colors.transparent,
elevation: 12.0,
),
),
);
await tester.pumpAndSettle();
final RenderPhysicalShape transparentTintModel = getModel(tester);
expect(transparentTintModel.color, equals(baseColor));
// With surfaceTintColor specified, it should not apply an overlay based
// on the elevation.
await tester.pumpWidget(
......
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