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

This reverts commit 1c9607fc9c982aa97ce8bf2c724b165505280ca4.
diff --git a/dev/tools/gen_defaults/lib/tabs_template.dart b/dev/tools/gen_defaults/lib/tabs_template.dart
index fbe7c6f..11febe6 100644
--- a/dev/tools/gen_defaults/lib/tabs_template.dart
+++ b/dev/tools/gen_defaults/lib/tabs_template.dart
@@ -12,7 +12,7 @@
 
   @override
   String generate() => '''
-class _${blockName}PrimaryDefaultsM3 extends TabBarTheme {
+class _${blockName}PrimaryDefaultsM3 extends TabBarThemeData {
   _${blockName}PrimaryDefaultsM3(this.context, this.isScrollable)
     : super(indicatorSize: TabBarIndicatorSize.label);
 
@@ -91,7 +91,7 @@
   static const EdgeInsetsGeometry iconMargin = EdgeInsets.only(bottom: 2);
 }
 
-class _${blockName}SecondaryDefaultsM3 extends TabBarTheme {
+class _${blockName}SecondaryDefaultsM3 extends TabBarThemeData {
   _${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 64f95bf..4dbe745 100644
--- a/packages/flutter/lib/src/material/tab_bar_theme.dart
+++ b/packages/flutter/lib/src/material/tab_bar_theme.dart
@@ -12,22 +12,305 @@
 
 /// Defines a theme for [TabBar] widgets.
 ///
-/// 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].
+/// Descendant widgets obtain the current [TabBarTheme] object using
+/// `TabBarTheme.of(context)`.
 ///
 /// See also:
 ///
-///  * [TabBar], a widget that displays a horizontal row of tabs.
+///  * [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.
 ///  * [ThemeData], which describes the overall theme information for the
 ///    application.
 @immutable
-class TabBarTheme with Diagnosticable {
+class TabBarThemeData with Diagnosticable {
   /// Creates a tab bar theme that can be used with [ThemeData.tabBarTheme].
-  const TabBarTheme({
+  const TabBarThemeData({
     this.indicator,
     this.indicatorColor,
     this.indicatorSize,
@@ -108,7 +391,7 @@
 
   /// Creates a copy of this object but with the given fields replaced with the
   /// new values.
-  TabBarTheme copyWith({
+  TabBarThemeData copyWith({
     Decoration? indicator,
     Color? indicatorColor,
     TabBarIndicatorSize? indicatorSize,
@@ -126,7 +409,7 @@
     TextScaler? textScaler,
     TabIndicatorAnimation? indicatorAnimation,
   }) {
-    return TabBarTheme(
+    return TabBarThemeData(
       indicator: indicator ?? this.indicator,
       indicatorColor: indicatorColor ?? this.indicatorColor,
       indicatorSize: indicatorSize ?? this.indicatorSize,
@@ -146,19 +429,14 @@
     );
   }
 
-  /// 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 TabBarTheme lerp(TabBarTheme a, TabBarTheme b, double t) {
+  static TabBarThemeData lerp(TabBarThemeData a, TabBarThemeData b, double t) {
     if (identical(a, b)) {
       return a;
     }
-    return TabBarTheme(
+    return TabBarThemeData(
       indicator: Decoration.lerp(a.indicator, b.indicator, t),
       indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t),
       indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize,
@@ -206,7 +484,7 @@
     if (other.runtimeType != runtimeType) {
       return false;
     }
-    return other is TabBarTheme
+    return other is TabBarThemeData
         && other.indicator == indicator
         && other.indicatorColor == indicatorColor
         && other.indicatorSize == indicatorSize
@@ -224,4 +502,25 @@
         && 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));
+  }
 }
diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart
index bc66874..45de852 100644
--- a/packages/flutter/lib/src/material/tabs.dart
+++ b/packages/flutter/lib/src/material/tabs.dart
@@ -92,7 +92,7 @@
 ///
 /// See also:
 ///  * [TabBar], which displays a row of tabs.
-///  * [TabBarTheme], which can be used to configure the appearance of the tab
+///  * [TabBarThemeData], which can be used to configure the appearance of the tab
 ///    indicator.
 enum TabIndicatorAnimation {
   /// The tab indicator animates linearly.
@@ -241,12 +241,12 @@
   final bool isPrimary;
   final Color? labelColor;
   final Color? unselectedLabelColor;
-  final TabBarTheme defaults;
+  final TabBarThemeData defaults;
   final Widget child;
 
   MaterialStateColor _resolveWithLabelColor(BuildContext context) {
     final ThemeData themeData = Theme.of(context);
-    final TabBarTheme tabBarTheme = TabBarTheme.of(context);
+    final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data;
     final Animation<double> animation = listenable as Animation<double>;
 
     // labelStyle.color (and tabBarTheme.labelStyle.color) is not considered
@@ -285,7 +285,7 @@
 
   @override
   Widget build(BuildContext context) {
-    final TabBarTheme tabBarTheme = TabBarTheme.of(context);
+    final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data;
     final Animation<double> animation = listenable as Animation<double>;
 
     final Set<MaterialState> states = isSelected
@@ -790,7 +790,7 @@
 ///
 /// Requires one of its ancestors to be a [Material] widget.
 ///
-/// Uses values from [TabBarTheme] if it is set in the current context.
+/// Uses values from [TabBarThemeData] 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 @@
   /// If this parameter is null, then the value of the Theme's indicatorColor
   /// property is used.
   ///
-  /// If [indicator] is specified or provided from [TabBarTheme],
+  /// If [indicator] is specified or provided from [TabBarThemeData],
   /// this property is ignored.
   final Color? indicatorColor;
 
@@ -971,7 +971,7 @@
   ///
   /// If [ThemeData.useMaterial3] is false, the default value is 2.0.
   ///
-  /// If [indicator] is specified or provided from [TabBarTheme],
+  /// If [indicator] is specified or provided from [TabBarThemeData],
   /// this property is ignored.
   final double indicatorWeight;
 
@@ -986,7 +986,7 @@
 
   /// Defines the appearance of the selected tab indicator.
   ///
-  /// If [indicator] is specified or provided from [TabBarTheme],
+  /// If [indicator] is specified or provided from [TabBarThemeData],
   /// the [indicatorColor] and [indicatorWeight] properties are ignored.
   ///
   /// The default, underline-style, selected tab indicator can be defined with
@@ -1030,7 +1030,7 @@
   ///
   /// If the [dividerColor] is [Colors.transparent], then the divider will not be drawn.
   ///
-  /// If null and [ThemeData.useMaterial3] is false, [TabBarTheme.dividerColor]
+  /// If null and [ThemeData.useMaterial3] is false, [TabBarThemeData.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 @@
   ///
   /// If the [dividerHeight] is zero or negative, then the divider will not be drawn.
   ///
-  /// If null and [ThemeData.useMaterial3] is true, [TabBarTheme.dividerHeight] is used.
+  /// If null and [ThemeData.useMaterial3] is true, [TabBarThemeData.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 [TabBarTheme.labelColor] is used. If that is also null and
+  /// If null, then [TabBarThemeData.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, [TabBarTheme.labelColor]) is a
+  /// If [labelColor] (or, if null, [TabBarThemeData.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 [TabBarTheme.labelColor] is specified, it overrides
+  /// When this color or the [TabBarThemeData.labelColor] is specified, it overrides
   /// the [TextStyle.color] specified for the [labelStyle] or the
-  /// [TabBarTheme.labelStyle].
+  /// [TabBarThemeData.labelStyle].
   ///
   /// See also:
   ///
@@ -1067,19 +1067,19 @@
 
   /// The color of unselected tab labels.
   ///
-  /// If [labelColor] (or, if null, [TabBarTheme.labelColor]) is a
+  /// If [labelColor] (or, if null, [TabBarThemeData.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 [TabBarTheme.unselectedLabelColor] is used. If that is also
+  /// If null, then [TabBarThemeData.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 [TabBarTheme.unselectedLabelColor] is specified, it
+  /// When this color or the [TabBarThemeData.unselectedLabelColor] is specified, it
   /// overrides the [TextStyle.color] specified for the [unselectedLabelStyle]
-  /// or the [TabBarTheme.unselectedLabelStyle].
+  /// or the [TabBarThemeData.unselectedLabelStyle].
   ///
   /// See also:
   ///
@@ -1088,14 +1088,14 @@
 
   /// The text style of the selected tab labels.
   ///
-  /// The color specified in [labelStyle] and [TabBarTheme.labelStyle] is used
-  /// to style the label when [labelColor] or [TabBarTheme.labelColor] are not
+  /// The color specified in [labelStyle] and [TabBarThemeData.labelStyle] is used
+  /// to style the label when [labelColor] or [TabBarThemeData.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 [TabBarTheme.labelStyle] will be used.
+  /// If this property is null, then [TabBarThemeData.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 @@
 
   /// The text style of the unselected tab labels.
   ///
-  /// The color specified in [unselectedLabelStyle] and [TabBarTheme.unselectedLabelStyle]
-  /// is used to style the label when [unselectedLabelColor] or [TabBarTheme.unselectedLabelColor]
+  /// The color specified in [unselectedLabelStyle] and [TabBarThemeData.unselectedLabelStyle]
+  /// is used to style the label when [unselectedLabelColor] or [TabBarThemeData.unselectedLabelColor]
   /// are not specified.
   ///
-  /// If this property is null, then [TabBarTheme.unselectedLabelStyle] will be used.
+  /// If this property is null, then [TabBarThemeData.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 @@
   ///  * [WidgetState.selected].
   /// {@endtemplate}
   ///
-  /// If null, then the value of [TabBarTheme.mouseCursor] is used. If
+  /// If null, then the value of [TabBarThemeData.mouseCursor] is used. If
   /// that is also null, then [WidgetStateMouseCursor.clickable] is used.
   ///
   /// See also:
@@ -1244,20 +1244,20 @@
   /// 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 [TabBarTheme.tabAlignment] is used.
+  /// If this is null, then the value of [TabBarThemeData.tabAlignment] is used.
   ///
-  /// If [TabBarTheme.tabAlignment] is null and [ThemeData.useMaterial3] is true,
+  /// If [TabBarThemeData.tabAlignment] is null and [ThemeData.useMaterial3] is true,
   /// then [TabAlignment.startOffset] is used if [isScrollable] is true,
   /// otherwise [TabAlignment.fill] is used.
   ///
-  /// If [TabBarTheme.tabAlignment] is null and [ThemeData.useMaterial3] is false,
+  /// If [TabBarThemeData.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 [TabBarTheme.textScaler] is used. If that is
+  /// If this is null, then the value of [TabBarThemeData.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 @@
 
   /// Specifies the animation behavior of the tab indicator.
   ///
-  /// If this is null, then the value of [TabBarTheme.indicatorAnimation] is used.
+  /// If this is null, then the value of [TabBarThemeData.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 @@
     _labelPaddings = List<EdgeInsetsGeometry>.filled(widget.tabs.length, EdgeInsets.zero, growable: true);
   }
 
-  TabBarTheme get _defaults {
+  TabBarThemeData get _defaults {
     if (Theme.of(context).useMaterial3) {
       return widget._isPrimary
         ? _TabsPrimaryDefaultsM3(context, widget.isScrollable)
@@ -1355,7 +1355,7 @@
 
   Decoration _getIndicator(TabBarIndicatorSize indicatorSize) {
     final ThemeData theme = Theme.of(context);
-    final TabBarTheme tabBarTheme = TabBarTheme.of(context);
+    final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data;
 
     if (widget.indicator != null) {
       return widget.indicator!;
@@ -1626,7 +1626,7 @@
     widget.onTap?.call(index);
   }
 
-  Widget _buildStyledTab(Widget child, bool isSelected, Animation<double> animation, TabBarTheme defaults) {
+  Widget _buildStyledTab(Widget child, bool isSelected, Animation<double> animation, TabBarThemeData defaults) {
     return _TabStyle(
       animation: animation,
       isSelected: isSelected,
@@ -1687,7 +1687,7 @@
     assert(debugCheckHasMaterialLocalizations(context));
     assert(_debugScheduleCheckHasValidTabsCount());
     final ThemeData theme = Theme.of(context);
-    final TabBarTheme tabBarTheme = TabBarTheme.of(context);
+    final TabBarThemeData tabBarTheme = TabBarTheme.of(context).data;
     final TabAlignment effectiveTabAlignment = widget.tabAlignment ?? tabBarTheme.tabAlignment ?? _defaults.tabAlignment!;
     assert(_debugTabAlignmentIsValid(effectiveTabAlignment));
 
@@ -1861,7 +1861,7 @@
           tabBar = CustomPaint(
             painter: _DividerPainter(
               dividerColor: dividerColor,
-              dividerHeight: widget.dividerHeight ?? tabBarTheme.dividerHeight ?? _defaults.dividerHeight!,
+              dividerHeight: dividerHeight,
             ),
             child: tabBar,
           );
@@ -2430,7 +2430,7 @@
 }
 
 // Hand coded defaults based on Material Design 2.
-class _TabsDefaultsM2 extends TabBarTheme {
+class _TabsDefaultsM2 extends TabBarThemeData {
   const _TabsDefaultsM2(this.context, this.isScrollable)
     : super(indicatorSize: TabBarIndicatorSize.tab);
 
@@ -2465,7 +2465,7 @@
 // Design token database by the script:
 //   dev/tools/gen_defaults/bin/gen_defaults.dart.
 
-class _TabsPrimaryDefaultsM3 extends TabBarTheme {
+class _TabsPrimaryDefaultsM3 extends TabBarThemeData {
   _TabsPrimaryDefaultsM3(this.context, this.isScrollable)
     : super(indicatorSize: TabBarIndicatorSize.label);
 
@@ -2544,7 +2544,7 @@
   static const EdgeInsetsGeometry iconMargin = EdgeInsets.only(bottom: 2);
 }
 
-class _TabsSecondaryDefaultsM3 extends TabBarTheme {
+class _TabsSecondaryDefaultsM3 extends TabBarThemeData {
   _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 47c420b..7e9ba3c 100644
--- a/packages/flutter/test/material/tab_bar_theme_test.dart
+++ b/packages/flutter/test/material/tab_bar_theme_test.dart
@@ -35,6 +35,7 @@
 ];
 
 Widget buildTabBar({
+  TabBarThemeData? localTabBarTheme,
   TabBarTheme? tabBarTheme,
   bool secondaryTabBar = false,
   List<Widget> tabs = _tabs,
@@ -47,31 +48,30 @@
   );
   addTearDown(controller.dispose);
 
-  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,
-          ),
-        ),
-      ),
+  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,
     );
   }
+
   return MaterialApp(
     theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: useMaterial3),
     home: Scaffold(
       body: RepaintBoundary(
         key: _painterKey,
-        child: TabBar(
-          tabs: tabs,
-          isScrollable: isScrollable,
-          controller: controller,
-        ),
+        child: tabBar,
       ),
     ),
   );
@@ -89,31 +89,122 @@
 }
 
 void main() {
-  test('TabBarTheme copyWith, ==, hashCode, defaults', () {
-    expect(const TabBarTheme(), const TabBarTheme().copyWith());
-    expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode);
+  test('TabBarThemeData copyWith, ==, hashCode, defaults', () {
+    expect(const TabBarThemeData(), const TabBarThemeData().copyWith());
+    expect(const TabBarThemeData().hashCode, const TabBarThemeData().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);
+    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('TabBarTheme lerp special cases', () {
-    const TabBarTheme theme = TabBarTheme();
-    expect(identical(TabBarTheme.lerp(theme, theme, 0.5), theme), true);
+  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)
+    );
   });
 
   testWidgets('Tab bar defaults (primary)', (WidgetTester tester) async {