diff --git a/dev/integration_tests/new_gallery/lib/studies/reply/app.dart b/dev/integration_tests/new_gallery/lib/studies/reply/app.dart index 9a8c23e9cf3..187ecb9eae3 100644 --- a/dev/integration_tests/new_gallery/lib/studies/reply/app.dart +++ b/dev/integration_tests/new_gallery/lib/studies/reply/app.dart @@ -152,7 +152,7 @@ class _RestorableEmailState extends RestorableListenable { ThemeData _buildReplyLightTheme(BuildContext context) { final ThemeData base = ThemeData(); return base.copyWith( - bottomAppBarTheme: const BottomAppBarTheme(color: ReplyColors.blue700), + bottomAppBarTheme: const BottomAppBarThemeData(color: ReplyColors.blue700), bottomSheetTheme: BottomSheetThemeData( backgroundColor: ReplyColors.blue700, modalBackgroundColor: Colors.white.withOpacity(0.7), @@ -192,7 +192,7 @@ ThemeData _buildReplyLightTheme(BuildContext context) { ThemeData _buildReplyDarkTheme(BuildContext context) { final ThemeData base = ThemeData.dark(); return base.copyWith( - bottomAppBarTheme: const BottomAppBarTheme(color: ReplyColors.darkBottomAppBarBackground), + bottomAppBarTheme: const BottomAppBarThemeData(color: ReplyColors.darkBottomAppBarBackground), bottomSheetTheme: BottomSheetThemeData( backgroundColor: ReplyColors.darkDrawerBackground, modalBackgroundColor: Colors.black.withOpacity(0.7), diff --git a/dev/integration_tests/new_gallery/lib/themes/material_demo_theme_data.dart b/dev/integration_tests/new_gallery/lib/themes/material_demo_theme_data.dart index 08c022ba63f..31ae484f200 100644 --- a/dev/integration_tests/new_gallery/lib/themes/material_demo_theme_data.dart +++ b/dev/integration_tests/new_gallery/lib/themes/material_demo_theme_data.dart @@ -20,7 +20,7 @@ class MaterialDemoThemeData { color: _colorScheme.primary, iconTheme: IconThemeData(color: _colorScheme.onPrimary), ), - bottomAppBarTheme: BottomAppBarTheme(color: _colorScheme.primary), + bottomAppBarTheme: BottomAppBarThemeData(color: _colorScheme.primary), checkboxTheme: CheckboxThemeData( fillColor: MaterialStateProperty.resolveWith((Set states) { if (states.contains(MaterialState.disabled)) { diff --git a/dev/tools/gen_defaults/lib/bottom_app_bar_template.dart b/dev/tools/gen_defaults/lib/bottom_app_bar_template.dart index 27e805c9f03..81ca33af6ae 100644 --- a/dev/tools/gen_defaults/lib/bottom_app_bar_template.dart +++ b/dev/tools/gen_defaults/lib/bottom_app_bar_template.dart @@ -14,7 +14,7 @@ class BottomAppBarTemplate extends TokenTemplate { @override String generate() => ''' -class _${blockName}DefaultsM3 extends BottomAppBarTheme { +class _${blockName}DefaultsM3 extends BottomAppBarThemeData { _${blockName}DefaultsM3(this.context) : super( elevation: ${elevation('md.comp.bottom-app-bar.container')}, diff --git a/packages/flutter/lib/src/material/bottom_app_bar.dart b/packages/flutter/lib/src/material/bottom_app_bar.dart index 4f989ea802a..facb51b1898 100644 --- a/packages/flutter/lib/src/material/bottom_app_bar.dart +++ b/packages/flutter/lib/src/material/bottom_app_bar.dart @@ -67,8 +67,8 @@ class BottomAppBar extends StatefulWidget { /// The [clipBehavior] argument defaults to [Clip.none]. /// Additionally, [elevation] must be non-negative. /// - /// If [color], [elevation], or [shape] are null, their [BottomAppBarTheme] values will be used. - /// If the corresponding [BottomAppBarTheme] property is null, then the default + /// If [color], [elevation], or [shape] are null, their [BottomAppBarThemeData] values will be used. + /// If the corresponding [BottomAppBarThemeData] property is null, then the default /// specified in the property's documentation will be used. const BottomAppBar({ super.key, @@ -100,7 +100,7 @@ class BottomAppBar extends StatefulWidget { /// The bottom app bar's background color. /// - /// If this property is null then [BottomAppBarTheme.color] of + /// If this property is null then [BottomAppBarThemeData.color] of /// [ThemeData.bottomAppBarTheme] is used. If that's null and [ThemeData.useMaterial3] /// is true, the default value is [ColorScheme.surface]; if [ThemeData.useMaterial3] /// is false, then the default value is `Color(0xFF424242)` in dark theme and @@ -113,14 +113,14 @@ class BottomAppBar extends StatefulWidget { /// This controls the size of the shadow below the bottom app bar. The /// value is non-negative. /// - /// If this property is null then [BottomAppBarTheme.elevation] of + /// If this property is null then [BottomAppBarThemeData.elevation] of /// [ThemeData.bottomAppBarTheme] is used. If that's null and /// [ThemeData.useMaterial3] is true, than the default value is 3 else is 8. final double? elevation; /// The notch that is made for the floating action button. /// - /// If this property is null then [BottomAppBarTheme.shape] of + /// If this property is null then [BottomAppBarThemeData.shape] of /// [ThemeData.bottomAppBarTheme] is used. If that's null then the shape will /// be rectangular with no notch. final NotchedShape? shape; @@ -143,7 +143,7 @@ class BottomAppBar extends StatefulWidget { /// which provide more flexibility. The intention is to eventually remove surface tint color from /// the framework. /// - /// If this property is null, then [BottomAppBarTheme.surfaceTintColor] + /// If this property is null, then [BottomAppBarThemeData.surfaceTintColor] /// of [ThemeData.bottomAppBarTheme] is used. If that is also null, the default /// value is [Colors.transparent]. /// @@ -154,7 +154,7 @@ class BottomAppBar extends StatefulWidget { /// The color of the shadow below the app bar. /// - /// If this property is null, then [BottomAppBarTheme.shadowColor] of + /// If this property is null, then [BottomAppBarThemeData.shadowColor] of /// [ThemeData.bottomAppBarTheme] is used. If that is also null, the default value /// is fully opaque black for Material 2, and transparent for Material 3. /// @@ -188,8 +188,8 @@ class _BottomAppBarState extends State { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final bool isMaterial3 = theme.useMaterial3; - final BottomAppBarTheme babTheme = BottomAppBarTheme.of(context); - final BottomAppBarTheme defaults = + final BottomAppBarThemeData babTheme = BottomAppBarTheme.of(context); + final BottomAppBarThemeData defaults = isMaterial3 ? _BottomAppBarDefaultsM3(context) : _BottomAppBarDefaultsM2(context); final bool hasFab = Scaffold.of(context).hasFloatingActionButton; @@ -291,7 +291,7 @@ class _BottomAppBarClipper extends CustomClipper { } } -class _BottomAppBarDefaultsM2 extends BottomAppBarTheme { +class _BottomAppBarDefaultsM2 extends BottomAppBarThemeData { const _BottomAppBarDefaultsM2(this.context) : super(elevation: 8.0); final BuildContext context; @@ -315,7 +315,7 @@ class _BottomAppBarDefaultsM2 extends BottomAppBarTheme { // dev/tools/gen_defaults/bin/gen_defaults.dart. // dart format off -class _BottomAppBarDefaultsM3 extends BottomAppBarTheme { +class _BottomAppBarDefaultsM3 extends BottomAppBarThemeData { _BottomAppBarDefaultsM3(this.context) : super( elevation: 3.0, diff --git a/packages/flutter/lib/src/material/bottom_app_bar_theme.dart b/packages/flutter/lib/src/material/bottom_app_bar_theme.dart index 679cedf899f..5696682cd86 100644 --- a/packages/flutter/lib/src/material/bottom_app_bar_theme.dart +++ b/packages/flutter/lib/src/material/bottom_app_bar_theme.dart @@ -15,11 +15,11 @@ import 'theme.dart'; /// Defines default property values for descendant [BottomAppBar] widgets. /// -/// Descendant widgets obtain the current [BottomAppBarTheme] object using -/// `BottomAppBarTheme.of(context)`. Instances of [BottomAppBarTheme] can be -/// customized with [BottomAppBarTheme.copyWith]. +/// Descendant widgets obtain the current [BottomAppBarThemeData] object using +/// `[BottomAppBarTheme.of]`. Instances of [BottomAppBarThemeData] can be +/// customized with [BottomAppBarThemeData.copyWith]. /// -/// Typically a [BottomAppBarTheme] is specified as part of the overall [Theme] +/// Typically a [BottomAppBarThemeData] is specified as part of the overall [Theme] /// with [ThemeData.bottomAppBarTheme]. /// /// All [BottomAppBarTheme] properties are `null` by default. When null, the @@ -30,9 +30,192 @@ import 'theme.dart'; /// * [ThemeData], which describes the overall theme information for the /// application. @immutable -class BottomAppBarTheme with Diagnosticable { +class BottomAppBarTheme extends InheritedTheme with Diagnosticable { /// Creates a theme that can be used for [ThemeData.bottomAppBarTheme]. const BottomAppBarTheme({ + super.key, + Color? color, + double? elevation, + NotchedShape? shape, + double? height, + Color? surfaceTintColor, + Color? shadowColor, + EdgeInsetsGeometry? padding, + BottomAppBarThemeData? data, + Widget? child, + }) : assert( + data == null || + (color ?? + elevation ?? + shape ?? + height ?? + surfaceTintColor ?? + shadowColor ?? + padding) == + null, + ), + _color = color, + _elevation = elevation, + _shape = shape, + _height = height, + _surfaceTintColor = surfaceTintColor, + _shadowColor = shadowColor, + _padding = padding, + _data = data, + super(child: child ?? const SizedBox.shrink()); + + final BottomAppBarThemeData? _data; + final Color? _color; + final double? _elevation; + final NotchedShape? _shape; + final double? _height; + final Color? _surfaceTintColor; + final Color? _shadowColor; + final EdgeInsetsGeometry? _padding; + + /// Overrides the default value for [BottomAppBar.color]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.color] property in [data] instead. + Color? get color => _data != null ? _data.color : _color; + + /// Overrides the default value for [BottomAppBar.elevation]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.elevation] property in [data] instead. + double? get elevation => _data != null ? _data.elevation : _elevation; + + /// Overrides the default value for [BottomAppBar.shape]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.shape] property in [data] instead. + NotchedShape? get shape => _data != null ? _data.shape : _shape; + + /// Overrides the default value for [BottomAppBar.height]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.height] property in [data] instead. + double? get height => _data != null ? _data.height : _height; + + /// Overrides the default value for [BottomAppBar.surfaceTintColor]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.surfaceTintColor] property in [data] instead. + /// + /// If null, [BottomAppBar] will not display an overlay color. + /// + /// See [Material.surfaceTintColor] for more details. + Color? get surfaceTintColor => _data != null ? _data.surfaceTintColor : _surfaceTintColor; + + /// Overrides the default value for [BottomAppBar.shadowColor]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.shadowColor] property in [data] instead. + Color? get shadowColor => _data != null ? _data.shadowColor : _shadowColor; + + /// Overrides the default value for [BottomAppBar.padding]. + /// + /// This property is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.padding] property in [data] instead. + EdgeInsetsGeometry? get padding => _data != null ? _data.padding : _padding; + + /// The properties used for all descendant [BottomAppBar] widgets. + BottomAppBarThemeData get data => + _data ?? + BottomAppBarThemeData( + color: _color, + elevation: _elevation, + shape: _shape, + height: _height, + surfaceTintColor: _surfaceTintColor, + shadowColor: _shadowColor, + padding: _padding, + ); + + /// Creates a copy of this object but with the given fields replaced with the + /// new values. + /// + /// This method is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.copyWith] method instead. + BottomAppBarTheme copyWith({ + Color? color, + double? elevation, + NotchedShape? shape, + double? height, + Color? surfaceTintColor, + Color? shadowColor, + EdgeInsetsGeometry? padding, + }) { + return BottomAppBarTheme( + color: color ?? this.color, + elevation: elevation ?? this.elevation, + shape: shape ?? this.shape, + height: height ?? this.height, + surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor, + shadowColor: shadowColor ?? this.shadowColor, + padding: padding ?? this.padding, + ); + } + + /// Returns the closest [BottomAppBarThemeData] instance given the build context. + static BottomAppBarThemeData of(BuildContext context) { + final BottomAppBarTheme? bottomAppBarTheme = + context.dependOnInheritedWidgetOfExactType(); + return bottomAppBarTheme?.data ?? Theme.of(context).bottomAppBarTheme; + } + + /// Linearly interpolate between two bottom app bar themes. + /// + /// {@macro dart.ui.shadow.lerp} + /// + /// This method is obsolete and will be deprecated in a future release: + /// please use the [BottomAppBarThemeData.lerp] instead. + static BottomAppBarTheme lerp(BottomAppBarTheme? a, BottomAppBarTheme? b, double t) { + if (identical(a, b) && a != null) { + return a; + } + return BottomAppBarTheme( + color: Color.lerp(a?.color, b?.color, t), + elevation: lerpDouble(a?.elevation, b?.elevation, t), + shape: t < 0.5 ? a?.shape : b?.shape, + height: lerpDouble(a?.height, b?.height, t), + surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t), + shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t), + padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t), + ); + } + + @override + bool updateShouldNotify(BottomAppBarTheme oldWidget) => data != oldWidget.data; + + @override + Widget wrap(BuildContext context, Widget child) { + return BottomAppBarTheme(data: data, child: child); + } +} + +/// Defines default property values for descendant [BottomAppBar] widgets. +/// +/// Descendant widgets obtain the current [BottomAppBarThemeData] object using +/// [BottomAppBarTheme.of]. Instances of [BottomAppBarThemeData] can be +/// customized with [BottomAppBarThemeData.copyWith]. +/// +/// Typically a [BottomAppBarThemeData] is specified as part of the overall [Theme] +/// with [ThemeData.bottomAppBarTheme]. +/// +/// All [BottomAppBarThemeData] properties are `null` by default. When null, the [BottomAppBar] +/// will use the values from [ThemeData] if they exist, otherwise it will +/// provide its own defaults. See the individual [BottomAppBar] properties for details. +/// +/// See also: +/// +/// * [BottomAppBar], which is the widget that this theme configures. +/// * [ThemeData], which describes the overall theme information for the +/// application. +@immutable +class BottomAppBarThemeData with Diagnosticable { + /// Creates a bottom app bar theme that can be used with [ThemeData.bottomAppBarTheme]. + const BottomAppBarThemeData({ this.color, this.elevation, this.shape, @@ -69,7 +252,7 @@ class BottomAppBarTheme with Diagnosticable { /// Creates a copy of this object but with the given fields replaced with the /// new values. - BottomAppBarTheme copyWith({ + BottomAppBarThemeData copyWith({ Color? color, double? elevation, NotchedShape? shape, @@ -78,7 +261,7 @@ class BottomAppBarTheme with Diagnosticable { Color? shadowColor, EdgeInsetsGeometry? padding, }) { - return BottomAppBarTheme( + return BottomAppBarThemeData( color: color ?? this.color, elevation: elevation ?? this.elevation, shape: shape ?? this.shape, @@ -89,19 +272,14 @@ class BottomAppBarTheme with Diagnosticable { ); } - /// The [ThemeData.bottomAppBarTheme] property of the ambient [Theme]. - static BottomAppBarTheme of(BuildContext context) { - return Theme.of(context).bottomAppBarTheme; - } - - /// Linearly interpolate between two BAB themes. + /// Linearly interpolate between two bottom app bar themes. /// /// {@macro dart.ui.shadow.lerp} - static BottomAppBarTheme lerp(BottomAppBarTheme? a, BottomAppBarTheme? b, double t) { + static BottomAppBarThemeData lerp(BottomAppBarThemeData? a, BottomAppBarThemeData? b, double t) { if (identical(a, b) && a != null) { return a; } - return BottomAppBarTheme( + return BottomAppBarThemeData( color: Color.lerp(a?.color, b?.color, t), elevation: lerpDouble(a?.elevation, b?.elevation, t), shape: t < 0.5 ? a?.shape : b?.shape, @@ -124,7 +302,7 @@ class BottomAppBarTheme with Diagnosticable { if (other.runtimeType != runtimeType) { return false; } - return other is BottomAppBarTheme && + return other is BottomAppBarThemeData && other.color == color && other.elevation == elevation && other.shape == shape && @@ -138,11 +316,13 @@ class BottomAppBarTheme with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(ColorProperty('color', color, defaultValue: null)); - properties.add(DiagnosticsProperty('elevation', elevation, defaultValue: null)); - properties.add(DiagnosticsProperty('shape', shape, defaultValue: null)); - properties.add(DiagnosticsProperty('height', height, defaultValue: null)); + properties.add(DoubleProperty('elevation', elevation, defaultValue: null)); + properties.add(DiagnosticsProperty('shape', shape, defaultValue: null)); + properties.add(DoubleProperty('height', height, defaultValue: null)); properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null)); properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null)); - properties.add(DiagnosticsProperty('padding', padding, defaultValue: null)); + properties.add( + DiagnosticsProperty('padding', padding, defaultValue: null), + ); } } diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index f01315b5ab2..8443784573f 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -320,7 +320,8 @@ class ThemeData with Diagnosticable { AppBarTheme? appBarTheme, BadgeThemeData? badgeTheme, MaterialBannerThemeData? bannerTheme, - BottomAppBarTheme? bottomAppBarTheme, + // TODO(huycozy): Change the parameter type to BottomAppBarThemeData + Object? bottomAppBarTheme, BottomNavigationBarThemeData? bottomNavigationBarTheme, BottomSheetThemeData? bottomSheetTheme, ButtonThemeData? buttonTheme, @@ -526,7 +527,16 @@ class ThemeData with Diagnosticable { appBarTheme ??= const AppBarTheme(); badgeTheme ??= const BadgeThemeData(); bannerTheme ??= const MaterialBannerThemeData(); - bottomAppBarTheme ??= const BottomAppBarTheme(); + // TODO(huycozy): Clean this up once the type of `bottomAppBarTheme` is changed to `BottomAppBarThemeData` + if (bottomAppBarTheme != null) { + if (bottomAppBarTheme is BottomAppBarTheme) { + bottomAppBarTheme = bottomAppBarTheme.data; + } else if (bottomAppBarTheme is! BottomAppBarThemeData) { + throw ArgumentError( + 'bottomAppBarTheme must be either a BottomAppBarThemeData or a BottomAppBarTheme', + ); + } + } bottomNavigationBarTheme ??= const BottomNavigationBarThemeData(); bottomSheetTheme ??= const BottomSheetThemeData(); cardTheme ??= const CardThemeData(); @@ -620,7 +630,9 @@ class ThemeData with Diagnosticable { appBarTheme: appBarTheme, badgeTheme: badgeTheme, bannerTheme: bannerTheme, - bottomAppBarTheme: bottomAppBarTheme, + // TODO(huycozy): Remove this type cast when bottomAppBarTheme is explicitly set to BottomAppBarThemeData + bottomAppBarTheme: + (bottomAppBarTheme as BottomAppBarThemeData?) ?? const BottomAppBarThemeData(), bottomNavigationBarTheme: bottomNavigationBarTheme, bottomSheetTheme: bottomSheetTheme, buttonTheme: buttonTheme, @@ -1292,7 +1304,7 @@ class ThemeData with Diagnosticable { final MaterialBannerThemeData bannerTheme; /// A theme for customizing the shape, elevation, and color of a [BottomAppBar]. - final BottomAppBarTheme bottomAppBarTheme; + final BottomAppBarThemeData bottomAppBarTheme; /// A theme for customizing the appearance and layout of [BottomNavigationBar] /// widgets. @@ -1519,7 +1531,8 @@ class ThemeData with Diagnosticable { AppBarTheme? appBarTheme, BadgeThemeData? badgeTheme, MaterialBannerThemeData? bannerTheme, - BottomAppBarTheme? bottomAppBarTheme, + // TODO(huycozy): Change the parameter type to BottomAppBarThemeData + Object? bottomAppBarTheme, BottomNavigationBarThemeData? bottomNavigationBarTheme, BottomSheetThemeData? bottomSheetTheme, ButtonThemeData? buttonTheme, @@ -1638,7 +1651,19 @@ class ThemeData with Diagnosticable { appBarTheme: appBarTheme ?? this.appBarTheme, badgeTheme: badgeTheme ?? this.badgeTheme, bannerTheme: bannerTheme ?? this.bannerTheme, - bottomAppBarTheme: bottomAppBarTheme ?? this.bottomAppBarTheme, + // TODO(huycozy): Remove these checks when bottomAppBarTheme is a BottomAppBarThemeData + bottomAppBarTheme: () { + if (bottomAppBarTheme != null) { + if (bottomAppBarTheme is BottomAppBarTheme) { + return bottomAppBarTheme.data; + } else if (bottomAppBarTheme is! BottomAppBarThemeData) { + throw ArgumentError( + 'bottomAppBarTheme must be either a BottomAppBarThemeData or a BottomAppBarTheme', + ); + } + } + return bottomAppBarTheme as BottomAppBarThemeData? ?? this.bottomAppBarTheme; + }(), bottomNavigationBarTheme: bottomNavigationBarTheme ?? this.bottomNavigationBarTheme, bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme, buttonTheme: buttonTheme ?? this.buttonTheme, @@ -1955,7 +1980,7 @@ class ThemeData with Diagnosticable { appBarTheme: AppBarTheme.lerp(a.appBarTheme, b.appBarTheme, t), badgeTheme: BadgeThemeData.lerp(a.badgeTheme, b.badgeTheme, t), bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t), - bottomAppBarTheme: BottomAppBarTheme.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t), + bottomAppBarTheme: BottomAppBarThemeData.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t), bottomNavigationBarTheme: BottomNavigationBarThemeData.lerp( a.bottomNavigationBarTheme, b.bottomNavigationBarTheme, @@ -2036,7 +2061,6 @@ class ThemeData with Diagnosticable { // order in every place that they are separated by section comments (e.g. // GENERAL CONFIGURATION). Each section except for deprecations should be // alphabetical by symbol name. - // GENERAL CONFIGURATION mapEquals(other.adaptationMap, adaptationMap) && other.applyElevationOverlayColor == applyElevationOverlayColor && other.cupertinoOverrideTheme == cupertinoOverrideTheme && @@ -2529,7 +2553,7 @@ class ThemeData with Diagnosticable { ), ); properties.add( - DiagnosticsProperty( + DiagnosticsProperty( 'bottomAppBarTheme', bottomAppBarTheme, defaultValue: defaultData.bottomAppBarTheme, diff --git a/packages/flutter/test/material/bottom_app_bar_test.dart b/packages/flutter/test/material/bottom_app_bar_test.dart index 1a26ae62b31..906aaa1c0d5 100644 --- a/packages/flutter/test/material/bottom_app_bar_test.dart +++ b/packages/flutter/test/material/bottom_app_bar_test.dart @@ -205,7 +205,7 @@ void main() { return Theme( data: Theme.of( context, - ).copyWith(bottomAppBarTheme: const BottomAppBarTheme(color: Color(0xffffff00))), + ).copyWith(bottomAppBarTheme: const BottomAppBarThemeData(color: Color(0xffffff00))), child: const Scaffold( floatingActionButton: FloatingActionButton(onPressed: null), bottomNavigationBar: BottomAppBar(), @@ -230,7 +230,7 @@ void main() { return Theme( data: Theme.of( context, - ).copyWith(bottomAppBarTheme: const BottomAppBarTheme(color: Color(0xffffff00))), + ).copyWith(bottomAppBarTheme: const BottomAppBarThemeData(color: Color(0xffffff00))), child: const Scaffold( floatingActionButton: FloatingActionButton(onPressed: null), bottomNavigationBar: BottomAppBar(color: Color(0xff0000ff)), @@ -251,7 +251,7 @@ void main() { testWidgets('Material3 - Color overrides theme color', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( - theme: ThemeData(bottomAppBarTheme: const BottomAppBarTheme(color: Color(0xffffff00))), + theme: ThemeData(bottomAppBarTheme: const BottomAppBarThemeData(color: Color(0xffffff00))), home: Builder( builder: (BuildContext context) { return const Scaffold( diff --git a/packages/flutter/test/material/bottom_app_bar_theme_test.dart b/packages/flutter/test/material/bottom_app_bar_theme_test.dart index 9f052d4a180..63b0cc4fe51 100644 --- a/packages/flutter/test/material/bottom_app_bar_theme_test.dart +++ b/packages/flutter/test/material/bottom_app_bar_theme_test.dart @@ -7,22 +7,133 @@ @Tags(['reduced-test-set']) library; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - test('BottomAppBarTheme lerp special cases', () { - expect(BottomAppBarTheme.lerp(null, null, 0), const BottomAppBarTheme()); - const BottomAppBarTheme data = BottomAppBarTheme(); - expect(identical(BottomAppBarTheme.lerp(data, data, 0.5), data), true); + test('BottomAppBarThemeData copyWith, ==, hashCode, defaults', () { + expect(const BottomAppBarThemeData(), const BottomAppBarThemeData().copyWith()); + expect( + const BottomAppBarThemeData().hashCode, + const BottomAppBarThemeData().copyWith().hashCode, + ); + expect(const BottomAppBarThemeData().color, null); + expect(const BottomAppBarThemeData().elevation, null); + expect(const BottomAppBarThemeData().shadowColor, null); + expect(const BottomAppBarThemeData().shape, null); + expect(const BottomAppBarThemeData().height, null); + expect(const BottomAppBarThemeData().surfaceTintColor, null); + expect(const BottomAppBarThemeData().padding, null); + }); + + test('BottomAppBarThemeData lerp special cases', () { + const BottomAppBarThemeData theme = BottomAppBarThemeData(); + expect(identical(BottomAppBarThemeData.lerp(theme, theme, 0.5), theme), true); + }); + + testWidgets('Default BottomAppBarThemeData debugFillProperties', (WidgetTester tester) async { + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + const BottomAppBarThemeData().debugFillProperties(builder); + + final List description = + builder.properties + .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) + .map((DiagnosticsNode node) => node.toString()) + .toList(); + + expect(description, []); + }); + + testWidgets('BottomAppBarThemeData implements debugFillProperties', (WidgetTester tester) async { + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + const BottomAppBarThemeData( + color: Color(0xffff0000), + elevation: 1.0, + shape: CircularNotchedRectangle(), + height: 1.0, + shadowColor: Color(0xff0000ff), + surfaceTintColor: Color(0xff00ff00), + padding: EdgeInsets.all(8), + ).debugFillProperties(builder); + + final List description = + builder.properties + .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) + .map((DiagnosticsNode node) => node.toString()) + .toList(); + + expect(description, [ + 'color: ${const Color(0xffff0000)}', + 'elevation: 1.0', + "shape: Instance of 'CircularNotchedRectangle'", + 'height: 1.0', + 'surfaceTintColor: ${const Color(0xff00ff00)}', + 'shadowColor: ${const Color(0xff0000ff)}', + 'padding: EdgeInsets.all(8.0)', + ]); + }); + + testWidgets('Local BottomAppBarTheme overrides defaults', (WidgetTester tester) async { + const Color color = Colors.blueAccent; + const double elevation = 1.0; + const Color shadowColor = Colors.black87; + const double height = 100.0; + const Color surfaceTintColor = Colors.transparent; + const NotchedShape shape = CircularNotchedRectangle(); + const EdgeInsetsGeometry padding = EdgeInsets.all(8); + const BottomAppBarThemeData themeData = BottomAppBarThemeData( + color: color, + elevation: elevation, + shadowColor: shadowColor, + shape: shape, + height: height, + surfaceTintColor: surfaceTintColor, + padding: padding, + ); + + await tester.pumpWidget(_withTheme(localBABTheme: themeData)); + + final PhysicalShape widget = _getBabRenderObject(tester); + expect(widget.color, themeData.color); + expect(widget.elevation, themeData.elevation); + expect(widget.shadowColor, themeData.shadowColor); + + final RenderBox renderBox = tester.renderObject(find.byType(BottomAppBar)); + expect(renderBox.size.height, themeData.height); + + final bool hasFab = + Scaffold.of(tester.element(find.byType(BottomAppBar))).hasFloatingActionButton; + if (hasFab) { + expect(widget.clipper.toString(), '_BottomAppBarClipper'); + } else { + expect(widget.clipper, isA()); + final ShapeBorderClipper clipper = widget.clipper as ShapeBorderClipper; + expect(clipper.shape, isA()); + } + + final Color effectiveColor = ElevationOverlay.applySurfaceTint( + themeData.color!, + themeData.surfaceTintColor, + themeData.elevation!, + ); + expect(widget.color, effectiveColor); + + // The BottomAppBar has two Padding widgets in its hierarchy: + // 1. The first Padding is from the SafeArea widget. + // 2. The second Padding is the one that applies the theme's padding. + final Padding paddingWidget = tester.widget( + find.descendant(of: find.byType(BottomAppBar), matching: find.byType(Padding).at(1)), + ); + expect(paddingWidget.padding, padding); }); group('Material 2 tests', () { testWidgets('Material2 - BAB theme overrides color', (WidgetTester tester) async { const Color themedColor = Colors.black87; - const BottomAppBarTheme theme = BottomAppBarTheme(color: themedColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData(color: themedColor); - await tester.pumpWidget(_withTheme(theme, useMaterial3: false)); + await tester.pumpWidget(_withTheme(babTheme: theme, useMaterial3: false)); final PhysicalShape widget = _getBabRenderObject(tester); expect(widget.color, themedColor); @@ -31,7 +142,7 @@ void main() { testWidgets('Material2 - BAB color - Widget', (WidgetTester tester) async { const Color babThemeColor = Colors.black87; const Color babColor = Colors.pink; - const BottomAppBarTheme theme = BottomAppBarTheme(color: babThemeColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData(color: babThemeColor); await tester.pumpWidget( MaterialApp( @@ -46,7 +157,7 @@ void main() { testWidgets('Material2 - BAB color - BabTheme', (WidgetTester tester) async { const Color babThemeColor = Colors.black87; - const BottomAppBarTheme theme = BottomAppBarTheme(color: babThemeColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData(color: babThemeColor); await tester.pumpWidget( MaterialApp( @@ -66,7 +177,7 @@ void main() { MaterialApp( theme: ThemeData( useMaterial3: false, - bottomAppBarTheme: const BottomAppBarTheme(color: themeColor), + bottomAppBarTheme: const BottomAppBarThemeData(color: themeColor), ), home: const Scaffold(body: BottomAppBar()), ), @@ -90,13 +201,13 @@ void main() { }); testWidgets('Material2 - BAB theme customizes shape', (WidgetTester tester) async { - const BottomAppBarTheme theme = BottomAppBarTheme( + const BottomAppBarThemeData theme = BottomAppBarThemeData( color: Colors.white30, shape: CircularNotchedRectangle(), elevation: 1.0, ); - await tester.pumpWidget(_withTheme(theme, useMaterial3: false)); + await tester.pumpWidget(_withTheme(babTheme: theme, useMaterial3: false)); await expectLater( find.byKey(_painterKey), @@ -122,8 +233,8 @@ void main() { group('Material 3 tests', () { testWidgets('Material3 - BAB theme overrides color', (WidgetTester tester) async { const Color themedColor = Colors.black87; - const BottomAppBarTheme theme = BottomAppBarTheme(color: themedColor, elevation: 0); - await tester.pumpWidget(_withTheme(theme, useMaterial3: true)); + const BottomAppBarThemeData theme = BottomAppBarThemeData(color: themedColor, elevation: 0); + await tester.pumpWidget(_withTheme(babTheme: theme)); final PhysicalShape widget = _getBabRenderObject(tester); expect(widget.color, themedColor); @@ -132,7 +243,7 @@ void main() { testWidgets('Material3 - BAB color - Widget', (WidgetTester tester) async { const Color babThemeColor = Colors.black87; const Color babColor = Colors.pink; - const BottomAppBarTheme theme = BottomAppBarTheme(color: babThemeColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData(color: babThemeColor); await tester.pumpWidget( MaterialApp( @@ -149,7 +260,7 @@ void main() { testWidgets('Material3 - BAB color - BabTheme', (WidgetTester tester) async { const Color babThemeColor = Colors.black87; - const BottomAppBarTheme theme = BottomAppBarTheme(color: babThemeColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData(color: babThemeColor); await tester.pumpWidget( MaterialApp( @@ -180,12 +291,12 @@ void main() { testWidgets('Material3 - BAB theme overrides surfaceTintColor', (WidgetTester tester) async { const Color color = Colors.blue; // base color that the surface tint will be applied to const Color babThemeSurfaceTintColor = Colors.black87; - const BottomAppBarTheme theme = BottomAppBarTheme( + const BottomAppBarThemeData theme = BottomAppBarThemeData( color: color, surfaceTintColor: babThemeSurfaceTintColor, elevation: 0, ); - await tester.pumpWidget(_withTheme(theme, useMaterial3: true)); + await tester.pumpWidget(_withTheme(babTheme: theme)); final PhysicalShape widget = _getBabRenderObject(tester); expect(widget.color, ElevationOverlay.applySurfaceTint(color, babThemeSurfaceTintColor, 0)); @@ -193,11 +304,11 @@ void main() { testWidgets('Material3 - BAB theme overrides shadowColor', (WidgetTester tester) async { const Color babThemeShadowColor = Colors.yellow; - const BottomAppBarTheme theme = BottomAppBarTheme( + const BottomAppBarThemeData theme = BottomAppBarThemeData( shadowColor: babThemeShadowColor, elevation: 0, ); - await tester.pumpWidget(_withTheme(theme, useMaterial3: true)); + await tester.pumpWidget(_withTheme(babTheme: theme)); final PhysicalShape widget = _getBabRenderObject(tester); expect(widget.shadowColor, babThemeShadowColor); @@ -207,7 +318,9 @@ void main() { const Color color = Colors.white10; // base color that the surface tint will be applied to const Color babThemeSurfaceTintColor = Colors.black87; const Color babSurfaceTintColor = Colors.pink; - const BottomAppBarTheme theme = BottomAppBarTheme(surfaceTintColor: babThemeSurfaceTintColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData( + surfaceTintColor: babThemeSurfaceTintColor, + ); await tester.pumpWidget( MaterialApp( theme: ThemeData(bottomAppBarTheme: theme), @@ -224,7 +337,7 @@ void main() { testWidgets('Material3 - BAB surfaceTintColor - BabTheme', (WidgetTester tester) async { const Color color = Colors.blue; // base color that the surface tint will be applied to const Color babThemeColor = Colors.black87; - const BottomAppBarTheme theme = BottomAppBarTheme(surfaceTintColor: babThemeColor); + const BottomAppBarThemeData theme = BottomAppBarThemeData(surfaceTintColor: babThemeColor); await tester.pumpWidget( MaterialApp( @@ -247,20 +360,23 @@ PhysicalShape _getBabRenderObject(WidgetTester tester) { final Key _painterKey = UniqueKey(); -Widget _withTheme(BottomAppBarTheme theme, {required bool useMaterial3}) { +Widget _withTheme({ + BottomAppBarThemeData? babTheme, + BottomAppBarThemeData? localBABTheme, + bool useMaterial3 = true, +}) { + Widget babWidget = const BottomAppBar( + child: Row(children: [Icon(Icons.add), Expanded(child: SizedBox()), Icon(Icons.add)]), + ); + if (localBABTheme != null) { + babWidget = BottomAppBarTheme(data: localBABTheme, child: babWidget); + } return MaterialApp( - theme: ThemeData(useMaterial3: useMaterial3, bottomAppBarTheme: theme), + theme: ThemeData(useMaterial3: useMaterial3, bottomAppBarTheme: babTheme), home: Scaffold( floatingActionButton: const FloatingActionButton(onPressed: null), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - bottomNavigationBar: RepaintBoundary( - key: _painterKey, - child: const BottomAppBar( - child: Row( - children: [Icon(Icons.add), Expanded(child: SizedBox()), Icon(Icons.add)], - ), - ), - ), + bottomNavigationBar: RepaintBoundary(key: _painterKey, child: babWidget), ), ); } diff --git a/packages/flutter/test/material/theme_data_test.dart b/packages/flutter/test/material/theme_data_test.dart index 328a7c0eaed..5f8a286c263 100644 --- a/packages/flutter/test/material/theme_data_test.dart +++ b/packages/flutter/test/material/theme_data_test.dart @@ -1332,7 +1332,7 @@ void main() { appBarTheme: const AppBarTheme(backgroundColor: Colors.black), badgeTheme: const BadgeThemeData(backgroundColor: Colors.black), bannerTheme: const MaterialBannerThemeData(backgroundColor: Colors.black), - bottomAppBarTheme: const BottomAppBarTheme(color: Colors.black), + bottomAppBarTheme: const BottomAppBarThemeData(color: Colors.black), bottomNavigationBarTheme: const BottomNavigationBarThemeData( type: BottomNavigationBarType.fixed, ), @@ -1461,7 +1461,7 @@ void main() { appBarTheme: const AppBarTheme(backgroundColor: Colors.white), badgeTheme: const BadgeThemeData(backgroundColor: Colors.black), bannerTheme: const MaterialBannerThemeData(backgroundColor: Colors.white), - bottomAppBarTheme: const BottomAppBarTheme(color: Colors.white), + bottomAppBarTheme: const BottomAppBarThemeData(color: Colors.white), bottomNavigationBarTheme: const BottomNavigationBarThemeData( type: BottomNavigationBarType.shifting, ),