Reverts "Normalize TabBarTheme (#155476)" (#155698)

Reverts: flutter/flutter#155476
Initiated by: eyebrowsoffire
Reason for reverting: The newly added tests are failing in postsubmit. See https://ci.chromium.org/ui/p/flutter/builders/prod/Windows%20framework_tests_libraries/19062/overview
Original PR Author: QuncCccccc

Reviewed By: {TahaTesser}

This change reverts the following previous change:
This PR is to make preparations to make `TabBarTheme` conform to Flutter's conventions for component themes:

* Added a `TabBarThemeData` class which defines overrides for the defaults for `TabBar` properties.
* Added 2 `TabBarTheme` constructor parameters: `TabBarThemeData? data` and `Widget? child`. This is now the preferred way to configure a `TabBarTheme`:
```
TabBarTheme(
  data: TabBarThemeData(labelColor: xxx, indicatorColor: xxx, ...),
  child: TabBar(...)
)
```
  These two properties are made nullable to not break existing apps which has customized `ThemeData.tabBarTheme`.

* Changed the type of component theme defaults from `TabBarTheme` to `TabBarThemeData`.

TODO:

* Fix internal failures.
* Change the type of `ThemeData.tabBarTheme` from `TabBarTheme` to `TabBarThemeData`. This may cause breaking changes, a migration guide will be created.

Addresses the "theme normalization" sub project within https://github.com/flutter/flutter/issues/91772
This commit is contained in:
auto-submit[bot]
2024-09-25 17:50:18 +00:00
committed by GitHub
parent f310625bc6
commit 1c9607fc9c
4 changed files with 99 additions and 489 deletions

View File

@@ -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);

View File

@@ -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].
///
/// Descendant widgets obtain the current theme's [TabBarTheme] object using
/// `TabBarTheme.of(context)`. Instances of [TabBarTheme] can be customized with
/// [TabBarTheme.copyWith].
///
/// 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<Color?>? overlayColor,
InteractiveInkFeatureFactory? splashFactory,
WidgetStateProperty<MouseCursor?>? 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<Color?>? _overlayColor;
final InteractiveInkFeatureFactory? _splashFactory;
final MaterialStateProperty<MouseCursor?>? _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<Color?>? 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<MouseCursor?>? 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<Color?>? overlayColor,
InteractiveInkFeatureFactory? splashFactory,
MaterialStateProperty<MouseCursor?>? 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<TabBarTheme>();
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<Color?>(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.
///
/// 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<Decoration?>('indicator', indicator, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('indicatorColor', indicatorColor, defaultValue: null));
properties.add(DiagnosticsProperty<TabBarIndicatorSize?>('indicatorSize', indicatorSize, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('dividerColor', dividerColor, defaultValue: null));
properties.add(DiagnosticsProperty<double?>('dividerHeight', dividerHeight, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('labelColor', labelColor, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry?>('labelPadding', labelPadding, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle?>('labelStyle', labelStyle, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('unselectedLabelColor', unselectedLabelColor, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle?>('unselectedLabelStyle', unselectedLabelStyle, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>?>('overlayColor', overlayColor, defaultValue: null));
properties.add(DiagnosticsProperty<InteractiveInkFeatureFactory?>('splashFactory', splashFactory, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>?>('mouseCursor', mouseCursor, defaultValue: null));
properties.add(DiagnosticsProperty<TabAlignment?>('tabAlignment', tabAlignment, defaultValue: null));
properties.add(DiagnosticsProperty<TextScaler?>('textScaler', textScaler, defaultValue: null));
properties.add(DiagnosticsProperty<TabIndicatorAnimation?>('indicatorAnimation', indicatorAnimation, defaultValue: null));
}
}

View File

@@ -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<double> animation = listenable as Animation<double>;
// 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<double> animation = listenable as Animation<double>;
final Set<MaterialState> 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<TabBar> {
_labelPaddings = List<EdgeInsetsGeometry>.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<TabBar> {
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<TabBar> {
widget.onTap?.call(index);
}
Widget _buildStyledTab(Widget child, bool isSelected, Animation<double> animation, TabBarThemeData defaults) {
Widget _buildStyledTab(Widget child, bool isSelected, Animation<double> animation, TabBarTheme defaults) {
return _TabStyle(
animation: animation,
isSelected: isSelected,
@@ -1687,7 +1687,7 @@ class _TabBarState extends State<TabBar> {
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> {
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<TabPageSelector> {
}
// 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);

View File

@@ -35,7 +35,6 @@ final List<SizedBox> _sizedTabs = <SizedBox>[
];
Widget buildTabBar({
TabBarThemeData? localTabBarTheme,
TabBarTheme? tabBarTheme,
bool secondaryTabBar = false,
List<Widget> 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);
test('TabBarTheme copyWith, ==, hashCode, defaults', () {
expect(const TabBarTheme(), const TabBarTheme().copyWith());
expect(const TabBarTheme().hashCode, const TabBarTheme().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);
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);
});
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<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[]);
});
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<Color>(Colors.yellow),
mouseCursor: WidgetStatePropertyAll<MouseCursor>(SystemMouseCursors.contextMenu),
tabAlignment: TabAlignment.center,
textScaler: TextScaler.noScaling,
indicatorAnimation: TabIndicatorAnimation.elastic,
).debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode n) => n.toString()).toList();
expect(description, <String>[
'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',
]);
});
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<RenderBox>(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 {