Unverified Commit 14a9b4a7 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Migrate AppBar to Material 3 (#101884)

parent 0c3c38dc
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:gen_defaults/app_bar_template.dart';
import 'package:gen_defaults/button_template.dart'; import 'package:gen_defaults/button_template.dart';
import 'package:gen_defaults/card_template.dart'; import 'package:gen_defaults/card_template.dart';
import 'package:gen_defaults/dialog_template.dart'; import 'package:gen_defaults/dialog_template.dart';
...@@ -78,6 +79,7 @@ Future<void> main(List<String> args) async { ...@@ -78,6 +79,7 @@ Future<void> main(List<String> args) async {
tokens['colorsLight'] = _readTokenFile('color_light.json'); tokens['colorsLight'] = _readTokenFile('color_light.json');
tokens['colorsDark'] = _readTokenFile('color_dark.json'); tokens['colorsDark'] = _readTokenFile('color_dark.json');
AppBarTemplate('$materialLib/app_bar.dart', tokens).updateFile();
ButtonTemplate('md.comp.elevated-button', '$materialLib/elevated_button.dart', tokens).updateFile(); ButtonTemplate('md.comp.elevated-button', '$materialLib/elevated_button.dart', tokens).updateFile();
ButtonTemplate('md.comp.outlined-button', '$materialLib/outlined_button.dart', tokens).updateFile(); ButtonTemplate('md.comp.outlined-button', '$materialLib/outlined_button.dart', tokens).updateFile();
ButtonTemplate('md.comp.text-button', '$materialLib/text_button.dart', tokens).updateFile(); ButtonTemplate('md.comp.text-button', '$materialLib/text_button.dart', tokens).updateFile();
......
// 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 AppBarTemplate extends TokenTemplate {
const AppBarTemplate(super.fileName, super.tokens)
: super(
colorSchemePrefix: '_colors.',
textThemePrefix: '_textTheme.',
);
@override
String generate() => '''
// Generated version ${tokens["version"]}
class _TokenDefaultsM3 extends AppBarTheme {
_TokenDefaultsM3(this.context)
: super(
elevation: ${elevation('md.comp.top-app-bar.small.container')},
scrolledUnderElevation: ${elevation('md.comp.top-app-bar.small.on-scroll.container')},
titleSpacing: NavigationToolbar.kMiddleSpacing,
toolbarHeight: ${tokens['md.comp.top-app-bar.small.container.height']},
);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
late final TextTheme _textTheme = _theme.textTheme;
@override
Color? get backgroundColor => ${componentColor('md.comp.top-app-bar.small.container')};
@override
Color? get foregroundColor => ${color('md.comp.top-app-bar.small.headline.color')};
@override
Color? get surfaceTintColor => ${componentColor('md.comp.top-app-bar.small.container.surface-tint-layer')};
@override
IconThemeData? get iconTheme => IconThemeData(
color: ${componentColor('md.comp.top-app-bar.small.leading-icon')},
size: ${tokens['md.comp.top-app-bar.small.leading-icon.size']},
);
@override
IconThemeData? get actionsIconTheme => IconThemeData(
color: ${componentColor('md.comp.top-app-bar.small.trailing-icon')},
size: ${tokens['md.comp.top-app-bar.small.trailing-icon.size']},
);
@override
TextStyle? get toolbarTextStyle => _textTheme.bodyText2;
@override
TextStyle? get titleTextStyle => ${textStyle('md.comp.top-app-bar.small.headline')};
}''';
}
...@@ -161,7 +161,9 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -161,7 +161,9 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this.flexibleSpace, this.flexibleSpace,
this.bottom, this.bottom,
this.elevation, this.elevation,
this.scrolledUnderElevation,
this.shadowColor, this.shadowColor,
this.surfaceTintColor,
this.shape, this.shape,
this.backgroundColor, this.backgroundColor,
this.foregroundColor, this.foregroundColor,
...@@ -372,7 +374,12 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -372,7 +374,12 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// {@template flutter.material.appbar.elevation} /// {@template flutter.material.appbar.elevation}
/// The z-coordinate at which to place this app bar relative to its parent. /// The z-coordinate at which to place this app bar relative to its parent.
/// ///
/// This property controls the size of the shadow below the app bar. /// This property controls the size of the shadow below the app bar if
/// [shadowColor] is not null.
///
/// If [surfaceTintColor] is not null then it will apply a surface tint overlay
/// to the background color (see [Material.surfaceTintColor] for more
/// detail).
/// ///
/// The value must be non-negative. /// The value must be non-negative.
/// ///
...@@ -383,11 +390,37 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -383,11 +390,37 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// ///
/// See also: /// See also:
/// ///
/// * [scrolledUnderElevation], which will be used when the app bar has
/// something scrolled underneath it.
/// * [shadowColor], which is the color of the shadow below the app bar. /// * [shadowColor], which is the color of the shadow below the app bar.
/// * [surfaceTintColor], which determines the elevation overlay that will
/// be applied to the background of the app bar.
/// * [shape], which defines the shape of the app bar's [Material] and its /// * [shape], which defines the shape of the app bar's [Material] and its
/// shadow. /// shadow.
final double? elevation; final double? elevation;
/// {@template flutter.material.appbar.scrolledUnderElevation}
/// The elevation that will be used if this app bar has something
/// scrolled underneath it.
///
/// If non-null then it [AppBarTheme.scrolledUnderElevation] of
/// [ThemeData.appBarTheme] will be used. If that is also null then [elevation]
/// will be used.
///
/// The value must be non-negative.
///
/// {@endtemplate}
///
/// See also:
/// * [elevation], which will be used if there is no content scrolled under
/// the app bar.
/// * [shadowColor], which is the color of the shadow below the app bar.
/// * [surfaceTintColor], which determines the elevation overlay that will
/// be applied to the background of the app bar.
/// * [shape], which defines the shape of the app bar's [Material] and its
/// shadow.
final double? scrolledUnderElevation;
/// {@template flutter.material.appbar.shadowColor} /// {@template flutter.material.appbar.shadowColor}
/// The color of the shadow below the app bar. /// The color of the shadow below the app bar.
/// ///
...@@ -402,6 +435,17 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -402,6 +435,17 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// * [shape], which defines the shape of the app bar and its shadow. /// * [shape], which defines the shape of the app bar and its shadow.
final Color? shadowColor; final Color? shadowColor;
/// {@template flutter.material.appbar.surfaceTintColor}
/// The color of the surface tint overlay applied to the app bar's
/// background color to indicate elevation.
///
/// If null no overlay will be applied.
/// {@endtemplate}
///
/// See also:
/// * [Material.surfaceTintColor], which described this feature in more detail.
final Color? surfaceTintColor;
/// {@template flutter.material.appbar.shape} /// {@template flutter.material.appbar.shape}
/// The shape of the app bar's [Material] as well as its shadow. /// The shape of the app bar's [Material] as well as its shadow.
/// ///
...@@ -709,23 +753,24 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -709,23 +753,24 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// * [SystemChrome.setSystemUIOverlayStyle] /// * [SystemChrome.setSystemUIOverlayStyle]
final SystemUiOverlayStyle? systemOverlayStyle; final SystemUiOverlayStyle? systemOverlayStyle;
bool _getEffectiveCenterTitle(ThemeData theme) { bool _getEffectiveCenterTitle(ThemeData theme) {
if (centerTitle != null) bool platformCenter() {
return centerTitle!; assert(theme.platform != null);
if (theme.appBarTheme.centerTitle != null) switch (theme.platform) {
return theme.appBarTheme.centerTitle!; case TargetPlatform.android:
assert(theme.platform != null); case TargetPlatform.fuchsia:
switch (theme.platform) { case TargetPlatform.linux:
case TargetPlatform.android: case TargetPlatform.windows:
case TargetPlatform.fuchsia: return false;
case TargetPlatform.linux: case TargetPlatform.iOS:
case TargetPlatform.windows: case TargetPlatform.macOS:
return false; return actions == null || actions!.length < 2;
case TargetPlatform.iOS: }
case TargetPlatform.macOS:
return actions == null || actions!.length < 2;
} }
return centerTitle
?? theme.appBarTheme.centerTitle
?? platformCenter();
} }
@override @override
...@@ -733,9 +778,6 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -733,9 +778,6 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
} }
class _AppBarState extends State<AppBar> { class _AppBarState extends State<AppBar> {
static const double _defaultElevation = 4.0;
static const Color _defaultShadowColor = Color(0xFF000000);
ScrollNotificationObserverState? _scrollNotificationObserver; ScrollNotificationObserverState? _scrollNotificationObserver;
bool _scrolledUnder = false; bool _scrolledUnder = false;
...@@ -795,8 +837,8 @@ class _AppBarState extends State<AppBar> { ...@@ -795,8 +837,8 @@ class _AppBarState extends State<AppBar> {
assert(!widget.primary || debugCheckHasMediaQuery(context)); assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
final AppBarTheme appBarTheme = AppBarTheme.of(context); final AppBarTheme appBarTheme = AppBarTheme.of(context);
final AppBarTheme defaults = theme.useMaterial3 ? _TokenDefaultsM3(context) : _DefaultsM2(context);
final ScaffoldState? scaffold = Scaffold.maybeOf(context); final ScaffoldState? scaffold = Scaffold.maybeOf(context);
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context); final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
...@@ -821,12 +863,23 @@ class _AppBarState extends State<AppBar> { ...@@ -821,12 +863,23 @@ class _AppBarState extends State<AppBar> {
states, states,
widget.backgroundColor, widget.backgroundColor,
appBarTheme.backgroundColor, appBarTheme.backgroundColor,
colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary, defaults.backgroundColor!,
); );
final Color foregroundColor = widget.foregroundColor final Color foregroundColor = widget.foregroundColor
?? appBarTheme.foregroundColor ?? appBarTheme.foregroundColor
?? (colorScheme.brightness == Brightness.dark ? colorScheme.onSurface : colorScheme.onPrimary); ?? defaults.foregroundColor!;
final double elevation = widget.elevation
?? appBarTheme.elevation
?? defaults.elevation!;
final double effectiveElevation = states.contains(MaterialState.scrolledUnder)
? widget.scrolledUnderElevation
?? appBarTheme.scrolledUnderElevation
?? defaults.scrolledUnderElevation
?? elevation
: elevation;
IconThemeData overallIconTheme = backwardsCompatibility IconThemeData overallIconTheme = backwardsCompatibility
? widget.iconTheme ? widget.iconTheme
...@@ -834,10 +887,13 @@ class _AppBarState extends State<AppBar> { ...@@ -834,10 +887,13 @@ class _AppBarState extends State<AppBar> {
?? theme.primaryIconTheme ?? theme.primaryIconTheme
: widget.iconTheme : widget.iconTheme
?? appBarTheme.iconTheme ?? appBarTheme.iconTheme
?? theme.iconTheme.copyWith(color: foregroundColor); ?? defaults.iconTheme!.copyWith(color: foregroundColor);
IconThemeData actionsIconTheme = widget.actionsIconTheme IconThemeData actionsIconTheme = widget.actionsIconTheme
?? appBarTheme.actionsIconTheme ?? appBarTheme.actionsIconTheme
?? widget.iconTheme
?? appBarTheme.iconTheme
?? defaults.actionsIconTheme?.copyWith(color: foregroundColor)
?? overallIconTheme; ?? overallIconTheme;
TextStyle? toolbarTextStyle = backwardsCompatibility TextStyle? toolbarTextStyle = backwardsCompatibility
...@@ -846,7 +902,7 @@ class _AppBarState extends State<AppBar> { ...@@ -846,7 +902,7 @@ class _AppBarState extends State<AppBar> {
?? theme.primaryTextTheme.bodyText2 ?? theme.primaryTextTheme.bodyText2
: widget.toolbarTextStyle : widget.toolbarTextStyle
?? appBarTheme.toolbarTextStyle ?? appBarTheme.toolbarTextStyle
?? theme.textTheme.bodyText2?.copyWith(color: foregroundColor); ?? defaults.toolbarTextStyle?.copyWith(color: foregroundColor);
TextStyle? titleTextStyle = backwardsCompatibility TextStyle? titleTextStyle = backwardsCompatibility
? widget.textTheme?.headline6 ? widget.textTheme?.headline6
...@@ -854,7 +910,7 @@ class _AppBarState extends State<AppBar> { ...@@ -854,7 +910,7 @@ class _AppBarState extends State<AppBar> {
?? theme.primaryTextTheme.headline6 ?? theme.primaryTextTheme.headline6
: widget.titleTextStyle : widget.titleTextStyle
?? appBarTheme.titleTextStyle ?? appBarTheme.titleTextStyle
?? theme.textTheme.headline6?.copyWith(color: foregroundColor); ?? defaults.titleTextStyle?.copyWith(color: foregroundColor);
if (widget.toolbarOpacity != 1.0) { if (widget.toolbarOpacity != 1.0) {
final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity); final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity);
...@@ -1050,6 +1106,7 @@ class _AppBarState extends State<AppBar> { ...@@ -1050,6 +1106,7 @@ class _AppBarState extends State<AppBar> {
) )
: widget.systemOverlayStyle : widget.systemOverlayStyle
?? appBarTheme.systemOverlayStyle ?? appBarTheme.systemOverlayStyle
?? defaults.systemOverlayStyle
?? _systemOverlayStyleForBrightness(ThemeData.estimateBrightnessForColor(backgroundColor)); ?? _systemOverlayStyleForBrightness(ThemeData.estimateBrightnessForColor(backgroundColor));
return Semantics( return Semantics(
...@@ -1058,13 +1115,14 @@ class _AppBarState extends State<AppBar> { ...@@ -1058,13 +1115,14 @@ class _AppBarState extends State<AppBar> {
value: overlayStyle, value: overlayStyle,
child: Material( child: Material(
color: backgroundColor, color: backgroundColor,
elevation: widget.elevation elevation: effectiveElevation,
?? appBarTheme.elevation
?? _defaultElevation,
shadowColor: widget.shadowColor shadowColor: widget.shadowColor
?? appBarTheme.shadowColor ?? appBarTheme.shadowColor
?? _defaultShadowColor, ?? defaults.shadowColor,
shape: widget.shape ?? appBarTheme.shape, surfaceTintColor: widget.surfaceTintColor
?? appBarTheme.surfaceTintColor
?? defaults.surfaceTintColor,
shape: widget.shape ?? appBarTheme.shape ?? defaults.shape,
child: Semantics( child: Semantics(
explicitChildNodes: true, explicitChildNodes: true,
child: appBar, child: appBar,
...@@ -1084,7 +1142,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -1084,7 +1142,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
required this.flexibleSpace, required this.flexibleSpace,
required this.bottom, required this.bottom,
required this.elevation, required this.elevation,
required this.scrolledUnderElevation,
required this.shadowColor, required this.shadowColor,
required this.surfaceTintColor,
required this.forceElevated, required this.forceElevated,
required this.backgroundColor, required this.backgroundColor,
required this.foregroundColor, required this.foregroundColor,
...@@ -1126,7 +1186,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -1126,7 +1186,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final Widget? flexibleSpace; final Widget? flexibleSpace;
final PreferredSizeWidget? bottom; final PreferredSizeWidget? bottom;
final double? elevation; final double? elevation;
final double? scrolledUnderElevation;
final Color? shadowColor; final Color? shadowColor;
final Color? surfaceTintColor;
final bool forceElevated; final bool forceElevated;
final Color? backgroundColor; final Color? backgroundColor;
final Color? foregroundColor; final Color? foregroundColor;
...@@ -1201,7 +1263,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { ...@@ -1201,7 +1263,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
: flexibleSpace, : flexibleSpace,
bottom: bottom, bottom: bottom,
elevation: forceElevated || isScrolledUnder ? elevation : 0.0, elevation: forceElevated || isScrolledUnder ? elevation : 0.0,
scrolledUnderElevation: scrolledUnderElevation,
shadowColor: shadowColor, shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
foregroundColor: foregroundColor, foregroundColor: foregroundColor,
brightness: brightness, brightness: brightness,
...@@ -1368,7 +1432,9 @@ class SliverAppBar extends StatefulWidget { ...@@ -1368,7 +1432,9 @@ class SliverAppBar extends StatefulWidget {
this.flexibleSpace, this.flexibleSpace,
this.bottom, this.bottom,
this.elevation, this.elevation,
this.scrolledUnderElevation,
this.shadowColor, this.shadowColor,
this.surfaceTintColor,
this.forceElevated = false, this.forceElevated = false,
this.backgroundColor, this.backgroundColor,
this.foregroundColor, this.foregroundColor,
...@@ -1454,11 +1520,21 @@ class SliverAppBar extends StatefulWidget { ...@@ -1454,11 +1520,21 @@ class SliverAppBar extends StatefulWidget {
/// This property is used to configure an [AppBar]. /// This property is used to configure an [AppBar].
final double? elevation; final double? elevation;
/// {@macro flutter.material.appbar.scrolledUnderElevation}
///
/// This property is used to configure an [AppBar].
final double? scrolledUnderElevation;
/// {@macro flutter.material.appbar.shadowColor} /// {@macro flutter.material.appbar.shadowColor}
/// ///
/// This property is used to configure an [AppBar]. /// This property is used to configure an [AppBar].
final Color? shadowColor; final Color? shadowColor;
/// {@macro flutter.material.appbar.surfaceTintColor}
///
/// This property is used to configure an [AppBar].
final Color? surfaceTintColor;
/// 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].
/// ///
...@@ -1760,7 +1836,9 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix ...@@ -1760,7 +1836,9 @@ 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,
scrolledUnderElevation: widget.scrolledUnderElevation,
shadowColor: widget.shadowColor, shadowColor: widget.shadowColor,
surfaceTintColor: widget.surfaceTintColor,
forceElevated: widget.forceElevated, forceElevated: widget.forceElevated,
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
foregroundColor: widget.foregroundColor, foregroundColor: widget.foregroundColor,
...@@ -1832,3 +1910,82 @@ class _RenderAppBarTitleBox extends RenderAligningShiftedBox { ...@@ -1832,3 +1910,82 @@ class _RenderAppBarTitleBox extends RenderAligningShiftedBox {
alignChild(); alignChild();
} }
} }
class _DefaultsM2 extends AppBarTheme {
_DefaultsM2(this.context)
: super(
elevation: 4.0,
shadowColor: const Color(0xFF000000),
titleSpacing: NavigationToolbar.kMiddleSpacing,
toolbarHeight: kToolbarHeight,
);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
@override
Color? get backgroundColor => _colors.brightness == Brightness.dark ? _colors.surface : _colors.primary;
@override
Color? get foregroundColor => _colors.brightness == Brightness.dark ? _colors.onSurface : _colors.onPrimary;
@override
IconThemeData? get iconTheme => _theme.iconTheme;
@override
TextStyle? get toolbarTextStyle => _theme.textTheme.bodyText2;
@override
TextStyle? get titleTextStyle => _theme.textTheme.headline6;
}
// BEGIN GENERATED TOKEN PROPERTIES
// Generated code to the end of this file. Do not edit by hand.
// These defaults are generated from the Material Design Token
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
// Generated version v0_92
class _TokenDefaultsM3 extends AppBarTheme {
_TokenDefaultsM3(this.context)
: super(
elevation: 0.0,
scrolledUnderElevation: 3.0,
titleSpacing: NavigationToolbar.kMiddleSpacing,
toolbarHeight: 64.0,
);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
late final TextTheme _textTheme = _theme.textTheme;
@override
Color? get backgroundColor => _colors.surface;
@override
Color? get foregroundColor => _colors.onSurface;
@override
Color? get surfaceTintColor => _colors.surfaceTint;
@override
IconThemeData? get iconTheme => IconThemeData(
color: _colors.onSurface,
size: 24.0,
);
@override
IconThemeData? get actionsIconTheme => IconThemeData(
color: _colors.onSurfaceVariant,
size: 24.0,
);
@override
TextStyle? get toolbarTextStyle => _textTheme.bodyText2;
@override
TextStyle? get titleTextStyle => _textTheme.titleLarge;
}
// END GENERATED TOKEN PROPERTIES
...@@ -37,7 +37,9 @@ class AppBarTheme with Diagnosticable { ...@@ -37,7 +37,9 @@ class AppBarTheme with Diagnosticable {
Color? backgroundColor, Color? backgroundColor,
this.foregroundColor, this.foregroundColor,
this.elevation, this.elevation,
this.scrolledUnderElevation,
this.shadowColor, this.shadowColor,
this.surfaceTintColor,
this.shape, this.shape,
this.iconTheme, this.iconTheme,
this.actionsIconTheme, this.actionsIconTheme,
...@@ -121,10 +123,18 @@ class AppBarTheme with Diagnosticable { ...@@ -121,10 +123,18 @@ class AppBarTheme with Diagnosticable {
/// descendant [AppBar] widgets. /// descendant [AppBar] widgets.
final double? elevation; final double? elevation;
/// Overrides the default value of [AppBar.scrolledUnderElevation] in all
/// descendant [AppBar] widgets.
final double? scrolledUnderElevation;
/// Overrides the default value for [AppBar.shadowColor] in all /// Overrides the default value for [AppBar.shadowColor] in all
/// descendant widgets. /// descendant widgets.
final Color? shadowColor; final Color? shadowColor;
/// Overrides the default value for [AppBar.surfaceTintColor] in all
/// descendant widgets.
final Color? surfaceTintColor;
/// Overrides the default value for [AppBar.shape] in all /// Overrides the default value for [AppBar.shape] in all
/// descendant widgets. /// descendant widgets.
final ShapeBorder? shape; final ShapeBorder? shape;
...@@ -237,7 +247,9 @@ class AppBarTheme with Diagnosticable { ...@@ -237,7 +247,9 @@ class AppBarTheme with Diagnosticable {
Color? backgroundColor, Color? backgroundColor,
Color? foregroundColor, Color? foregroundColor,
double? elevation, double? elevation,
double? scrolledUnderElevation,
Color? shadowColor, Color? shadowColor,
Color? surfaceTintColor,
ShapeBorder? shape, ShapeBorder? shape,
IconThemeData? iconTheme, IconThemeData? iconTheme,
@Deprecated( @Deprecated(
...@@ -266,7 +278,9 @@ class AppBarTheme with Diagnosticable { ...@@ -266,7 +278,9 @@ class AppBarTheme with Diagnosticable {
backgroundColor: backgroundColor ?? color ?? this.backgroundColor, backgroundColor: backgroundColor ?? color ?? this.backgroundColor,
foregroundColor: foregroundColor ?? this.foregroundColor, foregroundColor: foregroundColor ?? this.foregroundColor,
elevation: elevation ?? this.elevation, elevation: elevation ?? this.elevation,
scrolledUnderElevation: scrolledUnderElevation ?? this.scrolledUnderElevation,
shadowColor: shadowColor ?? this.shadowColor, shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
shape: shape ?? this.shape, shape: shape ?? this.shape,
iconTheme: iconTheme ?? this.iconTheme, iconTheme: iconTheme ?? this.iconTheme,
actionsIconTheme: actionsIconTheme ?? this.actionsIconTheme, actionsIconTheme: actionsIconTheme ?? this.actionsIconTheme,
...@@ -298,7 +312,9 @@ class AppBarTheme with Diagnosticable { ...@@ -298,7 +312,9 @@ class AppBarTheme with Diagnosticable {
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
foregroundColor: Color.lerp(a?.foregroundColor, b?.foregroundColor, t), foregroundColor: Color.lerp(a?.foregroundColor, b?.foregroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t), elevation: lerpDouble(a?.elevation, b?.elevation, t),
scrolledUnderElevation: lerpDouble(a?.scrolledUnderElevation, b?.scrolledUnderElevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t), shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t), shape: ShapeBorder.lerp(a?.shape, b?.shape, 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),
...@@ -319,7 +335,9 @@ class AppBarTheme with Diagnosticable { ...@@ -319,7 +335,9 @@ class AppBarTheme with Diagnosticable {
backgroundColor, backgroundColor,
foregroundColor, foregroundColor,
elevation, elevation,
scrolledUnderElevation,
shadowColor, shadowColor,
surfaceTintColor,
shape, shape,
iconTheme, iconTheme,
actionsIconTheme, actionsIconTheme,
...@@ -344,7 +362,9 @@ class AppBarTheme with Diagnosticable { ...@@ -344,7 +362,9 @@ class AppBarTheme with Diagnosticable {
&& other.backgroundColor == backgroundColor && other.backgroundColor == backgroundColor
&& other.foregroundColor == foregroundColor && other.foregroundColor == foregroundColor
&& other.elevation == elevation && other.elevation == elevation
&& other.scrolledUnderElevation == scrolledUnderElevation
&& other.shadowColor == shadowColor && other.shadowColor == shadowColor
&& other.surfaceTintColor == surfaceTintColor
&& other.shape == shape && other.shape == shape
&& other.iconTheme == iconTheme && other.iconTheme == iconTheme
&& other.actionsIconTheme == actionsIconTheme && other.actionsIconTheme == actionsIconTheme
...@@ -365,7 +385,9 @@ class AppBarTheme with Diagnosticable { ...@@ -365,7 +385,9 @@ class AppBarTheme with Diagnosticable {
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null)); properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(ColorProperty('foregroundColor', foregroundColor, defaultValue: null)); properties.add(ColorProperty('foregroundColor', foregroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<double>('elevation', elevation, defaultValue: null)); properties.add(DiagnosticsProperty<double>('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<double>('scrolledUnderElevation', scrolledUnderElevation, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null)); properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, 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));
......
...@@ -400,6 +400,9 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin { ...@@ -400,6 +400,9 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final Color? backgroundColor = _getBackgroundColor(context); final Color? backgroundColor = _getBackgroundColor(context);
final Color? modelShadowColor = widget.shadowColor ?? (theme.useMaterial3 ? null : theme.shadowColor);
// If no shadow color is specified, use 0 for elevation in the model so a drop shadow won't be painted.
final double modelElevation = modelShadowColor != null ? widget.elevation : 0;
assert( assert(
backgroundColor != null || widget.type == MaterialType.transparency, backgroundColor != null || widget.type == MaterialType.transparency,
'If Material type is not MaterialType.transparency, a color must ' 'If Material type is not MaterialType.transparency, a color must '
...@@ -449,9 +452,9 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin { ...@@ -449,9 +452,9 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
duration: widget.animationDuration, duration: widget.animationDuration,
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
elevation: widget.elevation, elevation: modelElevation,
color: color, color: color,
shadowColor: widget.shadowColor ?? (theme.useMaterial3 ? const Color(0x00000000) : theme.shadowColor), shadowColor: modelShadowColor ?? const Color(0x00000000),
animateColor: false, animateColor: false,
child: contents, child: contents,
); );
...@@ -476,7 +479,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin { ...@@ -476,7 +479,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
elevation: widget.elevation, elevation: widget.elevation,
color: backgroundColor!, color: backgroundColor!,
shadowColor: widget.shadowColor ?? (theme.useMaterial3 ? const Color(0x00000000) : theme.shadowColor), shadowColor: modelShadowColor,
surfaceTintColor: widget.surfaceTintColor, surfaceTintColor: widget.surfaceTintColor,
child: contents, child: contents,
); );
...@@ -742,8 +745,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget { ...@@ -742,8 +745,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
assert(shape != null), assert(shape != null),
assert(clipBehavior != null), assert(clipBehavior != null),
assert(elevation != null && elevation >= 0.0), assert(elevation != null && elevation >= 0.0),
assert(color != null), assert(color != null);
assert(shadowColor != null);
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
/// ///
...@@ -777,7 +779,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget { ...@@ -777,7 +779,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget {
final Color color; final Color color;
/// The target shadow color. /// The target shadow color.
final Color shadowColor; final Color? shadowColor;
/// The target surface tint color. /// The target surface tint color.
final Color? surfaceTintColor; final Color? surfaceTintColor;
...@@ -808,11 +810,13 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior> ...@@ -808,11 +810,13 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
widget.elevation, widget.elevation,
(dynamic value) => Tween<double>(begin: value as double), (dynamic value) => Tween<double>(begin: value as double),
) as Tween<double>?; ) as Tween<double>?;
_shadowColor = visitor( _shadowColor = widget.shadowColor != null
_shadowColor, ? visitor(
widget.shadowColor, _shadowColor,
(dynamic value) => ColorTween(begin: value as Color), widget.shadowColor,
) as ColorTween?; (dynamic value) => ColorTween(begin: value as Color),
) as ColorTween?
: null;
_surfaceTintColor = widget.surfaceTintColor != null _surfaceTintColor = widget.surfaceTintColor != null
? visitor( ? visitor(
_surfaceTintColor, _surfaceTintColor,
...@@ -834,15 +838,18 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior> ...@@ -834,15 +838,18 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
final Color color = Theme.of(context).useMaterial3 final Color color = Theme.of(context).useMaterial3
? ElevationOverlay.applySurfaceTint(widget.color, _surfaceTintColor?.evaluate(animation), elevation) ? ElevationOverlay.applySurfaceTint(widget.color, _surfaceTintColor?.evaluate(animation), elevation)
: ElevationOverlay.applyOverlay(context, widget.color, elevation); : ElevationOverlay.applyOverlay(context, widget.color, elevation);
// If no shadow color is specified, use 0 for elevation in the model so a drop shadow won't be painted.
final double modelElevation = widget.shadowColor != null ? elevation : 0;
final Color shadowColor = _shadowColor?.evaluate(animation) ?? const Color(0x00000000);
return PhysicalShape( return PhysicalShape(
clipper: ShapeBorderClipper( clipper: ShapeBorderClipper(
shape: shape, shape: shape,
textDirection: Directionality.maybeOf(context), textDirection: Directionality.maybeOf(context),
), ),
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
elevation: elevation, elevation: modelElevation,
color: color, color: color,
shadowColor: _shadowColor!.evaluate(animation)!, shadowColor: shadowColor,
child: _ShapeBorderPaint( child: _ShapeBorderPaint(
shape: shape, shape: shape,
borderOnForeground: widget.borderOnForeground, borderOnForeground: widget.borderOnForeground,
......
...@@ -1203,6 +1203,7 @@ class ThemeData with Diagnosticable { ...@@ -1203,6 +1203,7 @@ class ThemeData with Diagnosticable {
/// Components that have been migrated to Material 3 are: /// Components that have been migrated to Material 3 are:
/// ///
/// * [AlertDialog] /// * [AlertDialog]
/// * [AppBar]
/// * [Card] /// * [Card]
/// * [Dialog] /// * [Dialog]
/// * [ElevatedButton] /// * [ElevatedButton]
......
...@@ -954,6 +954,8 @@ void main() { ...@@ -954,6 +954,8 @@ void main() {
}); });
testWidgets('AppBar uses the specified elevation or defaults to 4.0', (WidgetTester tester) async { testWidgets('AppBar uses the specified elevation or defaults to 4.0', (WidgetTester tester) async {
final bool useMaterial3 = ThemeData().useMaterial3;
Widget buildAppBar([double? elevation]) { Widget buildAppBar([double? elevation]) {
return MaterialApp( return MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -967,15 +969,48 @@ void main() { ...@@ -967,15 +969,48 @@ void main() {
matching: find.byType(Material), matching: find.byType(Material),
)); ));
// Default elevation should be _AppBarState._defaultElevation = 4.0 // Default elevation should be used for the material.
await tester.pumpWidget(buildAppBar()); await tester.pumpWidget(buildAppBar());
expect(getMaterial().elevation, 4.0); expect(getMaterial().elevation, useMaterial3 ? 0 : 4);
// AppBar should use the specified elevation. // AppBar should use the specified elevation.
await tester.pumpWidget(buildAppBar(8.0)); await tester.pumpWidget(buildAppBar(8.0));
expect(getMaterial().elevation, 8.0); expect(getMaterial().elevation, 8.0);
}); });
testWidgets('scrolledUnderElevation', (WidgetTester tester) async {
Widget buildAppBar({double? elevation, double? scrolledUnderElevation}) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Title'),
elevation: elevation,
scrolledUnderElevation: scrolledUnderElevation,
),
body: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) => ListTile(title: Text('Item $index')),
),
),
);
}
Material getMaterial() => tester.widget<Material>(find.descendant(
of: find.byType(AppBar),
matching: find.byType(Material),
));
await tester.pumpWidget(buildAppBar(elevation: 2, scrolledUnderElevation: 10));
// Starts with the base elevation.
expect(getMaterial().elevation, 2);
await tester.fling(find.text('Item 2'), const Offset(0.0, -600.0), 2000.0);
await tester.pumpAndSettle();
// After scrolling it should be the scrolledUnderElevation.
expect(getMaterial().elevation, 10);
});
group('SliverAppBar elevation', () { group('SliverAppBar elevation', () {
Widget buildSliverAppBar(bool forceElevated, {double? elevation, double? themeElevation}) { Widget buildSliverAppBar(bool forceElevated, {double? elevation, double? themeElevation}) {
return MaterialApp( return MaterialApp(
...@@ -996,15 +1031,16 @@ void main() { ...@@ -996,15 +1031,16 @@ void main() {
// Regression test for https://github.com/flutter/flutter/issues/59158. // Regression test for https://github.com/flutter/flutter/issues/59158.
AppBar getAppBar() => tester.widget<AppBar>(find.byType(AppBar)); AppBar getAppBar() => tester.widget<AppBar>(find.byType(AppBar));
Material getMaterial() => tester.widget<Material>(find.byType(Material)); Material getMaterial() => tester.widget<Material>(find.byType(Material));
final bool useMaterial3 = ThemeData().useMaterial3;
// When forceElevated is off, SliverAppBar should not be elevated. // When forceElevated is off, SliverAppBar should not be elevated.
await tester.pumpWidget(buildSliverAppBar(false)); await tester.pumpWidget(buildSliverAppBar(false));
expect(getMaterial().elevation, 0.0); expect(getMaterial().elevation, 0.0);
// Default elevation should be _AppBarState._defaultElevation = 4.0, and // Default elevation should be used by the material, but
// the AppBar's elevation should not be specified by SliverAppBar. // the AppBar's elevation should not be specified by SliverAppBar.
await tester.pumpWidget(buildSliverAppBar(true)); await tester.pumpWidget(buildSliverAppBar(true));
expect(getMaterial().elevation, 4.0); expect(getMaterial().elevation, useMaterial3 ? 0.0 : 4.0);
expect(getAppBar().elevation, null); expect(getAppBar().elevation, null);
// SliverAppBar should use the specified elevation. // SliverAppBar should use the specified elevation.
...@@ -1312,6 +1348,8 @@ void main() { ...@@ -1312,6 +1348,8 @@ void main() {
final Key key = UniqueKey(); final Key key = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
// Test was designed against InkSplash so need to make sure that is used.
theme: ThemeData(splashFactory: InkSplash.splashFactory),
home: Center( home: Center(
child: AppBar( child: AppBar(
title: const Text('Abc'), title: const Text('Abc'),
...@@ -2005,44 +2043,55 @@ void main() { ...@@ -2005,44 +2043,55 @@ void main() {
)); ));
}); });
testWidgets('AppBar draws a light system bar for a light theme with a dark background', (WidgetTester tester) async { testWidgets('Default system bar brightness based on AppBar background color brightness.', (WidgetTester tester) async {
final ThemeData lightTheme = ThemeData(primarySwatch: Colors.deepOrange); Widget buildAppBar(ThemeData theme) {
await tester.pumpWidget(MaterialApp( return MaterialApp(
theme: lightTheme, theme: theme,
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(title: const Text('Title')),
title: const Text('test'),
), ),
), );
)); }
expect(lightTheme.primaryColorBrightness, Brightness.dark);
expect(lightTheme.colorScheme.brightness, Brightness.light);
expect(SystemChrome.latestStyle, const SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.light,
));
});
testWidgets('AppBar draws a dark system bar for a dark theme with a light background', (WidgetTester tester) async { // Using a light theme.
final ThemeData darkTheme = ThemeData(brightness: Brightness.dark, cardColor: Colors.white); {
await tester.pumpWidget( await tester.pumpWidget(buildAppBar(ThemeData.from(colorScheme: const ColorScheme.light())));
MaterialApp( final Material appBarMaterial = tester.widget<Material>(
theme: darkTheme, find.descendant(
home: Scaffold( of: find.byType(AppBar),
appBar: AppBar( matching: find.byType(Material),
title: const Text('test'),
),
), ),
), );
); final Brightness appBarBrightness = ThemeData.estimateBrightnessForColor(appBarMaterial.color!);
final Brightness onAppBarBrightness = appBarBrightness == Brightness.light
? Brightness.dark
: Brightness.light;
expect(SystemChrome.latestStyle, SystemUiOverlayStyle(
statusBarBrightness: appBarBrightness,
statusBarIconBrightness: onAppBarBrightness,
));
}
expect(darkTheme.primaryColorBrightness, Brightness.dark); // Using a dark theme.
expect(darkTheme.colorScheme.brightness, Brightness.dark); {
expect(SystemChrome.latestStyle, const SystemUiOverlayStyle( await tester.pumpWidget(buildAppBar(ThemeData.from(colorScheme: const ColorScheme.dark())));
statusBarBrightness: Brightness.light, final Material appBarMaterial = tester.widget<Material>(
statusBarIconBrightness: Brightness.dark, find.descendant(
)); of: find.byType(AppBar),
matching: find.byType(Material),
),
);
final Brightness appBarBrightness = ThemeData.estimateBrightnessForColor(appBarMaterial.color!);
final Brightness onAppBarBrightness = appBarBrightness == Brightness.light
? Brightness.dark
: Brightness.light;
expect(SystemChrome.latestStyle, SystemUiOverlayStyle(
statusBarBrightness: appBarBrightness,
statusBarIconBrightness: onAppBarBrightness,
));
}
}); });
testWidgets('Changing SliverAppBar snap from true to false', (WidgetTester tester) async { testWidgets('Changing SliverAppBar snap from true to false', (WidgetTester tester) async {
...@@ -2207,6 +2256,8 @@ void main() { ...@@ -2207,6 +2256,8 @@ void main() {
Widget buildFrame() { Widget buildFrame() {
return MaterialApp( return MaterialApp(
// Test designed against 2014 font sizes.
theme: ThemeData(textTheme: Typography.englishLike2014),
home: Builder( home: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return MediaQuery( return MediaQuery(
...@@ -2245,6 +2296,8 @@ void main() { ...@@ -2245,6 +2296,8 @@ void main() {
Widget buildFrame() { Widget buildFrame() {
return MaterialApp( return MaterialApp(
// Test designed against 2014 font sizes.
theme: ThemeData(textTheme: Typography.englishLike2014),
home: Builder( home: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return Directionality( return Directionality(
...@@ -2536,6 +2589,7 @@ void main() { ...@@ -2536,6 +2589,7 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData.light().copyWith( theme: ThemeData.light().copyWith(
useMaterial3: false,
appBarTheme: const AppBarTheme( appBarTheme: const AppBarTheme(
backwardsCompatibility: false, backwardsCompatibility: false,
), ),
......
...@@ -15,8 +15,10 @@ void main() { ...@@ -15,8 +15,10 @@ void main() {
}); });
testWidgets('Passing no AppBarTheme returns defaults', (WidgetTester tester) async { testWidgets('Passing no AppBarTheme returns defaults', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: theme,
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
actions: <Widget>[ actions: <Widget>[
...@@ -33,17 +35,33 @@ void main() { ...@@ -33,17 +35,33 @@ void main() {
final RichText actionIconText = _getAppBarIconRichText(tester); final RichText actionIconText = _getAppBarIconRichText(tester);
final DefaultTextStyle text = _getAppBarText(tester); final DefaultTextStyle text = _getAppBarText(tester);
expect(SystemChrome.latestStyle!.statusBarBrightness, SystemUiOverlayStyle.light.statusBarBrightness); if (theme.useMaterial3) {
expect(widget.color, Colors.blue); expect(SystemChrome.latestStyle!.statusBarBrightness, Brightness.light);
expect(widget.elevation, 4.0); expect(widget.color, theme.colorScheme.surface);
expect(widget.shadowColor, Colors.black); expect(widget.elevation, 0);
expect(widget.shape, null); expect(widget.shadowColor, null);
expect(iconTheme.data, const IconThemeData(color: Colors.white)); expect(widget.surfaceTintColor, theme.colorScheme.surfaceTint);
expect(actionsIconTheme.data, const IconThemeData(color: Colors.white)); expect(widget.shape, null);
expect(actionIconText.text.style!.color, Colors.white); expect(iconTheme.data, IconThemeData(color: theme.colorScheme.onSurface, size: 24));
expect(text.style, Typography.material2014().englishLike.bodyText2!.merge(Typography.material2014().white.bodyText2)); expect(actionsIconTheme.data, IconThemeData(color: theme.colorScheme.onSurfaceVariant, size: 24));
expect(tester.getSize(find.byType(AppBar)).height, kToolbarHeight); expect(actionIconText.text.style!.color, Colors.black);
expect(tester.getSize(find.byType(AppBar)).width, 800); expect(text.style, Typography.material2021().englishLike.bodyText2!.merge(Typography.material2021().black.bodyText2).copyWith(color: theme.colorScheme.onSurface));
expect(tester.getSize(find.byType(AppBar)).height, kToolbarHeight);
expect(tester.getSize(find.byType(AppBar)).width, 800);
} else {
expect(SystemChrome.latestStyle!.statusBarBrightness, SystemUiOverlayStyle.light.statusBarBrightness);
expect(widget.color, Colors.blue);
expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(widget.surfaceTintColor, null);
expect(widget.shape, null);
expect(iconTheme.data, const IconThemeData(color: Colors.white));
expect(actionsIconTheme.data, const IconThemeData(color: Colors.white));
expect(actionIconText.text.style!.color, Colors.white);
expect(text.style, Typography.material2014().englishLike.bodyText2!.merge(Typography.material2014().white.bodyText2));
expect(tester.getSize(find.byType(AppBar)).height, kToolbarHeight);
expect(tester.getSize(find.byType(AppBar)).width, 800);
}
}); });
testWidgets('AppBar uses values from AppBarTheme', (WidgetTester tester) async { testWidgets('AppBar uses values from AppBarTheme', (WidgetTester tester) async {
...@@ -73,6 +91,7 @@ void main() { ...@@ -73,6 +91,7 @@ void main() {
expect(widget.color, appBarTheme.backgroundColor); expect(widget.color, appBarTheme.backgroundColor);
expect(widget.elevation, appBarTheme.elevation); expect(widget.elevation, appBarTheme.elevation);
expect(widget.shadowColor, appBarTheme.shadowColor); expect(widget.shadowColor, appBarTheme.shadowColor);
expect(widget.surfaceTintColor, appBarTheme.surfaceTintColor);
expect(widget.shape, const StadiumBorder()); expect(widget.shape, const StadiumBorder());
expect(iconTheme.data, appBarTheme.iconTheme); expect(iconTheme.data, appBarTheme.iconTheme);
expect(actionsIconTheme.data, appBarTheme.actionsIconTheme); expect(actionsIconTheme.data, appBarTheme.actionsIconTheme);
...@@ -132,7 +151,8 @@ void main() { ...@@ -132,7 +151,8 @@ void main() {
const SystemUiOverlayStyle systemOverlayStyle = SystemUiOverlayStyle.light; const SystemUiOverlayStyle systemOverlayStyle = SystemUiOverlayStyle.light;
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 Color shadowColor = Colors.purple;
const Color surfaceTintColor = Colors.brown;
const ShapeBorder shape = RoundedRectangleBorder(); const ShapeBorder shape = RoundedRectangleBorder();
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);
...@@ -151,6 +171,7 @@ void main() { ...@@ -151,6 +171,7 @@ void main() {
systemOverlayStyle: systemOverlayStyle, systemOverlayStyle: systemOverlayStyle,
elevation: elevation, elevation: elevation,
shadowColor: shadowColor, shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
shape: shape, shape: shape,
iconTheme: iconThemeData, iconTheme: iconThemeData,
actionsIconTheme: actionsIconThemeData, actionsIconTheme: actionsIconThemeData,
...@@ -174,6 +195,7 @@ void main() { ...@@ -174,6 +195,7 @@ void main() {
expect(widget.color, color); expect(widget.color, color);
expect(widget.elevation, elevation); expect(widget.elevation, elevation);
expect(widget.shadowColor, shadowColor); expect(widget.shadowColor, shadowColor);
expect(widget.surfaceTintColor, surfaceTintColor);
expect(widget.shape, shape); expect(widget.shape, shape);
expect(iconTheme.data, iconThemeData); expect(iconTheme.data, iconThemeData);
expect(actionsIconTheme.data, actionsIconThemeData); expect(actionsIconTheme.data, actionsIconThemeData);
...@@ -228,6 +250,7 @@ void main() { ...@@ -228,6 +250,7 @@ void main() {
expect(widget.color, appBarTheme.backgroundColor); expect(widget.color, appBarTheme.backgroundColor);
expect(widget.elevation, appBarTheme.elevation); expect(widget.elevation, appBarTheme.elevation);
expect(widget.shadowColor, appBarTheme.shadowColor); expect(widget.shadowColor, appBarTheme.shadowColor);
expect(widget.surfaceTintColor, appBarTheme.surfaceTintColor);
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);
...@@ -235,15 +258,13 @@ void main() { ...@@ -235,15 +258,13 @@ void main() {
}); });
testWidgets('ThemeData colorScheme is used when no AppBarTheme is set', (WidgetTester tester) async { testWidgets('ThemeData colorScheme is used when no AppBarTheme is set', (WidgetTester tester) async {
late ThemeData theme; final ThemeData lightTheme = ThemeData.from(colorScheme: const ColorScheme.light());
final ThemeData darkTheme = ThemeData.from(colorScheme: const ColorScheme.dark());
Widget buildFrame(ThemeData appTheme) { Widget buildFrame(ThemeData appTheme) {
return MaterialApp( return MaterialApp(
theme: appTheme, theme: appTheme,
home: Builder( home: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
// This ThemeData has been localized with ThemeData.localize. The
// appTheme parameter has not, so its textTheme is incomplete.
theme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
actions: <Widget>[ actions: <Widget>[
...@@ -256,57 +277,120 @@ void main() { ...@@ -256,57 +277,120 @@ void main() {
); );
} }
// AppBar defaults for light themes: if (lightTheme.useMaterial3) {
// - elevation: 4 // M3 AppBar defaults for light themes:
// - shadow color: black // - elevation: 0
// - background color: ColorScheme.primary // - shadow color: null
// - foreground color: ColorScheme.onPrimary // - surface tint color: ColorScheme.surfaceTint
// - actions text: style bodyText2, foreground color // - background color: ColorScheme.surface
// - status bar brightness: light (based on color scheme brightness) // - foreground color: ColorScheme.onSurface
{ // - actions text: style bodyText2, foreground color
await tester.pumpWidget(buildFrame(ThemeData.from(colorScheme: const ColorScheme.light()))); // - status bar brightness: light (based on color scheme brightness)
{
final Material widget = _getAppBarMaterial(tester); await tester.pumpWidget(buildFrame(lightTheme));
final IconTheme iconTheme = _getAppBarIconTheme(tester);
final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); final Material widget = _getAppBarMaterial(tester);
final RichText actionIconText = _getAppBarIconRichText(tester); final IconTheme iconTheme = _getAppBarIconTheme(tester);
final DefaultTextStyle text = _getAppBarText(tester); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester);
final RichText actionIconText = _getAppBarIconRichText(tester);
expect(SystemChrome.latestStyle!.statusBarBrightness, SystemUiOverlayStyle.light.statusBarBrightness); final DefaultTextStyle text = _getAppBarText(tester);
expect(widget.color, theme.colorScheme.primary);
expect(widget.elevation, 4.0); expect(SystemChrome.latestStyle!.statusBarBrightness, Brightness.light);
expect(widget.shadowColor, Colors.black); expect(widget.color, lightTheme.colorScheme.surface);
expect(iconTheme.data.color, theme.colorScheme.onPrimary); expect(widget.elevation, 0);
expect(actionsIconTheme.data.color, theme.colorScheme.onPrimary); expect(widget.shadowColor, null);
expect(actionIconText.text.style!.color, theme.colorScheme.onPrimary); expect(widget.surfaceTintColor, lightTheme.colorScheme.surfaceTint);
expect(text.style.compareTo(theme.textTheme.bodyText2!.copyWith(color: theme.colorScheme.onPrimary)), RenderComparison.identical); expect(iconTheme.data.color, lightTheme.colorScheme.onSurface);
} expect(actionsIconTheme.data.color, lightTheme.colorScheme.onSurface);
expect(actionIconText.text.style!.color, lightTheme.colorScheme.onSurface);
// AppBar defaults for dark themes: expect(text.style, Typography.material2021().englishLike.bodyText2!.merge(Typography.material2021().black.bodyText2).copyWith(color: lightTheme.colorScheme.onSurface));
// - elevation: 4 }
// - shadow color: black
// - background color: ColorScheme.surface // M3 AppBar defaults for dark themes:
// - foreground color: ColorScheme.onSurface // - elevation: 0
// - actions text: style bodyText2, foreground color // - shadow color: null
// - status bar brightness: dark (based on background color) // - surface tint color: ColorScheme.surfaceTint
{ // - background color: ColorScheme.surface
await tester.pumpWidget(buildFrame(ThemeData.from(colorScheme: const ColorScheme.dark()))); // - foreground color: ColorScheme.onSurface
await tester.pumpAndSettle(); // Theme change animation // - actions text: style bodyText2, foreground color
// - status bar brightness: dark (based on background color)
final Material widget = _getAppBarMaterial(tester); {
final IconTheme iconTheme = _getAppBarIconTheme(tester); await tester.pumpWidget(buildFrame(ThemeData.from(colorScheme: const ColorScheme.dark())));
final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); await tester.pumpAndSettle(); // Theme change animation
final RichText actionIconText = _getAppBarIconRichText(tester);
final DefaultTextStyle text = _getAppBarText(tester); final Material widget = _getAppBarMaterial(tester);
final IconTheme iconTheme = _getAppBarIconTheme(tester);
expect(SystemChrome.latestStyle!.statusBarBrightness, SystemUiOverlayStyle.light.statusBarBrightness); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester);
expect(widget.color, theme.colorScheme.surface); final RichText actionIconText = _getAppBarIconRichText(tester);
expect(widget.elevation, 4.0); final DefaultTextStyle text = _getAppBarText(tester);
expect(widget.shadowColor, Colors.black);
expect(iconTheme.data.color, theme.colorScheme.onSurface); expect(SystemChrome.latestStyle!.statusBarBrightness, Brightness.dark);
expect(actionsIconTheme.data.color, theme.colorScheme.onSurface); expect(widget.color, darkTheme.colorScheme.surface);
expect(actionIconText.text.style!.color, theme.colorScheme.onSurface); expect(widget.elevation, 0);
expect(text.style.compareTo(theme.textTheme.bodyText2!.copyWith(color: theme.colorScheme.onSurface)), RenderComparison.identical); expect(widget.shadowColor, null);
expect(widget.surfaceTintColor, darkTheme.colorScheme.surfaceTint);
expect(iconTheme.data.color, darkTheme.colorScheme.onSurface);
expect(actionsIconTheme.data.color, darkTheme.colorScheme.onSurface);
expect(actionIconText.text.style!.color, darkTheme.colorScheme.onSurface);
expect(text.style, Typography.material2021().englishLike.bodyText2!.merge(Typography.material2021().black.bodyText2).copyWith(color: darkTheme.colorScheme.onSurface));
}
} else {
// AppBar defaults for light themes:
// - elevation: 4
// - shadow color: black
// - surface tint color: null
// - background color: ColorScheme.primary
// - foreground color: ColorScheme.onPrimary
// - actions text: style bodyText2, foreground color
// - status bar brightness: light (based on color scheme brightness)
{
await tester.pumpWidget(buildFrame(lightTheme));
final Material widget = _getAppBarMaterial(tester);
final IconTheme iconTheme = _getAppBarIconTheme(tester);
final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester);
final RichText actionIconText = _getAppBarIconRichText(tester);
final DefaultTextStyle text = _getAppBarText(tester);
expect(SystemChrome.latestStyle!.statusBarBrightness, SystemUiOverlayStyle.light.statusBarBrightness);
expect(widget.color, lightTheme.colorScheme.primary);
expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(widget.surfaceTintColor, null);
expect(iconTheme.data.color, lightTheme.colorScheme.onPrimary);
expect(actionsIconTheme.data.color, lightTheme.colorScheme.onPrimary);
expect(actionIconText.text.style!.color, lightTheme.colorScheme.onPrimary);
expect(text.style, Typography.material2014().englishLike.bodyText2!.merge(Typography.material2014().black.bodyText2).copyWith(color: lightTheme.colorScheme.onPrimary));
}
// AppBar defaults for dark themes:
// - elevation: 4
// - shadow color: black
// - surface tint color: null
// - background color: ColorScheme.surface
// - foreground color: ColorScheme.onSurface
// - actions text: style bodyText2, foreground color
// - status bar brightness: dark (based on background color)
{
await tester.pumpWidget(buildFrame(darkTheme));
await tester.pumpAndSettle(); // Theme change animation
final Material widget = _getAppBarMaterial(tester);
final IconTheme iconTheme = _getAppBarIconTheme(tester);
final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester);
final RichText actionIconText = _getAppBarIconRichText(tester);
final DefaultTextStyle text = _getAppBarText(tester);
expect(SystemChrome.latestStyle!.statusBarBrightness, SystemUiOverlayStyle.light.statusBarBrightness);
expect(widget.color, darkTheme.colorScheme.surface);
expect(widget.elevation, 4.0);
expect(widget.shadowColor, Colors.black);
expect(widget.surfaceTintColor, null);
expect(iconTheme.data.color, darkTheme.colorScheme.onSurface);
expect(actionsIconTheme.data.color, darkTheme.colorScheme.onSurface);
expect(actionIconText.text.style!.color, darkTheme.colorScheme.onSurface);
expect(text.style, Typography.material2014().englishLike.bodyText2!.merge(Typography.material2014().black.bodyText2).copyWith(color: darkTheme.colorScheme.onSurface));
}
} }
}); });
...@@ -315,7 +399,7 @@ void main() { ...@@ -315,7 +399,7 @@ void main() {
Widget buildFrame({ Color? appIconColor, Color? appBarIconColor }) { Widget buildFrame({ Color? appIconColor, Color? appBarIconColor }) {
return MaterialApp( return MaterialApp(
theme: ThemeData.from(colorScheme: const ColorScheme.light()), theme: ThemeData.from(useMaterial3: false, colorScheme: const ColorScheme.light()),
home: IconTheme( home: IconTheme(
data: IconThemeData(color: appIconColor), data: IconThemeData(color: appIconColor),
child: Builder( child: Builder(
...@@ -408,6 +492,22 @@ void main() { ...@@ -408,6 +492,22 @@ void main() {
expect(appBar.shadowColor, Colors.yellow); expect(appBar.shadowColor, Colors.yellow);
}); });
testWidgets('AppBar.surfaceTintColor takes priority over AppBarTheme.surfaceTintColor', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
theme: ThemeData(appBarTheme: const AppBarTheme(surfaceTintColor: Colors.red)),
home: Scaffold(
appBar: AppBar(
title: const Text('Title'),
surfaceTintColor: Colors.yellow,
),
),
));
final AppBar appBar = tester.widget(find.byType(AppBar));
// The AppBar.surfaceTintColor should be used instead of AppBarTheme.surfaceTintColor.
expect(appBar.surfaceTintColor, Colors.yellow);
});
testWidgets('AppBar uses AppBarTheme.titleSpacing', (WidgetTester tester) async { testWidgets('AppBar uses AppBarTheme.titleSpacing', (WidgetTester tester) async {
const double kTitleSpacing = 10; const double kTitleSpacing = 10;
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -493,6 +593,7 @@ void main() { ...@@ -493,6 +593,7 @@ void main() {
backgroundColor: Color(0xff000001), backgroundColor: Color(0xff000001),
elevation: 8.0, elevation: 8.0,
shadowColor: Color(0xff000002), shadowColor: Color(0xff000002),
surfaceTintColor: Color(0xff000003),
centerTitle: true, centerTitle: true,
titleSpacing: 40.0, titleSpacing: 40.0,
).debugFillProperties(builder); ).debugFillProperties(builder);
...@@ -507,6 +608,7 @@ void main() { ...@@ -507,6 +608,7 @@ void main() {
'backgroundColor: Color(0xff000001)', 'backgroundColor: Color(0xff000001)',
'elevation: 8.0', 'elevation: 8.0',
'shadowColor: Color(0xff000002)', 'shadowColor: Color(0xff000002)',
'surfaceTintColor: Color(0xff000003)',
'centerTitle: true', 'centerTitle: true',
'titleSpacing: 40.0', 'titleSpacing: 40.0',
]); ]);
...@@ -524,6 +626,7 @@ AppBarTheme _appBarTheme() { ...@@ -524,6 +626,7 @@ AppBarTheme _appBarTheme() {
const Color backgroundColor = Colors.lightBlue; const Color backgroundColor = Colors.lightBlue;
const double elevation = 6.0; const double elevation = 6.0;
const Color shadowColor = Colors.red; const Color shadowColor = Colors.red;
const Color surfaceTintColor = Colors.green;
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);
return const AppBarTheme( return const AppBarTheme(
...@@ -532,6 +635,7 @@ AppBarTheme _appBarTheme() { ...@@ -532,6 +635,7 @@ AppBarTheme _appBarTheme() {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shadowColor: shadowColor, shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
shape: StadiumBorder(), shape: StadiumBorder(),
iconTheme: iconThemeData, iconTheme: iconThemeData,
toolbarHeight: 96, toolbarHeight: 96,
......
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