Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 76 additions & 20 deletions packages/flutter/lib/src/material/app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';

import 'app_bar_theme.dart';
import 'back_button.dart';
import 'color_scheme.dart';
import 'constants.dart';
import 'debug.dart';
import 'flexible_space_bar.dart';
Expand Down Expand Up @@ -196,6 +197,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this.shadowColor,
this.shape,
this.backgroundColor,
this.foregroundColor,
this.brightness,
this.iconTheme,
this.actionsIconTheme,
Expand Down Expand Up @@ -357,20 +359,68 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// zero.
final ShapeBorder? shape;

/// The color to use for the app bar's material. Typically this should be set
/// along with [brightness], [iconTheme], [textTheme].
/// The fill color to use for the app bar's [Material].
///
/// If this property is null, then [AppBarTheme.color] of
/// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryColor] is used.
/// If null, then the [AppBarTheme.color] is used. If that value is also
/// null, then [AppBar] uses the overall theme's [ColorScheme.primary] if the
/// overall theme's brightness is [Brightness.light], and [ColorScheme.surface]
/// if the overall theme's [brightness] is [Brightness.dark].
///
/// See also:
///
/// * [foregroundColor], which specifies the color for icons and text within
/// the app bar.
/// * [Theme.of], which returns the current overall Material theme as
/// a [ThemeData].
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
/// default colors are based on.
/// * [ColorScheme.brightness], which indicates if the overall [Theme]
/// is light or dark.
final Color? backgroundColor;

/// The brightness of the app bar's material. Typically this is set along
/// with [backgroundColor], [iconTheme], [textTheme].
/// The default color for [Text] and [Icon]s within the app bar.
///
/// If this property is null, then [AppBarTheme.brightness] of
/// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryColorBrightness] is used.
/// If null, then [AppBarTheme.foregroundColor] is used. If that
/// value is also null, then [AppBar] uses the overall theme's
/// [ColorScheme.onPrimary] if the overall theme's brightness is
/// [Brightness.light], and [ColorScheme.onSurface] if the overall
/// theme's [brightness] is [Brightness.dark].
///
/// This color is used to configure [DefaultTextStyle] that contains
/// the app bar's children, and the default [IconTheme] widgets that
/// are created if [iconTheme] and [actionsIconTheme] are null.
///
/// See also:
///
/// * [backgroundColor], which specifies the app bar's background color.
/// * [Theme.of], which returns the current overall Material theme as
/// a [ThemeData].
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
/// default colors are based on.
/// * [ColorScheme.brightness], which indicates if the overall [Theme]
/// is light or dark.
final Color? foregroundColor;

/// Determines the brightness of the [SystemUiOverlayStyle]: for
/// [Brightness.dark], [SystemUiOverlayStyle.light] is used and fo
/// [Brightness.light], [SystemUiOverlayStyle.dark] is used.
///
/// If this value is null then [AppBarTheme.brightness] is used
/// and if that's null then overall theme's brightness is used.
///
/// The AppBar is built within a `AnnotatedRegion<SystemUiOverlayStyle>`
/// which causes [SystemChrome.setSystemUIOverlayStyle] to be called
/// automatically. Apps should not enclose the AppBar with
/// their own [AnnotatedRegion].
///
/// See also:
///
/// * [Theme.of], which returns the current overall Material theme as
/// a [ThemeData].
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
/// default colors are based on.
/// * [ColorScheme.brightness], which indicates if the overall [Theme]
/// is light or dark.
final Brightness? brightness;

/// The color, opacity, and size to use for app bar icons. Typically this
Expand Down Expand Up @@ -499,6 +549,7 @@ class _AppBarState extends State<AppBar> {
assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
final AppBarTheme appBarTheme = AppBarTheme.of(context);
final ScaffoldState? scaffold = Scaffold.maybeOf(context);
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
Expand All @@ -510,18 +561,25 @@ class _AppBarState extends State<AppBar> {

final double toolbarHeight = widget.toolbarHeight ?? kToolbarHeight;

final Color backgroundColor = widget.backgroundColor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these color calculations use the brightness property of the appBar itself if it has one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. The existing AppBar/AppBarTheme brightness property is (and was) only used to set the SystemUiOverlayStyle. This is not correctly documented, I'll fix that.

In this case the brightness of theme's color scheme is used because we're choosing default colors that contrast with the theme's colors, which is also as things were.

?? appBarTheme.color
?? (colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary);
final Color foregroundColor = widget.foregroundColor
?? appBarTheme.foregroundColor
?? (colorScheme.brightness == Brightness.dark ? colorScheme.onSurface : colorScheme.onPrimary);

IconThemeData overallIconTheme = widget.iconTheme
?? appBarTheme.iconTheme
?? theme.primaryIconTheme;
?? theme.iconTheme.copyWith(color: foregroundColor);
IconThemeData actionsIconTheme = widget.actionsIconTheme
?? appBarTheme.actionsIconTheme
?? overallIconTheme;
TextStyle? centerStyle = widget.textTheme?.headline6
?? appBarTheme.textTheme?.headline6
?? theme.primaryTextTheme.headline6;
?? theme.primaryTextTheme.headline6?.copyWith(color: foregroundColor);
TextStyle? sideStyle = widget.textTheme?.bodyText2
?? appBarTheme.textTheme?.bodyText2
?? theme.primaryTextTheme.bodyText2;
?? theme.primaryTextTheme.bodyText2?.copyWith(color: foregroundColor);

if (widget.toolbarOpacity != 1.0) {
final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity);
Expand All @@ -530,10 +588,10 @@ class _AppBarState extends State<AppBar> {
if (sideStyle?.color != null)
sideStyle = sideStyle!.copyWith(color: sideStyle.color!.withOpacity(opacity));
overallIconTheme = overallIconTheme.copyWith(
opacity: opacity * (overallIconTheme.opacity ?? 1.0)
opacity: opacity * (overallIconTheme.opacity ?? 1.0),
);
actionsIconTheme = actionsIconTheme.copyWith(
opacity: opacity * (actionsIconTheme.opacity ?? 1.0)
opacity: opacity * (actionsIconTheme.opacity ?? 1.0),
);
}

Expand Down Expand Up @@ -707,21 +765,19 @@ class _AppBarState extends State<AppBar> {
],
);
}

final Brightness brightness = widget.brightness
?? appBarTheme.brightness
?? theme.primaryColorBrightness;
?? colorScheme.brightness;
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark;

return Semantics(
container: true,
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
child: Material(
color: widget.backgroundColor
?? appBarTheme.color
?? theme.primaryColor,
color: backgroundColor,
elevation: widget.elevation
?? appBarTheme.elevation
?? _defaultElevation,
Expand Down
65 changes: 61 additions & 4 deletions packages/flutter/lib/src/material/app_bar_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class AppBarTheme with Diagnosticable {
const AppBarTheme({
this.brightness,
this.color,
this.foregroundColor,
this.elevation,
this.shadowColor,
this.iconTheme,
Expand All @@ -42,16 +43,66 @@ class AppBarTheme with Diagnosticable {
this.titleSpacing,
});

/// Default value for [AppBar.brightness].
/// AppBar uses this value to determine the default (background) [color] and
/// [foregroundColor] as well as the app bar's [SystemUiOverlayStyle].
///
/// If null, [AppBar] uses [ThemeData.primaryColorBrightness].
/// For [Brightness.dark], [SystemUiOverlayStyle.light] is used and for
/// [Brightness.light], [SystemUiOverlayStyle.dark] is used.
///
/// See also:
///
/// * [AppBar.brightness], which overrides the this value and the overall
/// theme's [ColorScheme.brightness].
/// * [Theme.of], which returns the current overall Material theme as
/// a [ThemeData].
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
/// default colors are based on.
/// * [ColorScheme.brightness], which indicates if the overall [Theme]
/// is light or dark.
final Brightness? brightness;

/// Default value for [AppBar.backgroundColor].
/// The app bar's background color.
///
/// If null, [AppBar] uses the overall theme's [ColorScheme.primary] if the
/// overall theme's brightness is [Brightness.light], and [ColorScheme.surface]
/// if the overall theme's [brightness] is [Brightness.dark].
///
/// If null, [AppBar] uses [ThemeData.primaryColor].
/// See also:
///
/// * [AppBar.backgroundColor], which specifies the AppBar's background color
/// and overrides the background color defined by this theme.
/// * [foregroundColor], which specifies the color for icons and text within
/// the app bar.
/// * [Theme.of], which returns the current overall Material theme as
/// a [ThemeData].
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
/// default colors are based on.
/// * [ColorScheme.brightness], which indicates if the overall [Theme]
/// is light or dark.
final Color? color;

/// The default color for [Text] and [Icon]s within the app bar.
///
/// If null, [AppBar] uses the overall theme's [ColorScheme.onPrimary] if the
/// overall theme's brightness is [Brightness.light], and [ColorScheme.onSurface]
/// if the overall theme's [brightness] is [Brightness.dark].
///
/// This color is used to configure [DefaultTextStyle] and [IconTheme]
/// widgets.
///
/// See also:
///
/// * [AppBar.foregroundColor], which specifies the app bar's text and icon
/// colors and overrides the foreground color defined by this theme.
/// * [color], which specifies the app bar's background color.
/// * [Theme.of], which returns the current overall Material theme as
/// a [ThemeData].
/// * [ThemeData.colorScheme], the thirteen colors that most Material widget
/// default colors are based on.
/// * [ColorScheme.brightness], which indicates if the overall [Theme]
/// is light or dark.
final Color? foregroundColor;

/// Default value for [AppBar.elevation].
///
/// If null, [AppBar] uses a default value of 4.0.
Expand Down Expand Up @@ -93,6 +144,7 @@ class AppBarTheme with Diagnosticable {
IconThemeData? actionsIconTheme,
Brightness? brightness,
Color? color,
Color? foregroundColor,
double? elevation,
Color? shadowColor,
IconThemeData? iconTheme,
Expand All @@ -103,6 +155,7 @@ class AppBarTheme with Diagnosticable {
return AppBarTheme(
brightness: brightness ?? this.brightness,
color: color ?? this.color,
foregroundColor: foregroundColor ?? this.foregroundColor,
elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
iconTheme: iconTheme ?? this.iconTheme,
Expand All @@ -128,6 +181,7 @@ class AppBarTheme with Diagnosticable {
return AppBarTheme(
brightness: t < 0.5 ? a?.brightness : b?.brightness,
color: Color.lerp(a?.color, b?.color, t),
foregroundColor: Color.lerp(a?.foregroundColor, b?.foregroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
iconTheme: IconThemeData.lerp(a?.iconTheme, b?.iconTheme, t),
Expand All @@ -143,6 +197,7 @@ class AppBarTheme with Diagnosticable {
return hashValues(
brightness,
color,
foregroundColor,
elevation,
shadowColor,
iconTheme,
Expand All @@ -162,6 +217,7 @@ class AppBarTheme with Diagnosticable {
return other is AppBarTheme
&& other.brightness == brightness
&& other.color == color
&& other.foregroundColor == foregroundColor
&& other.elevation == elevation
&& other.shadowColor == shadowColor
&& other.iconTheme == iconTheme
Expand All @@ -176,6 +232,7 @@ class AppBarTheme with Diagnosticable {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Brightness>('brightness', brightness, defaultValue: null));
properties.add(ColorProperty('color', color, defaultValue: null));
properties.add(ColorProperty('foregroundColor', foregroundColor, 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));
Expand Down
14 changes: 9 additions & 5 deletions packages/flutter/test/material/app_bar_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,7 @@ void main() {
));

expect(darkTheme.primaryColorBrightness, Brightness.dark);
expect(darkTheme.colorScheme.brightness, Brightness.dark);
expect(SystemChrome.latestStyle, const SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.light,
Expand All @@ -1777,14 +1778,17 @@ void main() {

testWidgets('AppBar draws a dark system bar for a light background', (WidgetTester tester) async {
final ThemeData lightTheme = ThemeData(primaryColor: Colors.white);
await tester.pumpWidget(MaterialApp(
theme: lightTheme,
home: Scaffold(
appBar: AppBar(title: const Text('test')),
await tester.pumpWidget(
MaterialApp(
theme: lightTheme,
home: Scaffold(
appBar: AppBar(title: const Text('test')),
),
),
));
);

expect(lightTheme.primaryColorBrightness, Brightness.light);
expect(lightTheme.colorScheme.brightness, Brightness.light);
expect(SystemChrome.latestStyle, const SystemUiOverlayStyle(
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.dark,
Expand Down
Loading