From 8b32ac7a512b35b2b01da949ff41be814d025207 Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Tue, 29 Nov 2022 13:11:43 -0600 Subject: [PATCH] Revert "Update SnackBar to support Material 3" (#116199) * Revert "Update SnackBar to support Material 3 (#115750)" This reverts commit d58855c49986d46588b0e2b1bf2f8b2bb97e3932. * Kick ci.yaml Co-authored-by: Jenn Magder --- dev/tools/gen_defaults/bin/gen_defaults.dart | 2 - .../gen_defaults/lib/snackbar_template.dart | 76 ---- .../lib/material/snack_bar/snack_bar.2.dart | 168 -------- .../flutter/lib/src/material/snack_bar.dart | 403 ++++-------------- .../lib/src/material/snack_bar_theme.dart | 37 +- .../flutter/lib/src/material/theme_data.dart | 1 - .../flutter/test/material/debug_test.dart | 1 - .../flutter/test/material/snack_bar_test.dart | 141 +----- .../test/material/snack_bar_theme_test.dart | 41 +- 9 files changed, 89 insertions(+), 781 deletions(-) delete mode 100644 dev/tools/gen_defaults/lib/snackbar_template.dart delete mode 100644 examples/api/lib/material/snack_bar/snack_bar.2.dart diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart index e8a6364a5849..de9b2b894473 100644 --- a/dev/tools/gen_defaults/bin/gen_defaults.dart +++ b/dev/tools/gen_defaults/bin/gen_defaults.dart @@ -44,7 +44,6 @@ import 'package:gen_defaults/progress_indicator_template.dart'; import 'package:gen_defaults/radio_template.dart'; import 'package:gen_defaults/segmented_button_template.dart'; import 'package:gen_defaults/slider_template.dart'; -import 'package:gen_defaults/snackbar_template.dart'; import 'package:gen_defaults/surface_tint.dart'; import 'package:gen_defaults/switch_template.dart'; import 'package:gen_defaults/text_field_template.dart'; @@ -162,7 +161,6 @@ Future main(List args) async { ProgressIndicatorTemplate('ProgressIndicator', '$materialLib/progress_indicator.dart', tokens).updateFile(); RadioTemplate('Radio', '$materialLib/radio.dart', tokens).updateFile(); SegmentedButtonTemplate('SegmentedButton', '$materialLib/segmented_button.dart', tokens).updateFile(); - SnackbarTemplate('md.comp.snackbar', 'Snackbar', '$materialLib/snack_bar.dart', tokens).updateFile(); SliderTemplate('md.comp.slider', 'Slider', '$materialLib/slider.dart', tokens).updateFile(); SurfaceTintTemplate('SurfaceTint', '$materialLib/elevation_overlay.dart', tokens).updateFile(); SwitchTemplate('Switch', '$materialLib/switch.dart', tokens).updateFile(); diff --git a/dev/tools/gen_defaults/lib/snackbar_template.dart b/dev/tools/gen_defaults/lib/snackbar_template.dart deleted file mode 100644 index 47aad664cf06..000000000000 --- a/dev/tools/gen_defaults/lib/snackbar_template.dart +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'template.dart'; - -class SnackbarTemplate extends TokenTemplate { - const SnackbarTemplate( - this.tokenGroup, super.blockName, super.fileName, super.tokens, { - super.colorSchemePrefix = '_colors.' - }); - - final String tokenGroup; - - @override - String generate() => ''' -class _${blockName}DefaultsM3 extends SnackBarThemeData { - _${blockName}DefaultsM3(this.context); - - final BuildContext context; - late final ThemeData _theme = Theme.of(context); - - late final ColorScheme _colors = _theme.colorScheme; - - @override - Color get backgroundColor => ${componentColor("$tokenGroup.container")}; - - @override - Color get actionTextColor => MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return ${componentColor("$tokenGroup.action.pressed.label-text")}; - } - if (states.contains(MaterialState.pressed)) { - return ${componentColor("$tokenGroup.action.pressed.label-text")}; - } - if (states.contains(MaterialState.hovered)) { - return ${componentColor("$tokenGroup.action.hover.label-text")}; - } - if (states.contains(MaterialState.focused)) { - return ${componentColor("$tokenGroup.action.focus.label-text")}; - } - return ${componentColor("$tokenGroup.action.label-text")}; - }); - - @override - Color get disabledActionTextColor => - ${componentColor("$tokenGroup.action.pressed.label-text")}; - - - @override - TextStyle get contentTextStyle => - ${textStyle("$tokenGroup.supporting-text")}!.copyWith - (color: ${componentColor("$tokenGroup.supporting-text")}, - ); - - @override - double get elevation => ${elevation("$tokenGroup.container")}; - - @override - ShapeBorder get shape => ${shape("$tokenGroup.container")}; - - @override - SnackBarBehavior get behavior => SnackBarBehavior.fixed; - - @override - EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0); - - @override - bool get showCloseIcon => false; - - @override - Color get iconColor => _colors.onInverseSurface; - } - -'''; -} diff --git a/examples/api/lib/material/snack_bar/snack_bar.2.dart b/examples/api/lib/material/snack_bar/snack_bar.2.dart deleted file mode 100644 index 6eb17467017c..000000000000 --- a/examples/api/lib/material/snack_bar/snack_bar.2.dart +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Flutter code sample for [SnackBar] with Material 3 specifications. - -import 'package:flutter/material.dart'; - -void main() => runApp(const MyApp()); - -// A Material 3 [SnackBar] demonstrating an optional icon, in either floating -// or fixed format. -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - static const String _title = 'Flutter Code Sample'; - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: _title, - theme: ThemeData(useMaterial3: true), - home: Scaffold( - appBar: AppBar(title: const Text(_title)), - body: const Center( - child: SnackBarExample(), - ), - ), - ); - } -} - -class SnackBarExample extends StatefulWidget { - const SnackBarExample({super.key}); - - @override - State createState() => _SnackBarExampleState(); -} - -class _SnackBarExampleState extends State { - SnackBarBehavior? _snackBarBehavior = SnackBarBehavior.floating; - bool _withIcon = true; - bool _withAction = true; - bool _multiLine = false; - bool _longActionLabel = false; - - Padding _configRow(List children) => Padding( - padding: const EdgeInsets.all(8.0), child: Row(children: children)); - - @override - Widget build(BuildContext context) { - return Padding(padding: const EdgeInsets.only(left: 50.0), child: Column( - children: [ - _configRow([ - Text('Snack Bar configuration', - style: Theme.of(context).textTheme.bodyLarge), - ]), - _configRow( - [ - const Text('Fixed'), - Radio( - value: SnackBarBehavior.fixed, - groupValue: _snackBarBehavior, - onChanged: (SnackBarBehavior? value) { - setState(() { - _snackBarBehavior = value; - }); - }, - ), - const Text('Floating'), - Radio( - value: SnackBarBehavior.floating, - groupValue: _snackBarBehavior, - onChanged: (SnackBarBehavior? value) { - setState(() { - _snackBarBehavior = value; - }); - }, - ), - ], - ), - _configRow( - [ - const Text('Include Icon '), - Switch( - value: _withIcon, - onChanged: (bool value) { - setState(() { - _withIcon = !_withIcon; - }); - }, - ), - ], - ), - _configRow( - [ - const Text('Include Action '), - Switch( - value: _withAction, - onChanged: (bool value) { - setState(() { - _withAction = !_withAction; - }); - }, - ), - const SizedBox(width: 16.0), - const Text('Long Action Label '), - Switch( - value: _longActionLabel, - onChanged: !_withAction - ? null - : (bool value) { - setState(() { - _longActionLabel = !_longActionLabel; - }); - }, - ), - ], - ), - _configRow( - [ - const Text('Multi Line Text'), - Switch( - value: _multiLine, - onChanged: _snackBarBehavior == SnackBarBehavior.fixed ? null : (bool value) { - setState(() { - _multiLine = !_multiLine; - }); - }, - ), - ], - ), - const SizedBox(height: 16.0), - ElevatedButton( - child: const Text('Show Snackbar'), - onPressed: () { - ScaffoldMessenger.of(context).showSnackBar(_snackBar()); - } - ), - ], - ), - ); -} - - SnackBar _snackBar() { - final SnackBarAction? action = _withAction - ? SnackBarAction( - label: _longActionLabel ? 'Long Action Text' : 'Action', - onPressed: () { - // Code to execute. - }, - ) - : null; - final double? width = - _snackBarBehavior == SnackBarBehavior.floating && _multiLine ? 400.0 : null; - final String label = _multiLine - ? 'A Snack Bar with quite a lot of text which spans across multiple lines' - : 'Single Line Snack Bar'; - return SnackBar( - content: Text(label), - showCloseIcon: _withIcon, - width: width, - behavior: _snackBarBehavior, - action: action, - duration: const Duration(seconds: 3), - ); - } -} diff --git a/packages/flutter/lib/src/material/snack_bar.dart b/packages/flutter/lib/src/material/snack_bar.dart index 3f6156b63b64..e56fc8532751 100644 --- a/packages/flutter/lib/src/material/snack_bar.dart +++ b/packages/flutter/lib/src/material/snack_bar.dart @@ -7,8 +7,6 @@ import 'package:flutter/widgets.dart'; import 'button_style.dart'; import 'color_scheme.dart'; -import 'icon_button.dart'; -import 'icons.dart'; import 'material.dart'; import 'material_state.dart'; import 'scaffold.dart'; @@ -21,13 +19,17 @@ import 'theme.dart'; // late BuildContext context; const double _singleLineVerticalPadding = 14.0; + +// TODO(ianh): We should check if the given text and actions are going to fit on +// one line or not, and if they are, use the single-line layout, and if not, use +// the multiline layout, https://github.com/flutter/flutter/issues/32782 +// See https://material.io/components/snackbars#specs, 'Longer Action Text' does +// not match spec. + const Duration _snackBarTransitionDuration = Duration(milliseconds: 250); const Duration _snackBarDisplayDuration = Duration(milliseconds: 4000); const Curve _snackBarHeightCurve = Curves.fastOutSlowIn; -const Curve _snackBarM3HeightCurve = Curves.easeInOutQuart; - -const Curve _snackBarFadeInCurve = Interval(0.4, 1.0); -const Curve _snackBarM3FadeInCurve = Interval(0.4, 0.6, curve: Curves.easeInCirc); +const Curve _snackBarFadeInCurve = Interval(0.45, 1.0, curve: Curves.fastOutSlowIn); const Curve _snackBarFadeOutCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlowIn); /// Specify how a [SnackBar] was closed. @@ -95,11 +97,6 @@ class SnackBarAction extends StatefulWidget { /// The button label color. If not provided, defaults to /// [SnackBarThemeData.actionTextColor]. - /// - /// If [textColor] is a [MaterialStateColor], then the text color will be - /// be resolved against the set of [MaterialState]s that the action text - /// is in, thus allowing for different colors for states such as pressed, - /// hovered and others. final Color? textColor; /// The button disabled label color. This color is shown after the @@ -135,36 +132,17 @@ class _SnackBarActionState extends State { @override Widget build(BuildContext context) { - final SnackBarThemeData defaults = Theme.of(context).useMaterial3 - ? _SnackbarDefaultsM3(context) - : _SnackbarDefaultsM2(context); - final SnackBarThemeData snackBarTheme = Theme.of(context).snackBarTheme; - - MaterialStateColor resolveForegroundColor() { - if (widget.textColor is MaterialStateColor) { - return widget.textColor! as MaterialStateColor; - } - if (snackBarTheme.actionTextColor is MaterialStateColor) { - return snackBarTheme.actionTextColor! as MaterialStateColor; - } - if (defaults.actionTextColor is MaterialStateColor) { - return defaults.actionTextColor! as MaterialStateColor; + Color? resolveForegroundColor(Set states) { + final SnackBarThemeData snackBarTheme = Theme.of(context).snackBarTheme; + if (states.contains(MaterialState.disabled)) { + return widget.disabledTextColor ?? snackBarTheme.disabledActionTextColor; } - return MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return widget.disabledTextColor ?? - snackBarTheme.disabledActionTextColor ?? - defaults.disabledActionTextColor!; - } - return widget.textColor ?? - snackBarTheme.actionTextColor ?? - defaults.actionTextColor!; - }); + return widget.textColor ?? snackBarTheme.actionTextColor; } return TextButton( style: ButtonStyle( - foregroundColor: resolveForegroundColor(), + foregroundColor: MaterialStateProperty.resolveWith(resolveForegroundColor), ), onPressed: _haveTriggeredAction ? null : _handlePressed, child: Text(widget.label), @@ -235,8 +213,6 @@ class SnackBar extends StatefulWidget { this.shape, this.behavior, this.action, - this.showCloseIcon, - this.closeIconColor, this.duration = _snackBarDisplayDuration, this.animation, this.onVisible, @@ -279,8 +255,7 @@ class SnackBar extends StatefulWidget { /// This property is only used when [behavior] is [SnackBarBehavior.floating]. /// It can not be used if [width] is specified. /// - /// If this property is null, then [SnackBarThemeData.insetPadding] of - /// [ThemeData.snackBarTheme] is used. If that is also null, then the default is + /// If this property is null, then the default is /// `EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0)`. final EdgeInsetsGeometry? margin; @@ -344,9 +319,6 @@ class SnackBar extends StatefulWidget { /// If this property is null, then [SnackBarThemeData.behavior] of /// [ThemeData.snackBarTheme] is used. If that is null, then the default is /// [SnackBarBehavior.fixed]. - /// - /// If this value is [SnackBarBehavior.floating], the length of the bar - /// is defined by either [width] or [margin]. final SnackBarBehavior? behavior; /// (optional) An action that the user can take based on the snack bar. @@ -357,24 +329,6 @@ class SnackBar extends StatefulWidget { /// The action should not be "dismiss" or "cancel". final SnackBarAction? action; - /// (optional) Whether to include a "close" icon widget. - /// - /// Tapping the icon will close the snack bar. - final bool? showCloseIcon; - - /// (optional) An optional color for the close icon, if [showCloseIcon] is - /// true. - /// - /// If this property is null, then [SnackBarThemeData.closeIconColor] of - /// [ThemeData.snackBarTheme] is used. If that is null, then the default is - /// inverse surface. - /// - /// If [closeIconColor] is a [MaterialStateColor], then the icon color will be - /// be resolved against the set of [MaterialState]s that the action text - /// is in, thus allowing for different colors for states such as pressed, - /// hovered and others. - final Color? closeIconColor; - /// The amount of time the snack bar should be displayed. /// /// Defaults to 4.0s. @@ -430,8 +384,6 @@ class SnackBar extends StatefulWidget { shape: shape, behavior: behavior, action: action, - showCloseIcon: showCloseIcon, - closeIconColor: closeIconColor, duration: duration, animation: newAnimation, onVisible: onVisible, @@ -491,38 +443,34 @@ class _SnackBarState extends State { final ColorScheme colorScheme = theme.colorScheme; final SnackBarThemeData snackBarTheme = theme.snackBarTheme; final bool isThemeDark = theme.brightness == Brightness.dark; - final Color buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary; - final SnackBarThemeData defaults = theme.useMaterial3 - ? _SnackbarDefaultsM3(context) - : _SnackbarDefaultsM2(context); + final Color buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary; // SnackBar uses a theme that is the opposite brightness from // the surrounding theme. final Brightness brightness = isThemeDark ? Brightness.light : Brightness.dark; + final Color themeBackgroundColor = isThemeDark + ? colorScheme.onSurface + : Color.alphaBlend(colorScheme.onSurface.withOpacity(0.80), colorScheme.surface); + final ThemeData inverseTheme = theme.copyWith( + colorScheme: ColorScheme( + primary: colorScheme.onPrimary, + primaryVariant: colorScheme.onPrimary, + secondary: buttonColor, + secondaryVariant: colorScheme.onSecondary, + surface: colorScheme.onSurface, + background: themeBackgroundColor, + error: colorScheme.onError, + onPrimary: colorScheme.primary, + onSecondary: colorScheme.secondary, + onSurface: colorScheme.surface, + onBackground: colorScheme.background, + onError: colorScheme.error, + brightness: brightness, + ), + ); - // Invert the theme values for Material 2. Material 3 values are tokenzied to pre-inverted values. - final ThemeData effectiveTheme = theme.useMaterial3 - ? theme - : theme.copyWith( - colorScheme: ColorScheme( - primary: colorScheme.onPrimary, - primaryVariant: colorScheme.onPrimary, - secondary: buttonColor, - secondaryVariant: colorScheme.onSecondary, - surface: colorScheme.onSurface, - background: defaults.backgroundColor!, - error: colorScheme.onError, - onPrimary: colorScheme.primary, - onSecondary: colorScheme.secondary, - onSurface: colorScheme.surface, - onBackground: colorScheme.background, - onError: colorScheme.error, - brightness: brightness, - ), - ); - - final TextStyle? contentTextStyle = snackBarTheme.contentTextStyle ?? defaults.contentTextStyle; - final SnackBarBehavior snackBarBehavior = widget.behavior ?? snackBarTheme.behavior ?? defaults.behavior!; + final TextStyle? contentTextStyle = snackBarTheme.contentTextStyle ?? ThemeData(brightness: brightness).textTheme.titleMedium; + final SnackBarBehavior snackBarBehavior = widget.behavior ?? snackBarTheme.behavior ?? SnackBarBehavior.fixed; final double? width = widget.width ?? snackBarTheme.width; assert((){ // Whether the behavior is set through the constructor or the theme, @@ -544,116 +492,47 @@ class _SnackBarState extends State { return true; }()); - final bool showCloseIcon = widget.showCloseIcon ?? snackBarTheme.showCloseIcon ?? defaults.showCloseIcon!; - final bool isFloatingSnackBar = snackBarBehavior == SnackBarBehavior.floating; final double horizontalPadding = isFloatingSnackBar ? 16.0 : 24.0; - final EdgeInsetsGeometry padding = widget.padding ?? - EdgeInsetsDirectional.only( - start: horizontalPadding, - end: widget.action != null || showCloseIcon - ? 0 - : horizontalPadding); + final EdgeInsetsGeometry padding = widget.padding + ?? EdgeInsetsDirectional.only(start: horizontalPadding, end: widget.action != null ? 0 : horizontalPadding); final double actionHorizontalMargin = (widget.padding?.resolve(TextDirection.ltr).right ?? horizontalPadding) / 2; - final double iconHorizontalMargin = (widget.padding?.resolve(TextDirection.ltr).right ?? horizontalPadding) / 12.0; final CurvedAnimation heightAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarHeightCurve); final CurvedAnimation fadeInAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarFadeInCurve); - final CurvedAnimation fadeInM3Animation = CurvedAnimation(parent: widget.animation!, curve: _snackBarM3FadeInCurve); - final CurvedAnimation fadeOutAnimation = CurvedAnimation( parent: widget.animation!, curve: _snackBarFadeOutCurve, reverseCurve: const Threshold(0.0), ); - // Material 3 Animation has a height animation on entry, but a direct fade out on exit. - final CurvedAnimation heightM3Animation = CurvedAnimation( - parent: widget.animation!, - curve: _snackBarM3HeightCurve, - reverseCurve: const Threshold(0.0), - ); - - - final IconButton? iconButton = showCloseIcon - ? IconButton( - icon: const Icon(Icons.close), - iconSize: 24.0, - color: widget.closeIconColor ?? snackBarTheme.closeIconColor ?? defaults.closeIconColor, - onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss), - ) - : null; - - // Calculate combined width of Action, Icon, and their padding, if they are present. - final TextPainter actionTextPainter = TextPainter( - text: TextSpan( - text: widget.action?.label ?? '', - style: Theme.of(context).textTheme.labelLarge, - ), - maxLines: 1, - textDirection: TextDirection.ltr) - ..layout(); - final double actionAndIconWidth = actionTextPainter.size.width + - (widget.action != null ? actionHorizontalMargin : 0) + - (showCloseIcon ? (iconButton?.iconSize ?? 0 + iconHorizontalMargin) : 0); - - final EdgeInsets margin = widget.margin?.resolve(TextDirection.ltr) ?? snackBarTheme.insetPadding ?? defaults.insetPadding!; - - final double snackBarWidth = widget.width ?? mediaQueryData.size.width - (margin.left + margin.right); - // Action and Icon will overflow to a new line if their width is greater - // than one quarter of the total Snack Bar width. - final bool actionLineOverflow = - actionAndIconWidth / snackBarWidth > 0.25; - - final List maybeActionAndIcon = [ - if (widget.action != null) - Padding( - padding: EdgeInsets.symmetric(horizontal: actionHorizontalMargin), - child: TextButtonTheme( - data: TextButtonThemeData( - style: TextButton.styleFrom( - foregroundColor: buttonColor, - padding: EdgeInsets.symmetric(horizontal: horizontalPadding), - ), - ), - child: widget.action!, - ), - ), - if (showCloseIcon) - Padding( - padding: EdgeInsets.symmetric(horizontal: iconHorizontalMargin), - child: iconButton, - ), - ]; Widget snackBar = Padding( padding: padding, - child: Column( - mainAxisSize: MainAxisSize.min, + child: Row( children: [ - Row( - children: [ - Expanded( - child: Container( - padding: widget.padding == null - ? const EdgeInsets.symmetric( - vertical: _singleLineVerticalPadding) - : null, - child: DefaultTextStyle( - style: contentTextStyle!, - child: widget.content, + Expanded( + child: Container( + padding: widget.padding == null ? const EdgeInsets.symmetric(vertical: _singleLineVerticalPadding) : null, + child: DefaultTextStyle( + style: contentTextStyle!, + child: widget.content, + ), + ), + ), + if (widget.action != null) + Padding( + padding: EdgeInsets.symmetric(horizontal: actionHorizontalMargin), + child: TextButtonTheme( + data: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: buttonColor, + padding: EdgeInsets.symmetric(horizontal: horizontalPadding), ), ), + child: widget.action!, ), - if(!actionLineOverflow) ...maybeActionAndIcon, - if(actionLineOverflow) SizedBox(width: snackBarWidth*0.4), - ], - ), - if(actionLineOverflow) Padding( - padding: const EdgeInsets.only(bottom: _singleLineVerticalPadding), - child: Row(mainAxisAlignment: MainAxisAlignment.end, - children: maybeActionAndIcon), - ), + ), ], ), ); @@ -665,17 +544,19 @@ class _SnackBarState extends State { ); } - final double elevation = widget.elevation ?? snackBarTheme.elevation ?? defaults.elevation!; - final Color backgroundColor = widget.backgroundColor ?? snackBarTheme.backgroundColor ?? defaults.backgroundColor!; - final ShapeBorder? shape = widget.shape ?? snackBarTheme.shape ?? (isFloatingSnackBar ? defaults.shape : null); + final double elevation = widget.elevation ?? snackBarTheme.elevation ?? 6.0; + final Color backgroundColor = widget.backgroundColor ?? snackBarTheme.backgroundColor ?? inverseTheme.colorScheme.background; + final ShapeBorder? shape = widget.shape + ?? snackBarTheme.shape + ?? (isFloatingSnackBar ? const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))) : null); snackBar = Material( shape: shape, elevation: elevation, color: backgroundColor, child: Theme( - data: effectiveTheme, - child: mediaQueryData.accessibleNavigation || theme.useMaterial3 + data: inverseTheme, + child: mediaQueryData.accessibleNavigation ? snackBar : FadeTransition( opacity: fadeOutAnimation, @@ -685,16 +566,24 @@ class _SnackBarState extends State { ); if (isFloatingSnackBar) { + const double topMargin = 5.0; + const double bottomMargin = 10.0; // If width is provided, do not include horizontal margins. if (width != null) { snackBar = Container( - margin: EdgeInsets.only(top: margin.top, bottom: margin.bottom), + margin: const EdgeInsets.only(top: topMargin, bottom: bottomMargin), width: width, child: snackBar, ); } else { + const double horizontalMargin = 15.0; snackBar = Padding( - padding: margin, + padding: widget.margin ?? const EdgeInsets.fromLTRB( + horizontalMargin, + topMargin, + horizontalMargin, + bottomMargin, + ), child: snackBar, ); } @@ -725,27 +614,11 @@ class _SnackBarState extends State { final Widget snackBarTransition; if (mediaQueryData.accessibleNavigation) { snackBarTransition = snackBar; - } else if (isFloatingSnackBar && !theme.useMaterial3) { + } else if (isFloatingSnackBar) { snackBarTransition = FadeTransition( opacity: fadeInAnimation, child: snackBar, ); - // Is Material 3 Floating Snack Bar. - } else if (isFloatingSnackBar && theme.useMaterial3) { - snackBarTransition = FadeTransition( - opacity: fadeInM3Animation, - child: AnimatedBuilder( - animation: heightM3Animation, - builder: (BuildContext context, Widget? child) { - return Align( - alignment: AlignmentDirectional.bottomStart, - heightFactor: heightM3Animation.value, - child: child, - ); - }, - child: snackBar, - ), - ); } else { snackBarTransition = AnimatedBuilder( animation: heightAnimation, @@ -770,123 +643,3 @@ class _SnackBarState extends State { ); } } - -// Hand coded defaults based on Material Design 2. -class _SnackbarDefaultsM2 extends SnackBarThemeData { - _SnackbarDefaultsM2(BuildContext context) - : _theme = Theme.of(context), - _colors = Theme.of(context).colorScheme, - super(elevation: 6.0); - - late final ThemeData _theme; - late final ColorScheme _colors; - - @override - Color get backgroundColor => _theme.brightness == Brightness.light - ? Color.alphaBlend(_colors.onSurface.withOpacity(0.80), _colors.surface) - : _colors.onSurface; - - @override - TextStyle? get contentTextStyle => ThemeData( - brightness: _theme.brightness == Brightness.light - ? Brightness.dark - : Brightness.light) - .textTheme - .titleMedium; - - @override - SnackBarBehavior get behavior => SnackBarBehavior.fixed; - - @override - Color get actionTextColor => _colors.secondary; - - @override - Color get disabledActionTextColor => _colors.onSurface - .withOpacity(_theme.brightness == Brightness.light ? 0.38 : 0.3); - - @override - ShapeBorder get shape => const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(4.0), - ), - ); - - @override - EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0); - - @override - bool get showCloseIcon => false; - - @override - Color get closeIconColor => _colors.onSurface; -} - -// BEGIN GENERATED TOKEN PROPERTIES - Snackbar - -// Do not edit by hand. The code between the "BEGIN GENERATED" and -// "END GENERATED" comments are generated from data in the Material -// Design token database by the script: -// dev/tools/gen_defaults/bin/gen_defaults.dart. - -// Token database version: v0_143 - -class _SnackbarDefaultsM3 extends SnackBarThemeData { - _SnackbarDefaultsM3(this.context); - - final BuildContext context; - late final ThemeData _theme = Theme.of(context); - - late final ColorScheme _colors = _theme.colorScheme; - - @override - Color get backgroundColor => _colors.inverseSurface; - - @override - Color get actionTextColor => MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return _colors.inversePrimary; - } - if (states.contains(MaterialState.pressed)) { - return _colors.inversePrimary; - } - if (states.contains(MaterialState.hovered)) { - return _colors.inversePrimary; - } - if (states.contains(MaterialState.focused)) { - return _colors.inversePrimary; - } - return _colors.inversePrimary; - }); - - @override - Color get disabledActionTextColor => - _colors.inversePrimary; - - - @override - TextStyle get contentTextStyle => - Theme.of(context).textTheme.bodyMedium!.copyWith - (color: _colors.onInverseSurface, - ); - - @override - double get elevation => 6.0; - - @override - ShapeBorder get shape => const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))); - - @override - SnackBarBehavior get behavior => SnackBarBehavior.fixed; - - @override - EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0); - - @override - bool get showCloseIcon => false; - - @override - Color get closeIconColor => _colors.onInverseSurface; - } - - -// END GENERATED TOKEN PROPERTIES - Snackbar diff --git a/packages/flutter/lib/src/material/snack_bar_theme.dart b/packages/flutter/lib/src/material/snack_bar_theme.dart index cbf21fba8e6d..9086975de7df 100644 --- a/packages/flutter/lib/src/material/snack_bar_theme.dart +++ b/packages/flutter/lib/src/material/snack_bar_theme.dart @@ -61,9 +61,6 @@ class SnackBarThemeData with Diagnosticable { this.shape, this.behavior, this.width, - this.insetPadding, - this.showCloseIcon, - this.closeIconColor, }) : assert(elevation == null || elevation >= 0.0), assert( width == null || @@ -118,21 +115,6 @@ class SnackBarThemeData with Diagnosticable { /// [SnackBarBehavior.floating]. final double? width; - /// Overrides the default value for [SnackBar.margin]. - /// - /// This value is only used when [behavior] is [SnackBarBehavior.floating]. - final EdgeInsets? insetPadding; - - /// Overrides the default value for [SnackBar.showCloseIcon]. - /// - /// Whether to show an optional "Close" icon. - final bool? showCloseIcon; - - /// Overrides the default value for [SnackBar.closeIconColor]. - /// - /// This value is only used if [showCloseIcon] is true. - final Color? closeIconColor; - /// Creates a copy of this object with the given fields replaced with the /// new values. SnackBarThemeData copyWith({ @@ -144,9 +126,6 @@ class SnackBarThemeData with Diagnosticable { ShapeBorder? shape, SnackBarBehavior? behavior, double? width, - EdgeInsets? insetPadding, - bool? showCloseIcon, - Color? closeIconColor, }) { return SnackBarThemeData( backgroundColor: backgroundColor ?? this.backgroundColor, @@ -157,9 +136,6 @@ class SnackBarThemeData with Diagnosticable { shape: shape ?? this.shape, behavior: behavior ?? this.behavior, width: width ?? this.width, - insetPadding: insetPadding ?? this.insetPadding, - showCloseIcon: showCloseIcon ?? this.showCloseIcon, - closeIconColor: closeIconColor ?? this.closeIconColor, ); } @@ -179,8 +155,6 @@ class SnackBarThemeData with Diagnosticable { shape: ShapeBorder.lerp(a?.shape, b?.shape, t), behavior: t < 0.5 ? a?.behavior : b?.behavior, width: lerpDouble(a?.width, b?.width, t), - insetPadding: EdgeInsets.lerp(a?.insetPadding, b?.insetPadding, t), - closeIconColor: Color.lerp(a?.closeIconColor, b?.closeIconColor, t), ); } @@ -194,9 +168,6 @@ class SnackBarThemeData with Diagnosticable { shape, behavior, width, - insetPadding, - showCloseIcon, - closeIconColor, ); @override @@ -215,10 +186,7 @@ class SnackBarThemeData with Diagnosticable { && other.elevation == elevation && other.shape == shape && other.behavior == behavior - && other.width == width - && other.insetPadding == insetPadding - && other.showCloseIcon == showCloseIcon - && other.closeIconColor == closeIconColor; + && other.width == width; } @override @@ -232,8 +200,5 @@ class SnackBarThemeData with Diagnosticable { properties.add(DiagnosticsProperty('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty('behavior', behavior, defaultValue: null)); properties.add(DoubleProperty('width', width, defaultValue: null)); - properties.add(DiagnosticsProperty('insetPadding', insetPadding, defaultValue: null)); - properties.add(DiagnosticsProperty('showCloseIcon', showCloseIcon, defaultValue: null)); - properties.add(ColorProperty('closeIconColor', closeIconColor, defaultValue: null)); } } diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 86c75a911c8c..5da898264563 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -1321,7 +1321,6 @@ class ThemeData with Diagnosticable { /// * [Navigation rail](https://m3.material.io/components/navigation-rail): [NavigationRail] /// * Progress indicators: [CircularProgressIndicator], [LinearProgressIndicator] /// * Radio button: [Radio] - /// * Snack bar: [SnackBar] /// * Switch: [Switch] /// * Top app bar: [AppBar] /// diff --git a/packages/flutter/test/material/debug_test.dart b/packages/flutter/test/material/debug_test.dart index a979d7cc0678..330ab5e56c7b 100644 --- a/packages/flutter/test/material/debug_test.dart +++ b/packages/flutter/test/material/debug_test.dart @@ -316,7 +316,6 @@ void main() { ' TextButtonTheme\n' ' Padding\n' ' Row\n' - ' Column\n' ' Padding\n' ' MediaQuery\n' ' Padding\n' diff --git a/packages/flutter/test/material/snack_bar_test.dart b/packages/flutter/test/material/snack_bar_test.dart index 4d5a847b878d..53a820a5fab0 100644 --- a/packages/flutter/test/material/snack_bar_test.dart +++ b/packages/flutter/test/material/snack_bar_test.dart @@ -2340,146 +2340,7 @@ void main() { await expectLater(find.byType(MaterialApp), matchesGoldenFile('snack_bar.goldenTest.backdropFilter.png')); }); - testWidgets('Floating snackbar can display optional icon', (WidgetTester tester) async { - await tester.pumpWidget(const MaterialApp( - home: Scaffold( - bottomSheet: SizedBox( - width: 200, - height: 50, - child: ColoredBox( - color: Colors.pink, - ), - ), - ), - )); - - final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger)); - scaffoldMessengerState.showSnackBar( - SnackBar( - content: const Text('Feeling snackish'), - duration: const Duration(seconds: 2), - action: SnackBarAction(label: 'ACTION', onPressed: () {}), - behavior: SnackBarBehavior.floating, - showCloseIcon: true, - ), - ); - await tester.pumpAndSettle(); // Have the SnackBar fully animate out. - - await expectLater( - find.byType(MaterialApp), - matchesGoldenFile( - 'snack_bar.goldenTest.floatingWithActionWithIcon.png')); - }); - - testWidgets('Fixed width snackbar can display optional icon', (WidgetTester tester) async { - await tester.pumpWidget(const MaterialApp( - home: Scaffold( - bottomSheet: SizedBox( - width: 200, - height: 50, - child: ColoredBox( - color: Colors.pink, - ), - ), - ), - )); - - final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger)); - scaffoldMessengerState.showSnackBar(SnackBar( - content: const Text('Go get a snack'), - duration: const Duration(seconds: 2), - action: SnackBarAction(label: 'ACTION', onPressed: () {}), - showCloseIcon: true, - behavior: SnackBarBehavior.fixed, - )); - await tester.pumpAndSettle(); // Have the SnackBar fully animate out. - - await expectLater(find.byType(MaterialApp), matchesGoldenFile('snack_bar.goldenTest.fixedWithActionWithIcon.png')); - }); - - testWidgets('Fixed snackbar can display optional icon without action', (WidgetTester tester) async { - await tester.pumpWidget(const MaterialApp( - home: Scaffold( - bottomSheet: SizedBox( - width: 200, - height: 50, - child: ColoredBox( - color: Colors.pink, - ), - ), - ), - )); - - final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger)); - scaffoldMessengerState.showSnackBar( - const SnackBar( - content: Text('I wonder if there are snacks nearby?'), - duration: Duration(seconds: 2), - behavior: SnackBarBehavior.fixed, - showCloseIcon: true, - ), - ); - await tester.pumpAndSettle(); // Have the SnackBar fully animate out. - - await expectLater(find.byType(MaterialApp), matchesGoldenFile('snack_bar.goldenTest.fixedWithIcon.png')); - }); - - testWidgets( - 'Floating width snackbar can display optional icon without action', (WidgetTester tester) async { - await tester.pumpWidget(const MaterialApp( - home: Scaffold( - bottomSheet: SizedBox( - width: 200, - height: 50, - child: ColoredBox( - color: Colors.pink, - ), - ), - ), - )); - - final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger)); - scaffoldMessengerState.showSnackBar(const SnackBar( - content: Text('Must go get a snack!'), - duration: Duration(seconds: 2), - showCloseIcon: true, - behavior: SnackBarBehavior.floating, - )); - await tester.pumpAndSettle(); // Have the SnackBar fully animate out. - - await expectLater(find.byType(MaterialApp), - matchesGoldenFile('snack_bar.goldenTest.floatingWithIcon.png')); - }); - - testWidgets('Fixed multi-line snackbar with icon is aligned correctly', (WidgetTester tester) async { - await tester.pumpWidget(const MaterialApp( - home: Scaffold( - bottomSheet: SizedBox( - width: 200, - height: 50, - child: ColoredBox( - color: Colors.pink, - ), - ), - ), - )); - - final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger)); - scaffoldMessengerState.showSnackBar(const SnackBar( - content: Text( - 'This is a really long snackbar message. So long, it spans across more than one line!'), - duration: Duration(seconds: 2), - showCloseIcon: true, - behavior: SnackBarBehavior.floating, - )); - await tester.pumpAndSettle(); // Have the SnackBar fully animate out. - - await expectLater(find.byType(MaterialApp), - matchesGoldenFile('snack_bar.goldenTest.multiLineWithIcon.png')); - }); - - testWidgets( - 'ScaffoldMessenger will alert for snackbars that cannot be presented', (WidgetTester tester) async { + testWidgets('ScaffoldMessenger will alert for snackbars that cannot be presented', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/103004 await tester.pumpWidget(const MaterialApp( home: Center(), diff --git a/packages/flutter/test/material/snack_bar_theme_test.dart b/packages/flutter/test/material/snack_bar_theme_test.dart index 2440c43f815d..7162b5df3b5c 100644 --- a/packages/flutter/test/material/snack_bar_theme_test.dart +++ b/packages/flutter/test/material/snack_bar_theme_test.dart @@ -22,9 +22,6 @@ void main() { expect(snackBarTheme.shape, null); expect(snackBarTheme.behavior, null); expect(snackBarTheme.width, null); - expect(snackBarTheme.insetPadding, null); - expect(snackBarTheme.showCloseIcon, null); - expect(snackBarTheme.closeIconColor, null); }); test( @@ -62,9 +59,6 @@ void main() { shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))), behavior: SnackBarBehavior.floating, width: 400.0, - insetPadding: EdgeInsets.all(10.0), - showCloseIcon: false, - closeIconColor: Color(0xFF0000AA), ).debugFillProperties(builder); final List description = builder.properties @@ -81,9 +75,6 @@ void main() { 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))', 'behavior: SnackBarBehavior.floating', 'width: 400.0', - 'insetPadding: EdgeInsets.all(10.0)', - 'showCloseIcon: false', - 'closeIconColor: Color(0xff0000aa)', ]); }); @@ -124,7 +115,7 @@ void main() { testWidgets('SnackBar uses values from SnackBarThemeData', (WidgetTester tester) async { const String text = 'I am a snack bar.'; const String action = 'ACTION'; - final SnackBarThemeData snackBarTheme = _snackBarTheme(showCloseIcon: true); + final SnackBarThemeData snackBarTheme = _snackBarTheme(); await tester.pumpWidget(MaterialApp( theme: ThemeData(snackBarTheme: snackBarTheme), @@ -153,14 +144,12 @@ void main() { final Material material = _getSnackBarMaterial(tester); final RenderParagraph button = _getSnackBarActionTextRenderObject(tester, action); final RenderParagraph content = _getSnackBarTextRenderObject(tester, text); - final Icon icon = _getSnackBarIcon(tester); expect(content.text.style, snackBarTheme.contentTextStyle); expect(material.color, snackBarTheme.backgroundColor); expect(material.elevation, snackBarTheme.elevation); expect(material.shape, snackBarTheme.shape); expect(button.text.style!.color, snackBarTheme.actionTextColor); - expect(icon.icon, Icons.close); }); testWidgets('SnackBar widget properties take priority over theme', (WidgetTester tester) async { @@ -174,7 +163,7 @@ void main() { const double snackBarWidth = 400.0; await tester.pumpWidget(MaterialApp( - theme: ThemeData(snackBarTheme: _snackBarTheme(showCloseIcon: true)), + theme: ThemeData(snackBarTheme: _snackBarTheme()), home: Scaffold( body: Builder( builder: (BuildContext context) { @@ -193,7 +182,6 @@ void main() { label: action, onPressed: () {}, ), - showCloseIcon: false, )); }, child: const Text('X'), @@ -216,7 +204,6 @@ void main() { expect(material.elevation, elevation); expect(material.shape, shape); expect(button.text.style!.color, textColor); - expect(_getSnackBarIconFinder(tester), findsNothing); // Assert width. final Offset snackBarBottomLeft = tester.getBottomLeft(materialFinder.first); final Offset snackBarBottomRight = tester.getBottomRight(materialFinder.first); @@ -227,7 +214,8 @@ void main() { testWidgets('SnackBar theme behavior is correct for floating', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( theme: ThemeData( - snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating)), + snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating), + ), home: Scaffold( floatingActionButton: FloatingActionButton( child: const Icon(Icons.send), @@ -401,14 +389,13 @@ void main() { }); } -SnackBarThemeData _snackBarTheme({bool? showCloseIcon}) { - return SnackBarThemeData( +SnackBarThemeData _snackBarTheme() { + return const SnackBarThemeData( backgroundColor: Colors.orange, actionTextColor: Colors.green, - contentTextStyle: const TextStyle(color: Colors.blue), + contentTextStyle: TextStyle(color: Colors.blue), elevation: 12.0, - showCloseIcon: showCloseIcon, - shape: const BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))), + shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))), ); } @@ -422,6 +409,7 @@ Finder _getSnackBarMaterialFinder(WidgetTester tester) { return find.descendant( of: find.byType(SnackBar), matching: find.byType(Material), + ); } @@ -432,17 +420,6 @@ RenderParagraph _getSnackBarActionTextRenderObject(WidgetTester tester, String t )); } -Icon _getSnackBarIcon(WidgetTester tester) { - return tester.widget(_getSnackBarIconFinder(tester)); -} - -Finder _getSnackBarIconFinder(WidgetTester tester) { - return find.descendant( - of: find.byType(SnackBar), - matching: find.byIcon(Icons.close), - ); -} - RenderParagraph _getSnackBarTextRenderObject(WidgetTester tester, String text) { return tester.renderObject(find.descendant( of: find.byType(SnackBar),