diff --git a/dev/tools/gen_defaults/lib/tabs_template.dart b/dev/tools/gen_defaults/lib/tabs_template.dart index 11febe6e6281e..fbe7c6f766456 100644 --- a/dev/tools/gen_defaults/lib/tabs_template.dart +++ b/dev/tools/gen_defaults/lib/tabs_template.dart @@ -12,7 +12,7 @@ class TabsTemplate extends TokenTemplate { @override String generate() => ''' -class _${blockName}PrimaryDefaultsM3 extends TabBarThemeData { +class _${blockName}PrimaryDefaultsM3 extends TabBarTheme { _${blockName}PrimaryDefaultsM3(this.context, this.isScrollable) : super(indicatorSize: TabBarIndicatorSize.label); @@ -91,7 +91,7 @@ class _${blockName}PrimaryDefaultsM3 extends TabBarThemeData { static const EdgeInsetsGeometry iconMargin = EdgeInsets.only(bottom: 2); } -class _${blockName}SecondaryDefaultsM3 extends TabBarThemeData { +class _${blockName}SecondaryDefaultsM3 extends TabBarTheme { _${blockName}SecondaryDefaultsM3(this.context, this.isScrollable) : super(indicatorSize: TabBarIndicatorSize.tab); diff --git a/packages/flutter/lib/src/material/tab_bar_theme.dart b/packages/flutter/lib/src/material/tab_bar_theme.dart index 4dbe745455ceb..64f95bf4fcd29 100644 --- a/packages/flutter/lib/src/material/tab_bar_theme.dart +++ b/packages/flutter/lib/src/material/tab_bar_theme.dart @@ -12,305 +12,22 @@ import 'theme.dart'; /// Defines a theme for [TabBar] widgets. /// -/// Descendant widgets obtain the current [TabBarTheme] object using -/// `TabBarTheme.of(context)`. +/// A tab bar theme describes the color of the tab label and the size/shape of +/// the [TabBar.indicator]. /// -/// See also: -/// -/// * [TabBarThemeData], which describes the actual configuration of a switch -/// theme. -@immutable -class TabBarTheme extends InheritedTheme with Diagnosticable { - /// Creates a tab bar theme that can be used with [ThemeData.tabBarTheme]. - const TabBarTheme({ - super.key, - Decoration? indicator, - Color? indicatorColor, - TabBarIndicatorSize? indicatorSize, - Color? dividerColor, - double? dividerHeight, - Color? labelColor, - EdgeInsetsGeometry? labelPadding, - TextStyle? labelStyle, - Color? unselectedLabelColor, - TextStyle? unselectedLabelStyle, - WidgetStateProperty? overlayColor, - InteractiveInkFeatureFactory? splashFactory, - WidgetStateProperty? mouseCursor, - TabAlignment? tabAlignment, - TextScaler? textScaler, - TabIndicatorAnimation? indicatorAnimation, - TabBarThemeData? data, - Widget? child, - }) : assert( - data == null || - (indicator ?? indicatorColor ?? indicatorSize ?? dividerColor ?? dividerHeight - ?? labelColor ?? labelPadding ?? labelStyle ?? unselectedLabelColor ?? unselectedLabelStyle - ?? overlayColor ?? splashFactory ?? mouseCursor ?? tabAlignment ?? textScaler - ?? indicatorAnimation) == null), - _indicator = indicator, - _indicatorColor = indicatorColor, - _indicatorSize = indicatorSize, - _dividerColor = dividerColor, - _dividerHeight = dividerHeight, - _labelColor = labelColor, - _labelPadding = labelPadding, - _labelStyle = labelStyle, - _unselectedLabelColor = unselectedLabelColor, - _unselectedLabelStyle = unselectedLabelStyle, - _overlayColor = overlayColor, - _splashFactory = splashFactory, - _mouseCursor = mouseCursor, - _tabAlignment = tabAlignment, - _textScaler = textScaler, - _indicatorAnimation = indicatorAnimation, - _data = data, - super(child: child ?? const SizedBox()); - - final TabBarThemeData? _data; - final Decoration? _indicator; - final Color? _indicatorColor; - final TabBarIndicatorSize? _indicatorSize; - final Color? _dividerColor; - final double? _dividerHeight; - final Color? _labelColor; - final EdgeInsetsGeometry? _labelPadding; - final TextStyle? _labelStyle; - final Color? _unselectedLabelColor; - final TextStyle? _unselectedLabelStyle; - final MaterialStateProperty? _overlayColor; - final InteractiveInkFeatureFactory? _splashFactory; - final MaterialStateProperty? _mouseCursor; - final TabAlignment? _tabAlignment; - final TextScaler? _textScaler; - final TabIndicatorAnimation? _indicatorAnimation; - - /// Overrides the default value for [TabBar.indicator]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.indicator] property in [data] instead. - Decoration? get indicator => _data != null ? _data.indicator : _indicator; - - /// Overrides the default value for [TabBar.indicatorColor]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.indicatorColor] property in [data] instead. - Color? get indicatorColor => _data != null ? _data.indicatorColor : _indicatorColor; - - /// Overrides the default value for [TabBar.indicatorSize]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.indicatorSize] property in [data] instead. - TabBarIndicatorSize? get indicatorSize => _data != null ? _data.indicatorSize : _indicatorSize; - - /// Overrides the default value for [TabBar.dividerColor]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.dividerColor] property in [data] instead. - Color? get dividerColor => _data != null ? _data.dividerColor : _dividerColor; - - /// Overrides the default value for [TabBar.dividerHeight]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.dividerHeight] property in [data] instead. - double? get dividerHeight => _data != null ? _data.dividerHeight : _dividerHeight; - - /// Overrides the default value for [TabBar.labelColor]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.labelColor] property in [data] instead. - Color? get labelColor => _data != null ? _data.labelColor : _labelColor; - - /// Overrides the default value for [TabBar.labelPadding]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.labelPadding] property in [data] instead. - EdgeInsetsGeometry? get labelPadding => _data != null ? _data.labelPadding : _labelPadding; - - /// Overrides the default value for [TabBar.labelStyle]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.labelStyle] property in [data] instead. - TextStyle? get labelStyle => _data != null ? _data.labelStyle : _labelStyle; - - /// Overrides the default value for [TabBar.unselectedLabelColor]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.unselectedLabelColor] property in [data] instead. - Color? get unselectedLabelColor => _data != null ? _data.unselectedLabelColor : _unselectedLabelColor; - - /// Overrides the default value for [TabBar.unselectedLabelStyle]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.unselectedLabelStyle] property in [data] instead. - TextStyle? get unselectedLabelStyle => _data != null ? _data.unselectedLabelStyle : _unselectedLabelStyle; - - /// Overrides the default value for [TabBar.overlayColor]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.overlayColor] property in [data] instead. - MaterialStateProperty? get overlayColor => _data != null ? _data.overlayColor : _overlayColor; - - /// Overrides the default value for [TabBar.splashFactory]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.splashFactory] property in [data] instead. - InteractiveInkFeatureFactory? get splashFactory => _data != null ? _data.splashFactory : _splashFactory; - - /// Overrides the default value of [TabBar.mouseCursor]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.mouseCursor] property in [data] instead. - MaterialStateProperty? get mouseCursor => _data != null ? _data.mouseCursor : _mouseCursor; - - /// Overrides the default value for [TabBar.tabAlignment]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.tabAlignment] property in [data] instead. - TabAlignment? get tabAlignment => _data != null ? _data.tabAlignment : _tabAlignment; - - /// Overrides the default value for [TabBar.textScaler]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.textScaler] property in [data] instead. - TextScaler? get textScaler => _data != null ? _data.textScaler : _textScaler; - - /// Overrides the default value for [TabBar.indicatorAnimation]. - /// - /// This property is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.indicatorAnimation] property in [data] instead. - TabIndicatorAnimation? get indicatorAnimation => _data != null ? _data.indicatorAnimation : _indicatorAnimation; - - /// The properties used for all descendant [TabBar] widgets. - TabBarThemeData get data => _data ?? TabBarThemeData( - indicator: _indicator, - indicatorColor: _indicatorColor, - indicatorSize: _indicatorSize, - dividerColor: _dividerColor, - dividerHeight: _dividerHeight, - labelColor: _labelColor, - labelPadding: _labelPadding, - labelStyle: _labelStyle, - unselectedLabelColor: _unselectedLabelColor, - unselectedLabelStyle: _unselectedLabelStyle, - overlayColor: _overlayColor, - splashFactory: _splashFactory, - mouseCursor: _mouseCursor, - tabAlignment: _tabAlignment, - textScaler: _textScaler, - indicatorAnimation: _indicatorAnimation, - ); - - /// 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 [TabBarThemeData.copyWith] instead. - TabBarTheme copyWith({ - Decoration? indicator, - Color? indicatorColor, - TabBarIndicatorSize? indicatorSize, - Color? dividerColor, - double? dividerHeight, - Color? labelColor, - EdgeInsetsGeometry? labelPadding, - TextStyle? labelStyle, - Color? unselectedLabelColor, - TextStyle? unselectedLabelStyle, - MaterialStateProperty? overlayColor, - InteractiveInkFeatureFactory? splashFactory, - MaterialStateProperty? mouseCursor, - TabAlignment? tabAlignment, - TextScaler? textScaler, - TabIndicatorAnimation? indicatorAnimation, - }) { - return TabBarTheme( - indicator: indicator ?? this.indicator, - indicatorColor: indicatorColor ?? this.indicatorColor, - indicatorSize: indicatorSize ?? this.indicatorSize, - dividerColor: dividerColor ?? this.dividerColor, - dividerHeight: dividerHeight ?? this.dividerHeight, - labelColor: labelColor ?? this.labelColor, - labelPadding: labelPadding ?? this.labelPadding, - labelStyle: labelStyle ?? this.labelStyle, - unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor, - unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle, - overlayColor: overlayColor ?? this.overlayColor, - splashFactory: splashFactory ?? this.splashFactory, - mouseCursor: mouseCursor ?? this.mouseCursor, - tabAlignment: tabAlignment ?? this.tabAlignment, - textScaler: textScaler ?? this.textScaler, - indicatorAnimation: indicatorAnimation ?? this.indicatorAnimation, - ); - } - - /// Returns the closest [TabBarTheme] instance given the build context. - static TabBarTheme of(BuildContext context) { - final TabBarTheme? tabBarTheme = context.dependOnInheritedWidgetOfExactType(); - return tabBarTheme ?? Theme.of(context).tabBarTheme; - } - - /// Linearly interpolate between two tab bar themes. - /// - /// {@macro dart.ui.shadow.lerp} - /// - /// This method is obsolete and will be deprecated in a future release: - /// please use the [TabBarThemeData.lerp] instead. - static TabBarTheme lerp(TabBarTheme a, TabBarTheme b, double t) { - if (identical(a, b)) { - return a; - } - return TabBarTheme( - indicator: Decoration.lerp(a.indicator, b.indicator, t), - indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t), - indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize, - dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t), - dividerHeight: t < 0.5 ? a.dividerHeight : b.dividerHeight, - labelColor: Color.lerp(a.labelColor, b.labelColor, t), - labelPadding: EdgeInsetsGeometry.lerp(a.labelPadding, b.labelPadding, t), - labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t), - unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t), - unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, t), - overlayColor: MaterialStateProperty.lerp(a.overlayColor, b.overlayColor, t, Color.lerp), - splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory, - mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor, - tabAlignment: t < 0.5 ? a.tabAlignment : b.tabAlignment, - textScaler: t < 0.5 ? a.textScaler : b.textScaler, - indicatorAnimation: t < 0.5 ? a.indicatorAnimation : b.indicatorAnimation, - ); - } - - @override - bool updateShouldNotify(TabBarTheme oldWidget) => data != oldWidget.data; - - @override - Widget wrap(BuildContext context, Widget child) { - return TabBarTheme(data: data, child: child); - } -} - -/// Defines default property values for descendant [TabBar] widgets. -/// -/// Descendant widgets obtain the current [TabBarThemeData] object using -/// `TabBarTheme.of(context).data`. Instances of [TabBarThemeData] can be -/// customized with [TabBarThemeData.copyWith]. -/// -/// Typically a [TabBarThemeData] is specified as part of the overall [Theme] -/// with [ThemeData.tabBarTheme]. -/// -/// All [TabBarThemeData] properties are `null` by default. When null, the [TabBar] -/// will use the values from [ThemeData] if they exist, otherwise it will -/// provide its own defaults. See the individual [TabBar] properties for details. +/// Descendant widgets obtain the current theme's [TabBarTheme] object using +/// `TabBarTheme.of(context)`. Instances of [TabBarTheme] can be customized with +/// [TabBarTheme.copyWith]. /// /// See also: /// -/// * [TabBar], which displays a row of tabs. +/// * [TabBar], a widget that displays a horizontal row of tabs. /// * [ThemeData], which describes the overall theme information for the /// application. @immutable -class TabBarThemeData with Diagnosticable { +class TabBarTheme with Diagnosticable { /// Creates a tab bar theme that can be used with [ThemeData.tabBarTheme]. - const TabBarThemeData({ + const TabBarTheme({ this.indicator, this.indicatorColor, this.indicatorSize, @@ -391,7 +108,7 @@ class TabBarThemeData with Diagnosticable { /// Creates a copy of this object but with the given fields replaced with the /// new values. - TabBarThemeData copyWith({ + TabBarTheme copyWith({ Decoration? indicator, Color? indicatorColor, TabBarIndicatorSize? indicatorSize, @@ -409,7 +126,7 @@ class TabBarThemeData with Diagnosticable { TextScaler? textScaler, TabIndicatorAnimation? indicatorAnimation, }) { - return TabBarThemeData( + return TabBarTheme( indicator: indicator ?? this.indicator, indicatorColor: indicatorColor ?? this.indicatorColor, indicatorSize: indicatorSize ?? this.indicatorSize, @@ -429,14 +146,19 @@ class TabBarThemeData with Diagnosticable { ); } + /// The data from the closest [TabBarTheme] instance given the build context. + static TabBarTheme of(BuildContext context) { + return Theme.of(context).tabBarTheme; + } + /// Linearly interpolate between two tab bar themes. /// /// {@macro dart.ui.shadow.lerp} - static TabBarThemeData lerp(TabBarThemeData a, TabBarThemeData b, double t) { + static TabBarTheme lerp(TabBarTheme a, TabBarTheme b, double t) { if (identical(a, b)) { return a; } - return TabBarThemeData( + return TabBarTheme( indicator: Decoration.lerp(a.indicator, b.indicator, t), indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t), indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize, @@ -484,7 +206,7 @@ class TabBarThemeData with Diagnosticable { if (other.runtimeType != runtimeType) { return false; } - return other is TabBarThemeData + return other is TabBarTheme && other.indicator == indicator && other.indicatorColor == indicatorColor && other.indicatorSize == indicatorSize @@ -502,25 +224,4 @@ class TabBarThemeData with Diagnosticable { && other.textScaler == textScaler && other.indicatorAnimation == indicatorAnimation; } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('indicator', indicator, defaultValue: null)); - properties.add(DiagnosticsProperty('indicatorColor', indicatorColor, defaultValue: null)); - properties.add(DiagnosticsProperty('indicatorSize', indicatorSize, defaultValue: null)); - properties.add(DiagnosticsProperty('dividerColor', dividerColor, defaultValue: null)); - properties.add(DiagnosticsProperty('dividerHeight', dividerHeight, defaultValue: null)); - properties.add(DiagnosticsProperty('labelColor', labelColor, defaultValue: null)); - properties.add(DiagnosticsProperty('labelPadding', labelPadding, defaultValue: null)); - properties.add(DiagnosticsProperty('labelStyle', labelStyle, defaultValue: null)); - properties.add(DiagnosticsProperty('unselectedLabelColor', unselectedLabelColor, defaultValue: null)); - properties.add(DiagnosticsProperty('unselectedLabelStyle', unselectedLabelStyle, defaultValue: null)); - properties.add(DiagnosticsProperty?>('overlayColor', overlayColor, defaultValue: null)); - properties.add(DiagnosticsProperty('splashFactory', splashFactory, defaultValue: null)); - properties.add(DiagnosticsProperty?>('mouseCursor', mouseCursor, defaultValue: null)); - properties.add(DiagnosticsProperty('tabAlignment', tabAlignment, defaultValue: null)); - properties.add(DiagnosticsProperty('textScaler', textScaler, defaultValue: null)); - properties.add(DiagnosticsProperty('indicatorAnimation', indicatorAnimation, defaultValue: null)); - } } diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 45de852821112..bc6687466a482 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -92,7 +92,7 @@ enum TabAlignment { /// /// See also: /// * [TabBar], which displays a row of tabs. -/// * [TabBarThemeData], which can be used to configure the appearance of the tab +/// * [TabBarTheme], which can be used to configure the appearance of the tab /// indicator. enum TabIndicatorAnimation { /// The tab indicator animates linearly. @@ -241,12 +241,12 @@ class _TabStyle extends AnimatedWidget { final bool isPrimary; final Color? labelColor; final Color? unselectedLabelColor; - final TabBarThemeData defaults; + final TabBarTheme defaults; final Widget child; MaterialStateColor _resolveWithLabelColor(BuildContext context) { final ThemeData themeData = Theme.of(context); - final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data; + final TabBarTheme tabBarTheme = TabBarTheme.of(context); final Animation animation = listenable as Animation; // labelStyle.color (and tabBarTheme.labelStyle.color) is not considered @@ -285,7 +285,7 @@ class _TabStyle extends AnimatedWidget { @override Widget build(BuildContext context) { - final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data; + final TabBarTheme tabBarTheme = TabBarTheme.of(context); final Animation animation = listenable as Animation; final Set states = isSelected @@ -790,7 +790,7 @@ class _TabBarScrollController extends ScrollController { /// /// Requires one of its ancestors to be a [Material] widget. /// -/// Uses values from [TabBarThemeData] if it is set in the current context. +/// Uses values from [TabBarTheme] if it is set in the current context. /// /// {@tool dartpad} /// This sample shows the implementation of [TabBar] and [TabBarView] using a [DefaultTabController]. @@ -954,7 +954,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// If this parameter is null, then the value of the Theme's indicatorColor /// property is used. /// - /// If [indicator] is specified or provided from [TabBarThemeData], + /// If [indicator] is specified or provided from [TabBarTheme], /// this property is ignored. final Color? indicatorColor; @@ -971,7 +971,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// /// If [ThemeData.useMaterial3] is false, the default value is 2.0. /// - /// If [indicator] is specified or provided from [TabBarThemeData], + /// If [indicator] is specified or provided from [TabBarTheme], /// this property is ignored. final double indicatorWeight; @@ -986,7 +986,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// Defines the appearance of the selected tab indicator. /// - /// If [indicator] is specified or provided from [TabBarThemeData], + /// If [indicator] is specified or provided from [TabBarTheme], /// the [indicatorColor] and [indicatorWeight] properties are ignored. /// /// The default, underline-style, selected tab indicator can be defined with @@ -1030,7 +1030,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// /// If the [dividerColor] is [Colors.transparent], then the divider will not be drawn. /// - /// If null and [ThemeData.useMaterial3] is false, [TabBarThemeData.dividerColor] + /// If null and [ThemeData.useMaterial3] is false, [TabBarTheme.dividerColor] /// color is used. If that is null and [ThemeData.useMaterial3] is true, /// [ColorScheme.outlineVariant] will be used, otherwise divider will not be drawn. final Color? dividerColor; @@ -1039,26 +1039,26 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// /// If the [dividerHeight] is zero or negative, then the divider will not be drawn. /// - /// If null and [ThemeData.useMaterial3] is true, [TabBarThemeData.dividerHeight] is used. + /// If null and [ThemeData.useMaterial3] is true, [TabBarTheme.dividerHeight] is used. /// If that is also null and [ThemeData.useMaterial3] is true, 1.0 will be used. /// Otherwise divider will not be drawn. final double? dividerHeight; /// The color of selected tab labels. /// - /// If null, then [TabBarThemeData.labelColor] is used. If that is also null and + /// If null, then [TabBarTheme.labelColor] is used. If that is also null and /// [ThemeData.useMaterial3] is true, [ColorScheme.primary] will be used, /// otherwise the color of the [ThemeData.primaryTextTheme]'s /// [TextTheme.bodyLarge] text color is used. /// - /// If [labelColor] (or, if null, [TabBarThemeData.labelColor]) is a + /// If [labelColor] (or, if null, [TabBarTheme.labelColor]) is a /// [WidgetStateColor], then the effective tab color will depend on the /// [WidgetState.selected] state, i.e. if the [Tab] is selected or not, /// ignoring [unselectedLabelColor] even if it's non-null. /// - /// When this color or the [TabBarThemeData.labelColor] is specified, it overrides + /// When this color or the [TabBarTheme.labelColor] is specified, it overrides /// the [TextStyle.color] specified for the [labelStyle] or the - /// [TabBarThemeData.labelStyle]. + /// [TabBarTheme.labelStyle]. /// /// See also: /// @@ -1067,19 +1067,19 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// The color of unselected tab labels. /// - /// If [labelColor] (or, if null, [TabBarThemeData.labelColor]) is a + /// If [labelColor] (or, if null, [TabBarTheme.labelColor]) is a /// [WidgetStateColor], then the unselected tabs are rendered with /// that [WidgetStateColor]'s resolved color for unselected state, even if /// [unselectedLabelColor] is non-null. /// - /// If null, then [TabBarThemeData.unselectedLabelColor] is used. If that is also + /// If null, then [TabBarTheme.unselectedLabelColor] is used. If that is also /// null and [ThemeData.useMaterial3] is true, [ColorScheme.onSurfaceVariant] /// will be used, otherwise unselected tab labels are rendered with /// [labelColor] at 70% opacity. /// - /// When this color or the [TabBarThemeData.unselectedLabelColor] is specified, it + /// When this color or the [TabBarTheme.unselectedLabelColor] is specified, it /// overrides the [TextStyle.color] specified for the [unselectedLabelStyle] - /// or the [TabBarThemeData.unselectedLabelStyle]. + /// or the [TabBarTheme.unselectedLabelStyle]. /// /// See also: /// @@ -1088,14 +1088,14 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// The text style of the selected tab labels. /// - /// The color specified in [labelStyle] and [TabBarThemeData.labelStyle] is used - /// to style the label when [labelColor] or [TabBarThemeData.labelColor] are not + /// The color specified in [labelStyle] and [TabBarTheme.labelStyle] is used + /// to style the label when [labelColor] or [TabBarTheme.labelColor] are not /// specified. /// /// If [unselectedLabelStyle] is null, then this text style will be used for /// both selected and unselected label styles. /// - /// If this property is null, then [TabBarThemeData.labelStyle] will be used. + /// If this property is null, then [TabBarTheme.labelStyle] will be used. /// /// If that is also null and [ThemeData.useMaterial3] is true, [TextTheme.titleSmall] /// will be used, otherwise the text style of the [ThemeData.primaryTextTheme]'s @@ -1104,11 +1104,11 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// The text style of the unselected tab labels. /// - /// The color specified in [unselectedLabelStyle] and [TabBarThemeData.unselectedLabelStyle] - /// is used to style the label when [unselectedLabelColor] or [TabBarThemeData.unselectedLabelColor] + /// The color specified in [unselectedLabelStyle] and [TabBarTheme.unselectedLabelStyle] + /// is used to style the label when [unselectedLabelColor] or [TabBarTheme.unselectedLabelColor] /// are not specified. /// - /// If this property is null, then [TabBarThemeData.unselectedLabelStyle] will be used. + /// If this property is null, then [TabBarTheme.unselectedLabelStyle] will be used. /// /// If that is also null and [ThemeData.useMaterial3] is true, [TextTheme.titleSmall] /// will be used, otherwise then the [labelStyle] value is used. If [labelStyle] is null, @@ -1159,7 +1159,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// * [WidgetState.selected]. /// {@endtemplate} /// - /// If null, then the value of [TabBarThemeData.mouseCursor] is used. If + /// If null, then the value of [TabBarTheme.mouseCursor] is used. If /// that is also null, then [WidgetStateMouseCursor.clickable] is used. /// /// See also: @@ -1244,20 +1244,20 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// If [TabBar.isScrollable] is true, only [TabAlignment.start], [TabAlignment.startOffset], /// and [TabAlignment.center] are supported. Otherwise an exception is thrown. /// - /// If this is null, then the value of [TabBarThemeData.tabAlignment] is used. + /// If this is null, then the value of [TabBarTheme.tabAlignment] is used. /// - /// If [TabBarThemeData.tabAlignment] is null and [ThemeData.useMaterial3] is true, + /// If [TabBarTheme.tabAlignment] is null and [ThemeData.useMaterial3] is true, /// then [TabAlignment.startOffset] is used if [isScrollable] is true, /// otherwise [TabAlignment.fill] is used. /// - /// If [TabBarThemeData.tabAlignment] is null and [ThemeData.useMaterial3] is false, + /// If [TabBarTheme.tabAlignment] is null and [ThemeData.useMaterial3] is false, /// then [TabAlignment.center] is used if [isScrollable] is true, /// otherwise [TabAlignment.fill] is used. final TabAlignment? tabAlignment; /// Specifies the text scaling behavior for the [Tab] label. /// - /// If this is null, then the value of [TabBarThemeData.textScaler] is used. If that is + /// If this is null, then the value of [TabBarTheme.textScaler] is used. If that is /// also null, then the text scaling behavior is determined by the [MediaQueryData.textScaler] /// from the ambient [MediaQuery], or 1.0 if there is no [MediaQuery] in scope. /// @@ -1267,7 +1267,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// Specifies the animation behavior of the tab indicator. /// - /// If this is null, then the value of [TabBarThemeData.indicatorAnimation] is used. + /// If this is null, then the value of [TabBarTheme.indicatorAnimation] is used. /// If that is also null, then the tab indicator will animate linearly if the /// [indicatorSize] is [TabBarIndicatorSize.tab], otherwise it will animate /// with an elastic effect if the [indicatorSize] is [TabBarIndicatorSize.label]. @@ -1343,7 +1343,7 @@ class _TabBarState extends State { _labelPaddings = List.filled(widget.tabs.length, EdgeInsets.zero, growable: true); } - TabBarThemeData get _defaults { + TabBarTheme get _defaults { if (Theme.of(context).useMaterial3) { return widget._isPrimary ? _TabsPrimaryDefaultsM3(context, widget.isScrollable) @@ -1355,7 +1355,7 @@ class _TabBarState extends State { Decoration _getIndicator(TabBarIndicatorSize indicatorSize) { final ThemeData theme = Theme.of(context); - final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data; + final TabBarTheme tabBarTheme = TabBarTheme.of(context); if (widget.indicator != null) { return widget.indicator!; @@ -1626,7 +1626,7 @@ class _TabBarState extends State { widget.onTap?.call(index); } - Widget _buildStyledTab(Widget child, bool isSelected, Animation animation, TabBarThemeData defaults) { + Widget _buildStyledTab(Widget child, bool isSelected, Animation animation, TabBarTheme defaults) { return _TabStyle( animation: animation, isSelected: isSelected, @@ -1687,7 +1687,7 @@ class _TabBarState extends State { assert(debugCheckHasMaterialLocalizations(context)); assert(_debugScheduleCheckHasValidTabsCount()); final ThemeData theme = Theme.of(context); - final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data; + final TabBarTheme tabBarTheme = TabBarTheme.of(context); final TabAlignment effectiveTabAlignment = widget.tabAlignment ?? tabBarTheme.tabAlignment ?? _defaults.tabAlignment!; assert(_debugTabAlignmentIsValid(effectiveTabAlignment)); @@ -1861,7 +1861,7 @@ class _TabBarState extends State { tabBar = CustomPaint( painter: _DividerPainter( dividerColor: dividerColor, - dividerHeight: dividerHeight, + dividerHeight: widget.dividerHeight ?? tabBarTheme.dividerHeight ?? _defaults.dividerHeight!, ), child: tabBar, ); @@ -2430,7 +2430,7 @@ class _TabPageSelectorState extends State { } // Hand coded defaults based on Material Design 2. -class _TabsDefaultsM2 extends TabBarThemeData { +class _TabsDefaultsM2 extends TabBarTheme { const _TabsDefaultsM2(this.context, this.isScrollable) : super(indicatorSize: TabBarIndicatorSize.tab); @@ -2465,7 +2465,7 @@ class _TabsDefaultsM2 extends TabBarThemeData { // Design token database by the script: // dev/tools/gen_defaults/bin/gen_defaults.dart. -class _TabsPrimaryDefaultsM3 extends TabBarThemeData { +class _TabsPrimaryDefaultsM3 extends TabBarTheme { _TabsPrimaryDefaultsM3(this.context, this.isScrollable) : super(indicatorSize: TabBarIndicatorSize.label); @@ -2544,7 +2544,7 @@ class _TabsPrimaryDefaultsM3 extends TabBarThemeData { static const EdgeInsetsGeometry iconMargin = EdgeInsets.only(bottom: 2); } -class _TabsSecondaryDefaultsM3 extends TabBarThemeData { +class _TabsSecondaryDefaultsM3 extends TabBarTheme { _TabsSecondaryDefaultsM3(this.context, this.isScrollable) : super(indicatorSize: TabBarIndicatorSize.tab); diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart index 7e9ba3c3c7628..47c420b6df9a1 100644 --- a/packages/flutter/test/material/tab_bar_theme_test.dart +++ b/packages/flutter/test/material/tab_bar_theme_test.dart @@ -35,7 +35,6 @@ final List _sizedTabs = [ ]; Widget buildTabBar({ - TabBarThemeData? localTabBarTheme, TabBarTheme? tabBarTheme, bool secondaryTabBar = false, List tabs = _tabs, @@ -48,30 +47,31 @@ Widget buildTabBar({ ); addTearDown(controller.dispose); - Widget tabBar = secondaryTabBar - ? TabBar.secondary( - tabs: tabs, - isScrollable: isScrollable, - controller: controller, - ) : TabBar( - tabs: tabs, - isScrollable: isScrollable, - controller: controller, - ); - - if (localTabBarTheme != null) { - tabBar = TabBarTheme( - data: localTabBarTheme, - child: tabBar, + if (secondaryTabBar) { + return MaterialApp( + theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: useMaterial3), + home: Scaffold( + body: RepaintBoundary( + key: _painterKey, + child: TabBar.secondary( + tabs: tabs, + isScrollable: isScrollable, + controller: controller, + ), + ), + ), ); } - return MaterialApp( theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: useMaterial3), home: Scaffold( body: RepaintBoundary( key: _painterKey, - child: tabBar, + child: TabBar( + tabs: tabs, + isScrollable: isScrollable, + controller: controller, + ), ), ), ); @@ -89,122 +89,31 @@ RenderParagraph _getText(WidgetTester tester, String text) { } void main() { - test('TabBarThemeData copyWith, ==, hashCode, defaults', () { - expect(const TabBarThemeData(), const TabBarThemeData().copyWith()); - expect(const TabBarThemeData().hashCode, const TabBarThemeData().copyWith().hashCode); - - expect(const TabBarThemeData().indicator, null); - expect(const TabBarThemeData().indicatorColor, null); - expect(const TabBarThemeData().indicatorSize, null); - expect(const TabBarThemeData().dividerColor, null); - expect(const TabBarThemeData().dividerHeight, null); - expect(const TabBarThemeData().labelColor, null); - expect(const TabBarThemeData().labelPadding, null); - expect(const TabBarThemeData().labelStyle, null); - expect(const TabBarThemeData().unselectedLabelColor, null); - expect(const TabBarThemeData().unselectedLabelStyle, null); - expect(const TabBarThemeData().overlayColor, null); - expect(const TabBarThemeData().splashFactory, null); - expect(const TabBarThemeData().mouseCursor, null); - expect(const TabBarThemeData().tabAlignment, null); - expect(const TabBarThemeData().textScaler, null); - expect(const TabBarThemeData().indicatorAnimation, null); - }); - - test('TabBarThemeData lerp special cases', () { - const TabBarThemeData theme = TabBarThemeData(); - expect(identical(TabBarThemeData.lerp(theme, theme, 0.5), theme), true); - }); - - testWidgets('Default TabBarThemeData debugFillProperties', (WidgetTester tester) async { - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - const TabBarThemeData().debugFillProperties(builder); - - final List description = builder.properties - .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) - .map((DiagnosticsNode node) => node.toString()) - .toList(); - - expect(description, []); - }); - - testWidgets('TabBarThemeData implements debugFillProperties', (WidgetTester tester) async { - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - const TabBarThemeData( - indicator: BoxDecoration(color: Color(0xFF00FF00)), - indicatorColor: Colors.red, - indicatorSize: TabBarIndicatorSize.label, - dividerColor: Color(0xff000001), - dividerHeight: 20.5, - labelColor: Color(0xff000002), - labelPadding: EdgeInsets.all(20.0), - labelStyle: TextStyle(color: Colors.amber), - unselectedLabelColor: Color(0xff654321), - unselectedLabelStyle: TextStyle(color: Colors.blue), - overlayColor: WidgetStatePropertyAll(Colors.yellow), - mouseCursor: WidgetStatePropertyAll(SystemMouseCursors.contextMenu), - tabAlignment: TabAlignment.center, - textScaler: TextScaler.noScaling, - indicatorAnimation: TabIndicatorAnimation.elastic, - ).debugFillProperties(builder); - final List description = builder.properties - .where((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info)) - .map((DiagnosticsNode n) => n.toString()).toList(); - expect(description, [ - 'indicator: BoxDecoration(color: Color(0xff00ff00))', - 'indicatorColor: MaterialColor(primary value: Color(0xfff44336))', - 'indicatorSize: TabBarIndicatorSize.label', - 'dividerColor: Color(0xff000001)', - 'dividerHeight: 20.5', - 'labelColor: Color(0xff000002)', - 'labelPadding: EdgeInsets.all(20.0)', - 'labelStyle: TextStyle(inherit: true, color: MaterialColor(primary value: Color(0xffffc107)))', - 'unselectedLabelColor: Color(0xff654321)', - 'unselectedLabelStyle: TextStyle(inherit: true, color: MaterialColor(primary value: Color(0xff2196f3)))', - 'overlayColor: WidgetStatePropertyAll(MaterialColor(primary value: Color(0xffffeb3b)))', - 'mouseCursor: WidgetStatePropertyAll(SystemMouseCursor(contextMenu))', - 'tabAlignment: TabAlignment.center', - 'textScaler: no scaling', - 'indicatorAnimation: TabIndicatorAnimation.elastic', - ]); + test('TabBarTheme copyWith, ==, hashCode, defaults', () { + expect(const TabBarTheme(), const TabBarTheme().copyWith()); + expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode); + + expect(const TabBarTheme().indicator, null); + expect(const TabBarTheme().indicatorColor, null); + expect(const TabBarTheme().indicatorSize, null); + expect(const TabBarTheme().dividerColor, null); + expect(const TabBarTheme().dividerHeight, null); + expect(const TabBarTheme().labelColor, null); + expect(const TabBarTheme().labelPadding, null); + expect(const TabBarTheme().labelStyle, null); + expect(const TabBarTheme().unselectedLabelColor, null); + expect(const TabBarTheme().unselectedLabelStyle, null); + expect(const TabBarTheme().overlayColor, null); + expect(const TabBarTheme().splashFactory, null); + expect(const TabBarTheme().mouseCursor, null); + expect(const TabBarTheme().tabAlignment, null); + expect(const TabBarTheme().textScaler, null); + expect(const TabBarTheme().indicatorAnimation, null); }); - testWidgets('Local TabBarTheme overrides defaults', (WidgetTester tester) async { - const Color indicatorColor = Colors.green; - const Color dividerColor = Color(0xff000001); - const double dividerHeight = 20.5; - const Color labelColor = Color(0xff000002); - const TextStyle labelStyle = TextStyle(fontSize: 32.0); - const Color unselectedLabelColor = Color(0xff654321); - const TextStyle unselectedLabelStyle = TextStyle(fontWeight: FontWeight.bold); - - const TabBarThemeData tabBarTheme = TabBarThemeData( - indicatorColor: indicatorColor, - dividerColor: dividerColor, - dividerHeight: dividerHeight, - labelColor: labelColor, - labelStyle: labelStyle, - unselectedLabelColor: unselectedLabelColor, - unselectedLabelStyle: unselectedLabelStyle, - ); - - // Test default label color and label styles. - await tester.pumpWidget(buildTabBar(useMaterial3: true, localTabBarTheme: tabBarTheme)); - - final RenderParagraph selectedLabel = _getText(tester, _tab1Text); - expect(selectedLabel.text.style!.color, labelColor); - expect(selectedLabel.text.style!.fontSize, 32.0); - final RenderParagraph unselectedLabel = _getText(tester, _tab2Text); - expect(unselectedLabel.text.style!.color, unselectedLabelColor); - expect(unselectedLabel.text.style!.fontWeight, FontWeight.bold); - - final RenderBox tabBarBox = tester.firstRenderObject(find.byType(TabBar)); - expect( - tabBarBox, - paints - ..line(color: dividerColor, strokeWidth: dividerHeight) - ..rrect(color: indicatorColor) - ); + test('TabBarTheme lerp special cases', () { + const TabBarTheme theme = TabBarTheme(); + expect(identical(TabBarTheme.lerp(theme, theme, 0.5), theme), true); }); testWidgets('Tab bar defaults (primary)', (WidgetTester tester) async {