Revert "Update SnackBar to support Material 3" (#116199)

* Revert "Update SnackBar to support Material 3 (#115750)"

This reverts commit d58855c499.

* Kick ci.yaml

Co-authored-by: Jenn Magder <magder@google.com>
This commit is contained in:
Kate Lovett 2022-11-29 13:11:43 -06:00 committed by GitHub
parent d58855c499
commit 8b32ac7a51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 89 additions and 781 deletions

View file

@ -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<void> main(List<String> args) async {
ProgressIndicatorTemplate('ProgressIndicator', '$materialLib/progress_indicator.dart', tokens).updateFile();
RadioTemplate('Radio<T>', '$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();

View file

@ -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<MaterialState> 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;
}
''';
}

View file

@ -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<SnackBarExample> createState() => _SnackBarExampleState();
}
class _SnackBarExampleState extends State<SnackBarExample> {
SnackBarBehavior? _snackBarBehavior = SnackBarBehavior.floating;
bool _withIcon = true;
bool _withAction = true;
bool _multiLine = false;
bool _longActionLabel = false;
Padding _configRow(List<Widget> 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: <Widget>[
_configRow(<Widget>[
Text('Snack Bar configuration',
style: Theme.of(context).textTheme.bodyLarge),
]),
_configRow(
<Widget>[
const Text('Fixed'),
Radio<SnackBarBehavior>(
value: SnackBarBehavior.fixed,
groupValue: _snackBarBehavior,
onChanged: (SnackBarBehavior? value) {
setState(() {
_snackBarBehavior = value;
});
},
),
const Text('Floating'),
Radio<SnackBarBehavior>(
value: SnackBarBehavior.floating,
groupValue: _snackBarBehavior,
onChanged: (SnackBarBehavior? value) {
setState(() {
_snackBarBehavior = value;
});
},
),
],
),
_configRow(
<Widget>[
const Text('Include Icon '),
Switch(
value: _withIcon,
onChanged: (bool value) {
setState(() {
_withIcon = !_withIcon;
});
},
),
],
),
_configRow(
<Widget>[
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(
<Widget>[
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),
);
}
}

View file

@ -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<SnackBarAction> {
@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;
Color? resolveForegroundColor(Set<MaterialState> states) {
final SnackBarThemeData snackBarTheme = Theme.of(context).snackBarTheme;
if (states.contains(MaterialState.disabled)) {
return widget.disabledTextColor ?? snackBarTheme.disabledActionTextColor;
}
if (snackBarTheme.actionTextColor is MaterialStateColor) {
return snackBarTheme.actionTextColor! as MaterialStateColor;
}
if (defaults.actionTextColor is MaterialStateColor) {
return defaults.actionTextColor! as MaterialStateColor;
}
return MaterialStateColor.resolveWith((Set<MaterialState> 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<Color?>(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<SnackBar> {
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<SnackBar> {
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<Widget> maybeActionAndIcon = <Widget>[
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: <Widget>[
Row(
children: <Widget>[
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<SnackBar> {
);
}
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<SnackBar> {
);
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<SnackBar> {
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<SnackBar> {
);
}
}
// 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<MaterialState> 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

View file

@ -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<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<SnackBarBehavior>('behavior', behavior, defaultValue: null));
properties.add(DoubleProperty('width', width, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsets>('insetPadding', insetPadding, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('showCloseIcon', showCloseIcon, defaultValue: null));
properties.add(ColorProperty('closeIconColor', closeIconColor, defaultValue: null));
}
}

View file

@ -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]
///

View file

@ -316,7 +316,6 @@ void main() {
' TextButtonTheme\n'
' Padding\n'
' Row\n'
' Column\n'
' Padding\n'
' MediaQuery\n'
' Padding\n'

View file

@ -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(),

View file

@ -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<String> 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<Icon>(_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),