From 94b2862fe99d9ea0eea8efda400a1482adbc65da Mon Sep 17 00:00:00 2001 From: darrenaustin Date: Wed, 24 Aug 2022 13:20:18 -0700 Subject: [PATCH 1/6] Added support for surfaceTintColor and shadowColor to the Dialog widgets. --- .../gen_defaults/lib/dialog_template.dart | 6 +- packages/flutter/lib/src/material/dialog.dart | 86 +++++++++++++++++-- .../lib/src/material/dialog_theme.dart | 18 ++++ .../flutter/test/material/dialog_test.dart | 6 ++ .../test/material/dialog_theme_test.dart | 16 +++- 5 files changed, 120 insertions(+), 12 deletions(-) diff --git a/dev/tools/gen_defaults/lib/dialog_template.dart b/dev/tools/gen_defaults/lib/dialog_template.dart index 6fe11741fdd90..9fad9b7fd2f58 100644 --- a/dev/tools/gen_defaults/lib/dialog_template.dart +++ b/dev/tools/gen_defaults/lib/dialog_template.dart @@ -27,9 +27,11 @@ 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 surfaceTintColor => ${componentColor("md.comp.dialog.container.surface-tint-layer")}; @override TextStyle? get titleTextStyle => ${textStyle("md.comp.dialog.headline")}; diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 4a5e8dd6e0178..47421f50f7655 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -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,53 @@ 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. + /// * , the Material + /// Design specification for dialogs. /// {@endtemplate} - /// {@macro flutter.material.material.elevation} final double? elevation; + /// {@template flutter.material.dialog.shadowColor} + /// The color to paint the [elevation] shadow under the dialog's [Material]. + /// + /// If null then no drop shadow will be painted. + /// + /// See also: + /// * [Material.shadowColor], which describes how the drop shadow is painted. + /// * [elevation], effects how the drop shadow is painted. + /// * [surfaceTintColor], if non-null will also provide a surface tint + /// overlay on the background color to indicate elevation. + /// {@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 null then no surface tint will be applied. + /// + /// See also: + /// * [Material.surfaceTintColor], which describes how the surface tint will + /// be applied to the background color of the dialog. + /// * [elevation], effects the opacity of the surface tint. + /// * [shadowColor], if non-null will also provide a drop shadow to + /// indicate elevation. + /// {@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 +198,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 +325,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 +525,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 +747,8 @@ class AlertDialog extends StatelessWidget { return Dialog( backgroundColor: backgroundColor, elevation: elevation, + shadowColor: shadowColor, + surfaceTintColor: surfaceTintColor, insetPadding: insetPadding, clipBehavior: clipBehavior, shape: shape, @@ -860,6 +914,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 +971,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 +1092,8 @@ class SimpleDialog extends StatelessWidget { return Dialog( backgroundColor: backgroundColor, elevation: elevation, + shadowColor: shadowColor, + surfaceTintColor: surfaceTintColor, insetPadding: insetPadding, clipBehavior: clipBehavior, shape: shape, @@ -1296,6 +1359,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 +1396,11 @@ 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 surfaceTintColor => _colors.surfaceTint; @override TextStyle? get titleTextStyle => _textTheme.headlineSmall; diff --git a/packages/flutter/lib/src/material/dialog_theme.dart b/packages/flutter/lib/src/material/dialog_theme.dart index b3c7cd3218d80..a53afee5ca5a7 100644 --- a/packages/flutter/lib/src/material/dialog_theme.dart +++ b/packages/flutter/lib/src/material/dialog_theme.dart @@ -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('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty('alignment', alignment, defaultValue: null)); properties.add(ColorProperty('iconColor', iconColor)); diff --git a/packages/flutter/test/material/dialog_test.dart b/packages/flutter/test/material/dialog_test.dart index 165a253bc5d4d..9a94021ee0db8 100644 --- a/packages/flutter/test/material/dialog_test.dart +++ b/packages/flutter/test/material/dialog_test.dart @@ -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: [ ], 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 { diff --git a/packages/flutter/test/material/dialog_theme_test.dart b/packages/flutter/test/material/dialog_theme_test.dart index b3018cd0fd600..a7a3c7d1f06f5 100644 --- a/packages/flutter/test/material/dialog_theme_test.dart +++ b/packages/flutter/test/material/dialog_theme_test.dart @@ -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, [ '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: [ ], ); - 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 { From 9e9ff18dacb98ab04e9e77d43026a0baf38dd448 Mon Sep 17 00:00:00 2001 From: darrenaustin Date: Tue, 30 Aug 2022 12:30:02 -0700 Subject: [PATCH 2/6] Updated the defaults for Material.shadowColor and Material.surfaceTint to allow turning off the features with a transparent color. --- .../lib/action_chip_template.dart | 4 +-- .../gen_defaults/lib/app_bar_template.dart | 5 ++- .../gen_defaults/lib/button_template.dart | 28 +++++++++------- dev/tools/gen_defaults/lib/card_template.dart | 4 +-- .../gen_defaults/lib/dialog_template.dart | 5 ++- .../lib/filter_chip_template.dart | 4 +-- .../lib/icon_button_template.dart | 33 +++++++++++++------ .../gen_defaults/lib/input_chip_template.dart | 4 +-- .../lib/navigation_bar_template.dart | 4 ++- dev/tools/gen_defaults/lib/template.dart | 24 ++++++++++---- .../flutter/lib/src/material/action_chip.dart | 5 +-- .../flutter/lib/src/material/app_bar.dart | 4 +++ .../flutter/lib/src/material/choice_chip.dart | 2 +- packages/flutter/lib/src/material/dialog.dart | 17 ++++++++-- .../lib/src/material/elevated_button.dart | 12 +++---- .../lib/src/material/elevation_overlay.dart | 9 ++--- .../lib/src/material/filled_button.dart | 29 +++++++++------- .../flutter/lib/src/material/filter_chip.dart | 2 +- .../flutter/lib/src/material/icon_button.dart | 24 ++++++++------ .../flutter/lib/src/material/input_chip.dart | 5 +-- .../flutter/lib/src/material/material.dart | 18 +++++----- .../lib/src/material/navigation_bar.dart | 32 +++++++++++++----- .../src/material/navigation_bar_theme.dart | 30 +++++++++++------ .../lib/src/material/outlined_button.dart | 20 ++++++----- .../flutter/lib/src/material/text_button.dart | 20 ++++++----- 25 files changed, 221 insertions(+), 123 deletions(-) diff --git a/dev/tools/gen_defaults/lib/action_chip_template.dart b/dev/tools/gen_defaults/lib/action_chip_template.dart index abbe5e14ffa19..b225c75a04e5f 100644 --- a/dev/tools/gen_defaults/lib/action_chip_template.dart +++ b/dev/tools/gen_defaults/lib/action_chip_template.dart @@ -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")}; diff --git a/dev/tools/gen_defaults/lib/app_bar_template.dart b/dev/tools/gen_defaults/lib/app_bar_template.dart index 6df3f5dbebdfc..de72bf363d613 100644 --- a/dev/tools/gen_defaults/lib/app_bar_template.dart +++ b/dev/tools/gen_defaults/lib/app_bar_template.dart @@ -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( diff --git a/dev/tools/gen_defaults/lib/button_template.dart b/dev/tools/gen_defaults/lib/button_template.dart index c65ac6bd813c7..aab88d252efa0 100644 --- a/dev/tools/gen_defaults/lib/button_template.dart +++ b/dev/tools/gen_defaults/lib/button_template.dart @@ -24,7 +24,7 @@ class ButtonTemplate extends TokenTemplate { } return ''' - ButtonStyleButton.allOrNull(Colors.transparent)'''; + const MaterialStatePropertyAll(Colors.transparent)'''; } String _elevation() { @@ -49,7 +49,15 @@ class ButtonTemplate extends TokenTemplate { } return ''' - ButtonStyleButton.allOrNull(0.0)'''; + const MaterialStatePropertyAll(0.0)'''; + } + + String _elevationColor(String token) { + if (tokens.containsKey(token)) { + return 'MaterialStatePropertyAll(${color(token)})'; + } else { + return 'const MaterialStatePropertyAll(Colors.transparent)'; + } } @override @@ -96,34 +104,30 @@ class _${blockName}DefaultsM3 extends ButtonStyle { return null; }); -${tokens.containsKey("$tokenGroup.container.shadow-color") ? ''' @override MaterialStateProperty? get shadowColor => - ButtonStyleButton.allOrNull(${color("$tokenGroup.container.shadow-color")});''' : ''' - // No default shadow color'''} + ${_elevationColor("$tokenGroup.container.shadow-color")}; -${tokens.containsKey("$tokenGroup.container.surface-tint-layer.color") ? ''' @override MaterialStateProperty? get surfaceTintColor => - ButtonStyleButton.allOrNull(${color("$tokenGroup.container.surface-tint-layer.color")});''' : ''' - // No default surface tint color'''} + ${_elevationColor("$tokenGroup.container.surface-tint-layer.color")}; @override MaterialStateProperty? get elevation =>${_elevation()}; @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(_scaledPadding(context)); + MaterialStatePropertyAll(_scaledPadding(context)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(64.0, ${tokens["$tokenGroup.container.height"]})); + const MaterialStatePropertyAll(Size(64.0, ${tokens["$tokenGroup.container.height"]})); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); ${tokens.containsKey("$tokenGroup.outline.color") ? ''' @override @@ -138,7 +142,7 @@ ${tokens.containsKey("$tokenGroup.outline.color") ? ''' @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(${shape("$tokenGroup.container")}); + const MaterialStatePropertyAll(${shape("$tokenGroup.container", '')}); @override MaterialStateProperty? get mouseCursor => diff --git a/dev/tools/gen_defaults/lib/card_template.dart b/dev/tools/gen_defaults/lib/card_template.dart index ec038b3b8693d..011c745fc24b4 100644 --- a/dev/tools/gen_defaults/lib/card_template.dart +++ b/dev/tools/gen_defaults/lib/card_template.dart @@ -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")}; } '''; } diff --git a/dev/tools/gen_defaults/lib/dialog_template.dart b/dev/tools/gen_defaults/lib/dialog_template.dart index 9fad9b7fd2f58..2f2ab654ddaf8 100644 --- a/dev/tools/gen_defaults/lib/dialog_template.dart +++ b/dev/tools/gen_defaults/lib/dialog_template.dart @@ -31,7 +31,10 @@ class _${blockName}DefaultsM3 extends DialogTheme { Color? get backgroundColor => ${componentColor("md.comp.dialog.container")}; @override - Color? get surfaceTintColor => ${componentColor("md.comp.dialog.container.surface-tint-layer")}; + 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")}; diff --git a/dev/tools/gen_defaults/lib/filter_chip_template.dart b/dev/tools/gen_defaults/lib/filter_chip_template.dart index 6a179f8df0dbb..4e5ae73b7c4cb 100644 --- a/dev/tools/gen_defaults/lib/filter_chip_template.dart +++ b/dev/tools/gen_defaults/lib/filter_chip_template.dart @@ -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 diff --git a/dev/tools/gen_defaults/lib/icon_button_template.dart b/dev/tools/gen_defaults/lib/icon_button_template.dart index 10a2009c9f8f0..f6cbadff0db2c 100644 --- a/dev/tools/gen_defaults/lib/icon_button_template.dart +++ b/dev/tools/gen_defaults/lib/icon_button_template.dart @@ -9,6 +9,15 @@ class IconButtonTemplate extends TokenTemplate { super.colorSchemePrefix = '_colors.', }); + + String _elevationColor(String token) { + if (tokens.containsKey(token)) { + return 'MaterialStatePropertyAll(${color(token)})'; + } else { + return 'const MaterialStatePropertyAll(Colors.transparent)'; + } + } + @override String generate() => ''' class _${blockName}DefaultsM3 extends ButtonStyle { @@ -26,7 +35,7 @@ class _${blockName}DefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get backgroundColor => - ButtonStyleButton.allOrNull(Colors.transparent); + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get foregroundColor => @@ -66,37 +75,41 @@ class _${blockName}DefaultsM3 extends ButtonStyle { return null; }); - // No default shadow color + @override + MaterialStateProperty? get elevation => + const MaterialStatePropertyAll(0.0); - // No default surface tint color + @override + MaterialStateProperty? get shadowColor => + ${_elevationColor("md.comp.icon-button.container.shadow-color")}; @override - MaterialStateProperty? get elevation => - ButtonStyleButton.allOrNull(0.0); + MaterialStateProperty? get surfaceTintColor => + ${_elevationColor("md.comp.icon-button.container.surface-tint-layer.color")}; @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(const EdgeInsets.all(8.0)); + const MaterialStatePropertyAll(EdgeInsets.all(8.0)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(${tokens["md.comp.icon-button.state-layer.size"]}, ${tokens["md.comp.icon-button.state-layer.size"]})); + const MaterialStatePropertyAll(Size(${tokens["md.comp.icon-button.state-layer.size"]}, ${tokens["md.comp.icon-button.state-layer.size"]})); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); @override MaterialStateProperty? get iconSize => - ButtonStyleButton.allOrNull(${tokens["md.comp.icon-button.icon.size"]}); + const MaterialStatePropertyAll(${tokens["md.comp.icon-button.icon.size"]}); // No default side @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(${shape("md.comp.icon-button.state-layer")}); + const MaterialStatePropertyAll(${shape("md.comp.icon-button.state-layer", "")}); @override MaterialStateProperty? get mouseCursor => diff --git a/dev/tools/gen_defaults/lib/input_chip_template.dart b/dev/tools/gen_defaults/lib/input_chip_template.dart index cfa907297997c..5fb9037a87247 100644 --- a/dev/tools/gen_defaults/lib/input_chip_template.dart +++ b/dev/tools/gen_defaults/lib/input_chip_template.dart @@ -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")}; diff --git a/dev/tools/gen_defaults/lib/navigation_bar_template.dart b/dev/tools/gen_defaults/lib/navigation_bar_template.dart index 1f5f263ba4c5f..48d47e35c1f0a 100644 --- a/dev/tools/gen_defaults/lib/navigation_bar_template.dart +++ b/dev/tools/gen_defaults/lib/navigation_bar_template.dart @@ -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? get iconTheme { return MaterialStateProperty.resolveWith((Set states) { diff --git a/dev/tools/gen_defaults/lib/template.dart b/dev/tools/gen_defaults/lib/template.dart index c21a8b2f6df63..05d3e8f9352e2 100644 --- a/dev/tools/gen_defaults/lib/template.dart +++ b/dev/tools/gen_defaults/lib/template.dart @@ -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 shape = tokens[tokens['$componentToken.shape']!]! as Map; 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 ''; diff --git a/packages/flutter/lib/src/material/action_chip.dart b/packages/flutter/lib/src/material/action_chip.dart index 070119ff69e3a..29a9bde24058c 100644 --- a/packages/flutter/lib/src/material/action_chip.dart +++ b/packages/flutter/lib/src/material/action_chip.dart @@ -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; diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index 2e740df38cba6..8c4f37f02c13b 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -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; diff --git a/packages/flutter/lib/src/material/choice_chip.dart b/packages/flutter/lib/src/material/choice_chip.dart index e9888d1766aba..0342171be1884 100644 --- a/packages/flutter/lib/src/material/choice_chip.dart +++ b/packages/flutter/lib/src/material/choice_chip.dart @@ -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 diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 47421f50f7655..29266a4f3b44b 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -91,7 +91,11 @@ class Dialog extends StatelessWidget { /// {@template flutter.material.dialog.shadowColor} /// The color to paint the [elevation] shadow under the dialog's [Material]. /// - /// If null then no drop shadow will be painted. + /// 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. @@ -105,7 +109,13 @@ class Dialog extends StatelessWidget { /// The color used as a surface tint overlay on the dialog's background color, /// which reflects the dialog's [elevation]. /// - /// If null then no surface tint will be applied. + /// 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 a transparent color + /// (i.e. [Color.alpha] is 0). /// /// See also: /// * [Material.surfaceTintColor], which describes how the surface tint will @@ -1399,6 +1409,9 @@ class _DialogDefaultsM3 extends DialogTheme { @override Color? get backgroundColor => _colors.surface; + @override + Color? get shadowColor => Colors.transparent; + @override Color? get surfaceTintColor => _colors.surfaceTint; diff --git a/packages/flutter/lib/src/material/elevated_button.dart b/packages/flutter/lib/src/material/elevated_button.dart index 93c93197b5d06..d86a298dfd60e 100644 --- a/packages/flutter/lib/src/material/elevated_button.dart +++ b/packages/flutter/lib/src/material/elevated_button.dart @@ -582,11 +582,11 @@ class _ElevatedButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get shadowColor => - ButtonStyleButton.allOrNull(_colors.shadow); + MaterialStatePropertyAll(_colors.shadow); @override MaterialStateProperty? get surfaceTintColor => - ButtonStyleButton.allOrNull(_colors.surfaceTint); + MaterialStatePropertyAll(_colors.surfaceTint); @override MaterialStateProperty? get elevation => @@ -608,23 +608,23 @@ class _ElevatedButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(_scaledPadding(context)); + MaterialStatePropertyAll(_scaledPadding(context)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(64.0, 40.0)); + const MaterialStatePropertyAll(Size(64.0, 40.0)); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); // No default side @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(const StadiumBorder()); + const MaterialStatePropertyAll(StadiumBorder()); @override MaterialStateProperty? get mouseCursor => diff --git a/packages/flutter/lib/src/material/elevation_overlay.dart b/packages/flutter/lib/src/material/elevation_overlay.dart index 1cba0bd53f282..bf32ff5b12f6b 100644 --- a/packages/flutter/lib/src/material/elevation_overlay.dart +++ b/packages/flutter/lib/src/material/elevation_overlay.dart @@ -23,11 +23,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.alpha != 0) { return Color.alphaBlend(surfaceTint.withOpacity(_surfaceTintOpacityForElevation(elevation)), color); } return color; diff --git a/packages/flutter/lib/src/material/filled_button.dart b/packages/flutter/lib/src/material/filled_button.dart index 989e7ca222d1c..f98efbdc2c428 100644 --- a/packages/flutter/lib/src/material/filled_button.dart +++ b/packages/flutter/lib/src/material/filled_button.dart @@ -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? get shadowColor => - ButtonStyleButton.allOrNull(_colors.shadow); + MaterialStatePropertyAll(_colors.shadow); - // No default surface tint color + @override + MaterialStateProperty? get surfaceTintColor => + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get elevation => @@ -575,23 +578,23 @@ class _FilledButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(_scaledPadding(context)); + MaterialStatePropertyAll(_scaledPadding(context)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(64.0, 40.0)); + const MaterialStatePropertyAll(Size(64.0, 40.0)); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); // No default side @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(const StadiumBorder()); + const MaterialStatePropertyAll(StadiumBorder()); @override MaterialStateProperty? get mouseCursor => @@ -673,9 +676,11 @@ class _FilledTonalButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get shadowColor => - ButtonStyleButton.allOrNull(_colors.shadow); + MaterialStatePropertyAll(_colors.shadow); - // No default surface tint color + @override + MaterialStateProperty? get surfaceTintColor => + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get elevation => @@ -697,23 +702,23 @@ class _FilledTonalButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(_scaledPadding(context)); + MaterialStatePropertyAll(_scaledPadding(context)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(64.0, 40.0)); + const MaterialStatePropertyAll(Size(64.0, 40.0)); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); // No default side @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(const StadiumBorder()); + const MaterialStatePropertyAll(StadiumBorder()); @override MaterialStateProperty? get mouseCursor => diff --git a/packages/flutter/lib/src/material/filter_chip.dart b/packages/flutter/lib/src/material/filter_chip.dart index a4e1da1898693..347facea081db 100644 --- a/packages/flutter/lib/src/material/filter_chip.dart +++ b/packages/flutter/lib/src/material/filter_chip.dart @@ -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 diff --git a/packages/flutter/lib/src/material/icon_button.dart b/packages/flutter/lib/src/material/icon_button.dart index e196ec9736ea3..e173330d58bb0 100644 --- a/packages/flutter/lib/src/material/icon_button.dart +++ b/packages/flutter/lib/src/material/icon_button.dart @@ -979,7 +979,7 @@ class _IconButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get backgroundColor => - ButtonStyleButton.allOrNull(Colors.transparent); + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get foregroundColor => @@ -1019,37 +1019,41 @@ class _IconButtonDefaultsM3 extends ButtonStyle { return null; }); - // No default shadow color + @override + MaterialStateProperty? get elevation => + const MaterialStatePropertyAll(0.0); - // No default surface tint color + @override + MaterialStateProperty? get shadowColor => + const MaterialStatePropertyAll(Colors.transparent); @override - MaterialStateProperty? get elevation => - ButtonStyleButton.allOrNull(0.0); + MaterialStateProperty? get surfaceTintColor => + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(const EdgeInsets.all(8.0)); + const MaterialStatePropertyAll(EdgeInsets.all(8.0)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(40.0, 40.0)); + const MaterialStatePropertyAll(Size(40.0, 40.0)); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); @override MaterialStateProperty? get iconSize => - ButtonStyleButton.allOrNull(24.0); + const MaterialStatePropertyAll(24.0); // No default side @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(const StadiumBorder()); + const MaterialStatePropertyAll(StadiumBorder()); @override MaterialStateProperty? get mouseCursor => diff --git a/packages/flutter/lib/src/material/input_chip.dart b/packages/flutter/lib/src/material/input_chip.dart index d47ee17bcdd75..cdc18a9d520a9 100644 --- a/packages/flutter/lib/src/material/input_chip.dart +++ b/packages/flutter/lib/src/material/input_chip.dart @@ -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; diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 1194a0f1bc498..ccf9d3fea4442 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -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 a transparent color (i.e. [Color.alpha] is 0). /// /// See also: /// * [ThemeData.useMaterial3], which determines the default value for this @@ -283,8 +283,8 @@ 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]. + /// and not transparent ([Color.alpha] is 0), 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 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 with TickerProviderStateMixin { clipBehavior: widget.clipBehavior, elevation: modelElevation, color: color, - shadowColor: modelShadowColor ?? const Color(0x00000000), + shadowColor: modelShadowColor, animateColor: false, child: contents, ); diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart index 7fd5e24425b38..5af44876796c1 100644 --- a/packages/flutter/lib/src/material/navigation_bar.dart +++ b/packages/flutter/lib/src/material/navigation_bar.dart @@ -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,23 @@ 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 +136,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 +182,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 +1262,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? get iconTheme { diff --git a/packages/flutter/lib/src/material/navigation_bar_theme.dart b/packages/flutter/lib/src/material/navigation_bar_theme.dart index 140144dcbcc14..69067f01887d9 100644 --- a/packages/flutter/lib/src/material/navigation_bar_theme.dart +++ b/packages/flutter/lib/src/material/navigation_bar_theme.dart @@ -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? 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(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('indicatorShape', indicatorShape, defaultValue: null)); properties.add(DiagnosticsProperty>('labelTextStyle', labelTextStyle, defaultValue: null)); diff --git a/packages/flutter/lib/src/material/outlined_button.dart b/packages/flutter/lib/src/material/outlined_button.dart index 2342ca71e6bd4..3797d538df07f 100644 --- a/packages/flutter/lib/src/material/outlined_button.dart +++ b/packages/flutter/lib/src/material/outlined_button.dart @@ -470,7 +470,7 @@ class _OutlinedButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get backgroundColor => - ButtonStyleButton.allOrNull(Colors.transparent); + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get foregroundColor => @@ -496,27 +496,31 @@ class _OutlinedButtonDefaultsM3 extends ButtonStyle { return null; }); - // No default shadow color + @override + MaterialStateProperty? get shadowColor => + const MaterialStatePropertyAll(Colors.transparent); - // No default surface tint color + @override + MaterialStateProperty? get surfaceTintColor => + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get elevation => - ButtonStyleButton.allOrNull(0.0); + const MaterialStatePropertyAll(0.0); @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(_scaledPadding(context)); + MaterialStatePropertyAll(_scaledPadding(context)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(64.0, 40.0)); + const MaterialStatePropertyAll(Size(64.0, 40.0)); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); @override MaterialStateProperty? get side => @@ -529,7 +533,7 @@ class _OutlinedButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(const StadiumBorder()); + const MaterialStatePropertyAll(StadiumBorder()); @override MaterialStateProperty? get mouseCursor => diff --git a/packages/flutter/lib/src/material/text_button.dart b/packages/flutter/lib/src/material/text_button.dart index e60ae98b7f627..e1838c5929c68 100644 --- a/packages/flutter/lib/src/material/text_button.dart +++ b/packages/flutter/lib/src/material/text_button.dart @@ -514,7 +514,7 @@ class _TextButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty? get backgroundColor => - ButtonStyleButton.allOrNull(Colors.transparent); + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get foregroundColor => @@ -540,33 +540,37 @@ class _TextButtonDefaultsM3 extends ButtonStyle { return null; }); - // No default shadow color + @override + MaterialStateProperty? get shadowColor => + const MaterialStatePropertyAll(Colors.transparent); - // No default surface tint color + @override + MaterialStateProperty? get surfaceTintColor => + const MaterialStatePropertyAll(Colors.transparent); @override MaterialStateProperty? get elevation => - ButtonStyleButton.allOrNull(0.0); + const MaterialStatePropertyAll(0.0); @override MaterialStateProperty? get padding => - ButtonStyleButton.allOrNull(_scaledPadding(context)); + MaterialStatePropertyAll(_scaledPadding(context)); @override MaterialStateProperty? get minimumSize => - ButtonStyleButton.allOrNull(const Size(64.0, 40.0)); + const MaterialStatePropertyAll(Size(64.0, 40.0)); // No default fixedSize @override MaterialStateProperty? get maximumSize => - ButtonStyleButton.allOrNull(Size.infinite); + const MaterialStatePropertyAll(Size.infinite); // No default side @override MaterialStateProperty? get shape => - ButtonStyleButton.allOrNull(const StadiumBorder()); + const MaterialStatePropertyAll(StadiumBorder()); @override MaterialStateProperty? get mouseCursor => From c2b1969431c97367b3169da48683991f7451530d Mon Sep 17 00:00:00 2001 From: darrenaustin Date: Tue, 30 Aug 2022 13:20:37 -0700 Subject: [PATCH 3/6] Updated and added some tests. --- .../test/material/icon_button_test.dart | 6 +- .../test/material/icon_button_theme_test.dart | 6 +- .../flutter/test/material/material_test.dart | 69 +++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/packages/flutter/test/material/icon_button_test.dart b/packages/flutter/test/material/icon_button_test.dart index 5d65c374fceae..4fd4bfd07554c 100644 --- a/packages/flutter/test/material/icon_button_test.dart +++ b/packages/flutter/test/material/icon_button_test.dart @@ -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); diff --git a/packages/flutter/test/material/icon_button_theme_test.dart b/packages/flutter/test/material/icon_button_theme_test.dart index 44fd8a6d7e228..e3a7e482b6ce2 100644 --- a/packages/flutter/test/material/icon_button_theme_test.dart +++ b/packages/flutter/test/material/icon_button_theme_test.dart @@ -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(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(buttonMaterialFinder); - expect(material.shadowColor, null); + expect(material.shadowColor, Colors.transparent); await tester.pumpWidget(buildFrame(themeShadowColor: shadowColor)); await tester.pumpAndSettle(); // theme animation diff --git a/packages/flutter/test/material/material_test.dart b/packages/flutter/test/material/material_test.dart index 661d75ca42c97..79d911e5247a0 100644 --- a/packages/flutter/test/material/material_test.dart +++ b/packages/flutter/test/material/material_test.dart @@ -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( From 21a7043bca86d791d2786cd356a3c89d5432e743 Mon Sep 17 00:00:00 2001 From: darrenaustin Date: Wed, 31 Aug 2022 18:02:38 -0700 Subject: [PATCH 4/6] Updated from review comments. --- packages/flutter/lib/src/material/dialog.dart | 15 ++++++++------- .../lib/src/material/elevation_overlay.dart | 3 ++- packages/flutter/lib/src/material/material.dart | 8 ++++---- .../flutter/lib/src/material/navigation_bar.dart | 1 - 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 29266a4f3b44b..006926e561de9 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -89,7 +89,8 @@ class Dialog extends StatelessWidget { final double? elevation; /// {@template flutter.material.dialog.shadowColor} - /// The color to paint the [elevation] shadow under the dialog's [Material]. + /// 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. @@ -99,9 +100,9 @@ class Dialog extends StatelessWidget { /// /// See also: /// * [Material.shadowColor], which describes how the drop shadow is painted. - /// * [elevation], effects how the drop shadow is painted. - /// * [surfaceTintColor], if non-null will also provide a surface tint - /// overlay on the background color to indicate elevation. + /// * [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; @@ -120,9 +121,9 @@ class Dialog extends StatelessWidget { /// See also: /// * [Material.surfaceTintColor], which describes how the surface tint will /// be applied to the background color of the dialog. - /// * [elevation], effects the opacity of the surface tint. - /// * [shadowColor], if non-null will also provide a drop shadow to - /// indicate elevation. + /// * [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; diff --git a/packages/flutter/lib/src/material/elevation_overlay.dart b/packages/flutter/lib/src/material/elevation_overlay.dart index bf32ff5b12f6b..66719912b0ba8 100644 --- a/packages/flutter/lib/src/material/elevation_overlay.dart +++ b/packages/flutter/lib/src/material/elevation_overlay.dart @@ -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 @@ -28,7 +29,7 @@ class ElevationOverlay { /// [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 && surfaceTint.alpha != 0) { + if (surfaceTint != null && surfaceTint != Colors.transparent) { return Color.alphaBlend(surfaceTint.withOpacity(_surfaceTintOpacityForElevation(elevation)), color); } return color; diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index ccf9d3fea4442..67a696dd3dea6 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -263,7 +263,7 @@ class Material extends StatefulWidget { /// then [ThemeData.shadowColor] will be used. /// /// To remove the drop shadow when [elevation] is greater than 0, set - /// [shadowColor] to a transparent color (i.e. [Color.alpha] is 0). + /// [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, - /// and not transparent ([Color.alpha] is 0), 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. /// diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart index 5af44876796c1..d4c78938141e0 100644 --- a/packages/flutter/lib/src/material/navigation_bar.dart +++ b/packages/flutter/lib/src/material/navigation_bar.dart @@ -117,7 +117,6 @@ class NavigationBar extends StatelessWidget { /// 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 From 478c372bc8edf0740bbec4f9dff64a40277882b2 Mon Sep 17 00:00:00 2001 From: darrenaustin Date: Wed, 31 Aug 2022 18:39:43 -0700 Subject: [PATCH 5/6] Updated an example test. --- examples/api/test/material/appbar/app_bar.1_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/api/test/material/appbar/app_bar.1_test.dart b/examples/api/test/material/appbar/app_bar.1_test.dart index 2713cf41bffb7..180605347f360 100644 --- a/examples/api/test/material/appbar/app_bar.1_test.dart +++ b/examples/api/test/material/appbar/app_bar.1_test.dart @@ -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); From 00408210c674821aa287ebb4836cb1d390728569 Mon Sep 17 00:00:00 2001 From: darrenaustin Date: Thu, 1 Sep 2022 19:59:39 -0700 Subject: [PATCH 6/6] Added support for shadowColor and surfaceTintColor for Drawer widget. --- packages/flutter/lib/src/material/dialog.dart | 3 +- packages/flutter/lib/src/material/drawer.dart | 41 ++++++++++++++++++- .../lib/src/material/drawer_theme.dart | 20 +++++++++ .../test/material/drawer_theme_test.dart | 35 ++++++++++++++-- 4 files changed, 92 insertions(+), 7 deletions(-) diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 006926e561de9..405a579afb624 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -115,8 +115,7 @@ class Dialog extends StatelessWidget { /// If null and [ThemeData.useMaterial3] is true then [ThemeData]'s /// [ColorScheme.surfaceTint] will be used. /// - /// To disable this feature, set [surfaceTintColor] to a transparent color - /// (i.e. [Color.alpha] is 0). + /// To disable this feature, set [surfaceTintColor] to [Colors.transparent]. /// /// See also: /// * [Material.surfaceTintColor], which describes how the surface tint will diff --git a/packages/flutter/lib/src/material/drawer.dart b/packages/flutter/lib/src/material/drawer.dart index fcafbdd5219ab..0619e6fa7f2e7 100644 --- a/packages/flutter/lib/src/material/drawer.dart +++ b/packages/flutter/lib/src/material/drawer.dart @@ -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, ), diff --git a/packages/flutter/lib/src/material/drawer_theme.dart b/packages/flutter/lib/src/material/drawer_theme.dart index 1aabcae4aa450..ac4277f0f8de9 100644 --- a/packages/flutter/lib/src/material/drawer_theme.dart +++ b/packages/flutter/lib/src/material/drawer_theme.dart @@ -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('shape', shape, defaultValue: null)); properties.add(DoubleProperty('width', width, defaultValue: null)); } diff --git a/packages/flutter/test/material/drawer_theme_test.dart b/packages/flutter/test/material/drawer_theme_test.dart index da6b9e8293a00..e84f7f488f04d 100644 --- a/packages/flutter/test/material/drawer_theme_test.dart +++ b/packages/flutter/test/material/drawer_theme_test.dart @@ -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 scaffoldKey = GlobalKey(); + 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);