Add shadowColor to AppBar and AppBarTheme (#58708)

This commit is contained in:
Anis Alibegić 2020-06-15 18:30:08 +02:00 committed by GitHub
parent 6202476691
commit d9bf8794c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 5 deletions

View file

@ -175,7 +175,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// and [automaticallyImplyLeading] must not be null. Additionally, if
/// [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
/// [AppBarTheme] values will be used. If the corresponding [AppBarTheme] property is null,
/// then the default specified in the property's documentation will be used.
@ -190,6 +190,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this.flexibleSpace,
this.bottom,
this.elevation,
this.shadowColor,
this.shape,
this.backgroundColor,
this.brightness,
@ -338,6 +339,13 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// for app bars.
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.
///
/// A shadow is only displayed if the [elevation] is greater than
@ -456,6 +464,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
class _AppBarState extends State<AppBar> {
static const double _defaultElevation = 4.0;
static const Color _defaultShadowColor = Color(0xFF000000);
void _handleDrawerButton() {
Scaffold.of(context).openDrawer();
@ -665,6 +674,9 @@ class _AppBarState extends State<AppBar> {
elevation: widget.elevation
?? appBarTheme.elevation
?? _defaultElevation,
shadowColor: widget.shadowColor
?? appBarTheme.shadowColor
?? _defaultShadowColor,
shape: widget.shape,
child: Semantics(
explicitChildNodes: true,
@ -737,6 +749,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@required this.flexibleSpace,
@required this.bottom,
@required this.elevation,
@required this.shadowColor,
@required this.forceElevated,
@required this.backgroundColor,
@required this.brightness,
@ -765,6 +778,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final Widget flexibleSpace;
final PreferredSizeWidget bottom;
final double elevation;
final Color shadowColor;
final bool forceElevated;
final Color backgroundColor;
final Brightness brightness;
@ -822,6 +836,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
: flexibleSpace,
bottom: bottom,
elevation: forceElevated || overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent) ? elevation ?? 4.0 : 0.0,
shadowColor: shadowColor,
backgroundColor: backgroundColor,
brightness: brightness,
iconTheme: iconTheme,
@ -849,6 +864,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|| bottom != oldDelegate.bottom
|| _bottomHeight != oldDelegate._bottomHeight
|| elevation != oldDelegate.elevation
|| shadowColor != oldDelegate.shadowColor
|| backgroundColor != oldDelegate.backgroundColor
|| brightness != oldDelegate.brightness
|| iconTheme != oldDelegate.iconTheme
@ -964,6 +980,7 @@ class SliverAppBar extends StatefulWidget {
this.flexibleSpace,
this.bottom,
this.elevation,
this.shadowColor,
this.forceElevated = false,
this.backgroundColor,
this.brightness,
@ -1081,6 +1098,14 @@ class SliverAppBar extends StatefulWidget {
/// shadow is drawn, regardless of the value of [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
/// content is not scrolled under the [AppBar].
///
@ -1339,6 +1364,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
flexibleSpace: widget.flexibleSpace,
bottom: widget.bottom,
elevation: widget.elevation,
shadowColor: widget.shadowColor,
forceElevated: widget.forceElevated,
backgroundColor: widget.backgroundColor,
brightness: widget.brightness,

View file

@ -36,6 +36,7 @@ class AppBarTheme with Diagnosticable {
this.brightness,
this.color,
this.elevation,
this.shadowColor,
this.iconTheme,
this.actionsIconTheme,
this.textTheme,
@ -57,6 +58,11 @@ class AppBarTheme with Diagnosticable {
/// If null, [AppBar] uses a default value of 4.0.
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].
///
/// If null, [AppBar] uses [ThemeData.primaryIconTheme].
@ -84,6 +90,7 @@ class AppBarTheme with Diagnosticable {
Brightness brightness,
Color color,
double elevation,
Color shadowColor,
IconThemeData iconTheme,
TextTheme textTheme,
bool centerTitle,
@ -92,6 +99,7 @@ class AppBarTheme with Diagnosticable {
brightness: brightness ?? this.brightness,
color: color ?? this.color,
elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
iconTheme: iconTheme ?? this.iconTheme,
actionsIconTheme: actionsIconTheme ?? this.actionsIconTheme,
textTheme: textTheme ?? this.textTheme,
@ -115,6 +123,7 @@ class AppBarTheme with Diagnosticable {
brightness: t < 0.5 ? a?.brightness : b?.brightness,
color: Color.lerp(a?.color, b?.color, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
iconTheme: IconThemeData.lerp(a?.iconTheme, b?.iconTheme, t),
actionsIconTheme: IconThemeData.lerp(a?.actionsIconTheme, b?.actionsIconTheme, t),
textTheme: TextTheme.lerp(a?.textTheme, b?.textTheme, t),
@ -128,6 +137,7 @@ class AppBarTheme with Diagnosticable {
brightness,
color,
elevation,
shadowColor,
iconTheme,
actionsIconTheme,
textTheme,
@ -145,6 +155,7 @@ class AppBarTheme with Diagnosticable {
&& other.brightness == brightness
&& other.color == color
&& other.elevation == elevation
&& other.shadowColor == shadowColor
&& other.iconTheme == iconTheme
&& other.actionsIconTheme == actionsIconTheme
&& other.textTheme == textTheme
@ -157,6 +168,7 @@ class AppBarTheme with Diagnosticable {
properties.add(DiagnosticsProperty<Brightness>('brightness', brightness, defaultValue: null));
properties.add(ColorProperty('color', color, 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>('actionsIconTheme', actionsIconTheme, defaultValue: null));
properties.add(DiagnosticsProperty<TextTheme>('textTheme', textTheme, defaultValue: null));

View file

@ -33,6 +33,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, Brightness.dark);
expect(widget.color, Colors.blue);
expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(iconTheme.data, const IconThemeData(color: Colors.white));
expect(actionsIconTheme.data, const IconThemeData(color: Colors.white));
expect(actionIconText.text.style.color, Colors.white);
@ -61,6 +62,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, appBarTheme.brightness);
expect(widget.color, appBarTheme.color);
expect(widget.elevation, appBarTheme.elevation);
expect(widget.shadowColor, appBarTheme.shadowColor);
expect(iconTheme.data, appBarTheme.iconTheme);
expect(actionsIconTheme.data, appBarTheme.actionsIconTheme);
expect(actionIconText.text.style.color, appBarTheme.actionsIconTheme.color);
@ -71,6 +73,7 @@ void main() {
const Brightness brightness = Brightness.dark;
const Color color = Colors.orange;
const double elevation = 3.0;
const Color shadowColor = Colors.red;
const IconThemeData iconThemeData = IconThemeData(color: Colors.green);
const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.lightBlue);
const TextTheme textTheme = TextTheme(headline6: TextStyle(color: Colors.orange), bodyText2: TextStyle(color: Colors.pink));
@ -83,6 +86,7 @@ void main() {
backgroundColor: color,
brightness: brightness,
elevation: elevation,
shadowColor: shadowColor,
iconTheme: iconThemeData,
actionsIconTheme: actionsIconThemeData,
textTheme: textTheme,
@ -101,6 +105,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, brightness);
expect(widget.color, color);
expect(widget.elevation, elevation);
expect(widget.shadowColor, shadowColor);
expect(iconTheme.data, iconThemeData);
expect(actionsIconTheme.data, actionsIconThemeData);
expect(actionIconText.text.style.color, actionsIconThemeData.color);
@ -151,6 +156,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, appBarTheme.brightness);
expect(widget.color, appBarTheme.color);
expect(widget.elevation, appBarTheme.elevation);
expect(widget.shadowColor, appBarTheme.shadowColor);
expect(iconTheme.data, appBarTheme.iconTheme);
expect(actionsIconTheme.data, appBarTheme.actionsIconTheme);
expect(actionIconText.text.style.color, appBarTheme.actionsIconTheme.color);
@ -178,6 +184,7 @@ void main() {
expect(SystemChrome.latestStyle.statusBarBrightness, themeData.brightness);
expect(widget.color, themeData.primaryColor);
expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(iconTheme.data, themeData.primaryIconTheme);
expect(actionsIconTheme.data, themeData.primaryIconTheme);
expect(actionIconText.text.style.color, themeData.primaryIconTheme.color);
@ -199,10 +206,11 @@ void main() {
await tester.pumpWidget(MaterialApp(
theme: ThemeData(appBarTheme: const AppBarTheme(centerTitle: true)),
home: Scaffold(
appBar: AppBar(
title: const Text('Title'),
centerTitle: false,
)),
appBar: AppBar(
title: const Text('Title'),
centerTitle: false,
),
),
));
final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar));
@ -221,12 +229,29 @@ void main() {
// the value of NavigationToolBar.centerMiddle should be 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() {
const Brightness brightness = Brightness.light;
const Color color = Colors.lightBlue;
const double elevation = 6.0;
const Color shadowColor = Colors.red;
const IconThemeData iconThemeData = IconThemeData(color: Colors.black);
const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.pink);
const TextTheme textTheme = TextTheme(bodyText2: TextStyle(color: Colors.yellow));
@ -235,6 +260,7 @@ AppBarTheme _appBarTheme() {
brightness: brightness,
color: color,
elevation: elevation,
shadowColor: shadowColor,
iconTheme: iconThemeData,
textTheme: textTheme,
);
@ -284,6 +310,7 @@ RichText _getAppBarIconRichText(WidgetTester tester) {
).first,
);
}
DefaultTextStyle _getAppBarText(WidgetTester tester) {
return tester.widget<DefaultTextStyle>(
find.descendant(