Unverified Commit d9bf8794 authored by Anis Alibegić's avatar Anis Alibegić Committed by GitHub

Add shadowColor to AppBar and AppBarTheme (#58708)

parent 62024766
...@@ -175,7 +175,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -175,7 +175,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// and [automaticallyImplyLeading] must not be null. Additionally, if /// and [automaticallyImplyLeading] must not be null. Additionally, if
/// [elevation] is specified, it must be non-negative. /// [elevation] is specified, it must be non-negative.
/// ///
/// If [backgroundColor], [elevation], [brightness], [iconTheme], /// If [backgroundColor], [elevation], [shadowColor], [brightness], [iconTheme],
/// [actionsIconTheme], [textTheme] or [centerTitle] are null, then their /// [actionsIconTheme], [textTheme] or [centerTitle] are null, then their
/// [AppBarTheme] values will be used. If the corresponding [AppBarTheme] property is null, /// [AppBarTheme] values will be used. If the corresponding [AppBarTheme] property is null,
/// then the default specified in the property's documentation will be used. /// then the default specified in the property's documentation will be used.
...@@ -190,6 +190,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -190,6 +190,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this.flexibleSpace, this.flexibleSpace,
this.bottom, this.bottom,
this.elevation, this.elevation,
this.shadowColor,
this.shape, this.shape,
this.backgroundColor, this.backgroundColor,
this.brightness, this.brightness,
...@@ -338,6 +339,13 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -338,6 +339,13 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// for app bars. /// for app bars.
final double elevation; final double elevation;
/// The color to paint the shadow below the app bar.
///
/// If this property is null, then [ThemeData.appBarTheme.shadowColor] is used,
/// if that is also null, the default value is fully opaque black, the appropriate
/// color for shadows.
final Color shadowColor;
/// The material's shape as well its shadow. /// The material's shape as well its shadow.
/// ///
/// A shadow is only displayed if the [elevation] is greater than /// A shadow is only displayed if the [elevation] is greater than
...@@ -456,6 +464,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -456,6 +464,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
class _AppBarState extends State<AppBar> { class _AppBarState extends State<AppBar> {
static const double _defaultElevation = 4.0; static const double _defaultElevation = 4.0;
static const Color _defaultShadowColor = Color(0xFF000000);
void _handleDrawerButton() { void _handleDrawerButton() {
Scaffold.of(context).openDrawer(); Scaffold.of(context).openDrawer();
...@@ -665,6 +674,9 @@ class _AppBarState extends State<AppBar> { ...@@ -665,6 +674,9 @@ class _AppBarState extends State<AppBar> {
elevation: widget.elevation elevation: widget.elevation
?? appBarTheme.elevation ?? appBarTheme.elevation
?? _defaultElevation, ?? _defaultElevation,
shadowColor: widget.shadowColor
?? appBarTheme.shadowColor
?? _defaultShadowColor,
shape: widget.shape, shape: widget.shape,
child: Semantics( child: Semantics(
explicitChildNodes: true, explicitChildNodes: true,
...@@ -737,6 +749,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -737,6 +749,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@required this.flexibleSpace, @required this.flexibleSpace,
@required this.bottom, @required this.bottom,
@required this.elevation, @required this.elevation,
@required this.shadowColor,
@required this.forceElevated, @required this.forceElevated,
@required this.backgroundColor, @required this.backgroundColor,
@required this.brightness, @required this.brightness,
...@@ -765,6 +778,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -765,6 +778,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final Widget flexibleSpace; final Widget flexibleSpace;
final PreferredSizeWidget bottom; final PreferredSizeWidget bottom;
final double elevation; final double elevation;
final Color shadowColor;
final bool forceElevated; final bool forceElevated;
final Color backgroundColor; final Color backgroundColor;
final Brightness brightness; final Brightness brightness;
...@@ -822,6 +836,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -822,6 +836,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
: flexibleSpace, : flexibleSpace,
bottom: bottom, bottom: bottom,
elevation: forceElevated || overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent) ? elevation ?? 4.0 : 0.0, elevation: forceElevated || overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent) ? elevation ?? 4.0 : 0.0,
shadowColor: shadowColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
brightness: brightness, brightness: brightness,
iconTheme: iconTheme, iconTheme: iconTheme,
...@@ -849,6 +864,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -849,6 +864,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|| bottom != oldDelegate.bottom || bottom != oldDelegate.bottom
|| _bottomHeight != oldDelegate._bottomHeight || _bottomHeight != oldDelegate._bottomHeight
|| elevation != oldDelegate.elevation || elevation != oldDelegate.elevation
|| shadowColor != oldDelegate.shadowColor
|| backgroundColor != oldDelegate.backgroundColor || backgroundColor != oldDelegate.backgroundColor
|| brightness != oldDelegate.brightness || brightness != oldDelegate.brightness
|| iconTheme != oldDelegate.iconTheme || iconTheme != oldDelegate.iconTheme
...@@ -964,6 +980,7 @@ class SliverAppBar extends StatefulWidget { ...@@ -964,6 +980,7 @@ class SliverAppBar extends StatefulWidget {
this.flexibleSpace, this.flexibleSpace,
this.bottom, this.bottom,
this.elevation, this.elevation,
this.shadowColor,
this.forceElevated = false, this.forceElevated = false,
this.backgroundColor, this.backgroundColor,
this.brightness, this.brightness,
...@@ -1081,6 +1098,14 @@ class SliverAppBar extends StatefulWidget { ...@@ -1081,6 +1098,14 @@ class SliverAppBar extends StatefulWidget {
/// shadow is drawn, regardless of the value of [elevation]. /// shadow is drawn, regardless of the value of [elevation].
final double elevation; final double elevation;
/// The color to paint the shadow below the app bar. Typically this should be set
/// along with [elevation].
///
/// If this property is null, then [ThemeData.appBarTheme.shadowColor] is used,
/// if that is also null, the default value is fully opaque black, the appropriate
/// color for shadows.
final Color shadowColor;
/// Whether to show the shadow appropriate for the [elevation] even if the /// Whether to show the shadow appropriate for the [elevation] even if the
/// content is not scrolled under the [AppBar]. /// content is not scrolled under the [AppBar].
/// ///
...@@ -1339,6 +1364,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix ...@@ -1339,6 +1364,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
flexibleSpace: widget.flexibleSpace, flexibleSpace: widget.flexibleSpace,
bottom: widget.bottom, bottom: widget.bottom,
elevation: widget.elevation, elevation: widget.elevation,
shadowColor: widget.shadowColor,
forceElevated: widget.forceElevated, forceElevated: widget.forceElevated,
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
brightness: widget.brightness, brightness: widget.brightness,
......
...@@ -36,6 +36,7 @@ class AppBarTheme with Diagnosticable { ...@@ -36,6 +36,7 @@ class AppBarTheme with Diagnosticable {
this.brightness, this.brightness,
this.color, this.color,
this.elevation, this.elevation,
this.shadowColor,
this.iconTheme, this.iconTheme,
this.actionsIconTheme, this.actionsIconTheme,
this.textTheme, this.textTheme,
...@@ -57,6 +58,11 @@ class AppBarTheme with Diagnosticable { ...@@ -57,6 +58,11 @@ class AppBarTheme with Diagnosticable {
/// If null, [AppBar] uses a default value of 4.0. /// If null, [AppBar] uses a default value of 4.0.
final double elevation; final double elevation;
/// Default value for [AppBar.shadowColor].
///
/// If null, [AppBar] uses a default value of fully opaque black.
final Color shadowColor;
/// Default value for [AppBar.iconTheme]. /// Default value for [AppBar.iconTheme].
/// ///
/// If null, [AppBar] uses [ThemeData.primaryIconTheme]. /// If null, [AppBar] uses [ThemeData.primaryIconTheme].
...@@ -84,6 +90,7 @@ class AppBarTheme with Diagnosticable { ...@@ -84,6 +90,7 @@ class AppBarTheme with Diagnosticable {
Brightness brightness, Brightness brightness,
Color color, Color color,
double elevation, double elevation,
Color shadowColor,
IconThemeData iconTheme, IconThemeData iconTheme,
TextTheme textTheme, TextTheme textTheme,
bool centerTitle, bool centerTitle,
...@@ -92,6 +99,7 @@ class AppBarTheme with Diagnosticable { ...@@ -92,6 +99,7 @@ class AppBarTheme with Diagnosticable {
brightness: brightness ?? this.brightness, brightness: brightness ?? this.brightness,
color: color ?? this.color, color: color ?? this.color,
elevation: elevation ?? this.elevation, elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
iconTheme: iconTheme ?? this.iconTheme, iconTheme: iconTheme ?? this.iconTheme,
actionsIconTheme: actionsIconTheme ?? this.actionsIconTheme, actionsIconTheme: actionsIconTheme ?? this.actionsIconTheme,
textTheme: textTheme ?? this.textTheme, textTheme: textTheme ?? this.textTheme,
...@@ -115,6 +123,7 @@ class AppBarTheme with Diagnosticable { ...@@ -115,6 +123,7 @@ class AppBarTheme with Diagnosticable {
brightness: t < 0.5 ? a?.brightness : b?.brightness, brightness: t < 0.5 ? a?.brightness : b?.brightness,
color: Color.lerp(a?.color, b?.color, t), color: Color.lerp(a?.color, b?.color, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t), elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
iconTheme: IconThemeData.lerp(a?.iconTheme, b?.iconTheme, t), iconTheme: IconThemeData.lerp(a?.iconTheme, b?.iconTheme, t),
actionsIconTheme: IconThemeData.lerp(a?.actionsIconTheme, b?.actionsIconTheme, t), actionsIconTheme: IconThemeData.lerp(a?.actionsIconTheme, b?.actionsIconTheme, t),
textTheme: TextTheme.lerp(a?.textTheme, b?.textTheme, t), textTheme: TextTheme.lerp(a?.textTheme, b?.textTheme, t),
...@@ -128,6 +137,7 @@ class AppBarTheme with Diagnosticable { ...@@ -128,6 +137,7 @@ class AppBarTheme with Diagnosticable {
brightness, brightness,
color, color,
elevation, elevation,
shadowColor,
iconTheme, iconTheme,
actionsIconTheme, actionsIconTheme,
textTheme, textTheme,
...@@ -145,6 +155,7 @@ class AppBarTheme with Diagnosticable { ...@@ -145,6 +155,7 @@ class AppBarTheme with Diagnosticable {
&& other.brightness == brightness && other.brightness == brightness
&& other.color == color && other.color == color
&& other.elevation == elevation && other.elevation == elevation
&& other.shadowColor == shadowColor
&& other.iconTheme == iconTheme && other.iconTheme == iconTheme
&& other.actionsIconTheme == actionsIconTheme && other.actionsIconTheme == actionsIconTheme
&& other.textTheme == textTheme && other.textTheme == textTheme
...@@ -157,6 +168,7 @@ class AppBarTheme with Diagnosticable { ...@@ -157,6 +168,7 @@ class AppBarTheme with Diagnosticable {
properties.add(DiagnosticsProperty<Brightness>('brightness', brightness, defaultValue: null)); properties.add(DiagnosticsProperty<Brightness>('brightness', brightness, defaultValue: null));
properties.add(ColorProperty('color', color, defaultValue: null)); properties.add(ColorProperty('color', color, defaultValue: null));
properties.add(DiagnosticsProperty<double>('elevation', elevation, defaultValue: null)); properties.add(DiagnosticsProperty<double>('elevation', elevation, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(DiagnosticsProperty<IconThemeData>('iconTheme', iconTheme, defaultValue: null)); properties.add(DiagnosticsProperty<IconThemeData>('iconTheme', iconTheme, defaultValue: null));
properties.add(DiagnosticsProperty<IconThemeData>('actionsIconTheme', actionsIconTheme, defaultValue: null)); properties.add(DiagnosticsProperty<IconThemeData>('actionsIconTheme', actionsIconTheme, defaultValue: null));
properties.add(DiagnosticsProperty<TextTheme>('textTheme', textTheme, defaultValue: null)); properties.add(DiagnosticsProperty<TextTheme>('textTheme', textTheme, defaultValue: null));
......
...@@ -33,6 +33,7 @@ void main() { ...@@ -33,6 +33,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, Brightness.dark); expect(SystemChrome.latestStyle.statusBarBrightness, Brightness.dark);
expect(widget.color, Colors.blue); expect(widget.color, Colors.blue);
expect(widget.elevation, 4.0); expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(iconTheme.data, const IconThemeData(color: Colors.white)); expect(iconTheme.data, const IconThemeData(color: Colors.white));
expect(actionsIconTheme.data, const IconThemeData(color: Colors.white)); expect(actionsIconTheme.data, const IconThemeData(color: Colors.white));
expect(actionIconText.text.style.color, Colors.white); expect(actionIconText.text.style.color, Colors.white);
...@@ -61,6 +62,7 @@ void main() { ...@@ -61,6 +62,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, appBarTheme.brightness); expect(SystemChrome.latestStyle.statusBarBrightness, appBarTheme.brightness);
expect(widget.color, appBarTheme.color); expect(widget.color, appBarTheme.color);
expect(widget.elevation, appBarTheme.elevation); expect(widget.elevation, appBarTheme.elevation);
expect(widget.shadowColor, appBarTheme.shadowColor);
expect(iconTheme.data, appBarTheme.iconTheme); expect(iconTheme.data, appBarTheme.iconTheme);
expect(actionsIconTheme.data, appBarTheme.actionsIconTheme); expect(actionsIconTheme.data, appBarTheme.actionsIconTheme);
expect(actionIconText.text.style.color, appBarTheme.actionsIconTheme.color); expect(actionIconText.text.style.color, appBarTheme.actionsIconTheme.color);
...@@ -71,6 +73,7 @@ void main() { ...@@ -71,6 +73,7 @@ void main() {
const Brightness brightness = Brightness.dark; const Brightness brightness = Brightness.dark;
const Color color = Colors.orange; const Color color = Colors.orange;
const double elevation = 3.0; const double elevation = 3.0;
const Color shadowColor = Colors.red;
const IconThemeData iconThemeData = IconThemeData(color: Colors.green); const IconThemeData iconThemeData = IconThemeData(color: Colors.green);
const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.lightBlue); const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.lightBlue);
const TextTheme textTheme = TextTheme(headline6: TextStyle(color: Colors.orange), bodyText2: TextStyle(color: Colors.pink)); const TextTheme textTheme = TextTheme(headline6: TextStyle(color: Colors.orange), bodyText2: TextStyle(color: Colors.pink));
...@@ -83,6 +86,7 @@ void main() { ...@@ -83,6 +86,7 @@ void main() {
backgroundColor: color, backgroundColor: color,
brightness: brightness, brightness: brightness,
elevation: elevation, elevation: elevation,
shadowColor: shadowColor,
iconTheme: iconThemeData, iconTheme: iconThemeData,
actionsIconTheme: actionsIconThemeData, actionsIconTheme: actionsIconThemeData,
textTheme: textTheme, textTheme: textTheme,
...@@ -101,6 +105,7 @@ void main() { ...@@ -101,6 +105,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, brightness); expect(SystemChrome.latestStyle.statusBarBrightness, brightness);
expect(widget.color, color); expect(widget.color, color);
expect(widget.elevation, elevation); expect(widget.elevation, elevation);
expect(widget.shadowColor, shadowColor);
expect(iconTheme.data, iconThemeData); expect(iconTheme.data, iconThemeData);
expect(actionsIconTheme.data, actionsIconThemeData); expect(actionsIconTheme.data, actionsIconThemeData);
expect(actionIconText.text.style.color, actionsIconThemeData.color); expect(actionIconText.text.style.color, actionsIconThemeData.color);
...@@ -151,6 +156,7 @@ void main() { ...@@ -151,6 +156,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, appBarTheme.brightness); expect(SystemChrome.latestStyle.statusBarBrightness, appBarTheme.brightness);
expect(widget.color, appBarTheme.color); expect(widget.color, appBarTheme.color);
expect(widget.elevation, appBarTheme.elevation); expect(widget.elevation, appBarTheme.elevation);
expect(widget.shadowColor, appBarTheme.shadowColor);
expect(iconTheme.data, appBarTheme.iconTheme); expect(iconTheme.data, appBarTheme.iconTheme);
expect(actionsIconTheme.data, appBarTheme.actionsIconTheme); expect(actionsIconTheme.data, appBarTheme.actionsIconTheme);
expect(actionIconText.text.style.color, appBarTheme.actionsIconTheme.color); expect(actionIconText.text.style.color, appBarTheme.actionsIconTheme.color);
...@@ -178,6 +184,7 @@ void main() { ...@@ -178,6 +184,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, themeData.brightness); expect(SystemChrome.latestStyle.statusBarBrightness, themeData.brightness);
expect(widget.color, themeData.primaryColor); expect(widget.color, themeData.primaryColor);
expect(widget.elevation, 4.0); expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(iconTheme.data, themeData.primaryIconTheme); expect(iconTheme.data, themeData.primaryIconTheme);
expect(actionsIconTheme.data, themeData.primaryIconTheme); expect(actionsIconTheme.data, themeData.primaryIconTheme);
expect(actionIconText.text.style.color, themeData.primaryIconTheme.color); expect(actionIconText.text.style.color, themeData.primaryIconTheme.color);
...@@ -199,10 +206,11 @@ void main() { ...@@ -199,10 +206,11 @@ void main() {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: ThemeData(appBarTheme: const AppBarTheme(centerTitle: true)), theme: ThemeData(appBarTheme: const AppBarTheme(centerTitle: true)),
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Title'), title: const Text('Title'),
centerTitle: false, centerTitle: false,
)), ),
),
)); ));
final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar));
...@@ -221,12 +229,29 @@ void main() { ...@@ -221,12 +229,29 @@ void main() {
// the value of NavigationToolBar.centerMiddle should be true. // the value of NavigationToolBar.centerMiddle should be true.
expect(navToolBar.centerMiddle, true); expect(navToolBar.centerMiddle, true);
}); });
testWidgets('AppBar.shadowColor takes priority over AppBarTheme.shadowColor', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
theme: ThemeData(appBarTheme: const AppBarTheme(shadowColor: Colors.red)),
home: Scaffold(
appBar: AppBar(
title: const Text('Title'),
shadowColor: Colors.yellow,
),
),
));
final AppBar appBar = tester.widget(find.byType(AppBar));
// The AppBar.shadowColor should be used instead of AppBarTheme.shadowColor.
expect(appBar.shadowColor, Colors.yellow);
});
} }
AppBarTheme _appBarTheme() { AppBarTheme _appBarTheme() {
const Brightness brightness = Brightness.light; const Brightness brightness = Brightness.light;
const Color color = Colors.lightBlue; const Color color = Colors.lightBlue;
const double elevation = 6.0; const double elevation = 6.0;
const Color shadowColor = Colors.red;
const IconThemeData iconThemeData = IconThemeData(color: Colors.black); const IconThemeData iconThemeData = IconThemeData(color: Colors.black);
const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.pink); const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.pink);
const TextTheme textTheme = TextTheme(bodyText2: TextStyle(color: Colors.yellow)); const TextTheme textTheme = TextTheme(bodyText2: TextStyle(color: Colors.yellow));
...@@ -235,6 +260,7 @@ AppBarTheme _appBarTheme() { ...@@ -235,6 +260,7 @@ AppBarTheme _appBarTheme() {
brightness: brightness, brightness: brightness,
color: color, color: color,
elevation: elevation, elevation: elevation,
shadowColor: shadowColor,
iconTheme: iconThemeData, iconTheme: iconThemeData,
textTheme: textTheme, textTheme: textTheme,
); );
...@@ -284,6 +310,7 @@ RichText _getAppBarIconRichText(WidgetTester tester) { ...@@ -284,6 +310,7 @@ RichText _getAppBarIconRichText(WidgetTester tester) {
).first, ).first,
); );
} }
DefaultTextStyle _getAppBarText(WidgetTester tester) { DefaultTextStyle _getAppBarText(WidgetTester tester) {
return tester.widget<DefaultTextStyle>( return tester.widget<DefaultTextStyle>(
find.descendant( find.descendant(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment