@@ -166,7 +166,7 @@ class Tab extends StatelessWidget implements PreferredSizeWidget {
166166class _TabStyle extends AnimatedWidget {
167167 const _TabStyle ({
168168 required Animation <double > animation,
169- required this .selected ,
169+ required this .isSelected ,
170170 required this .labelColor,
171171 required this .unselectedLabelColor,
172172 required this .labelStyle,
@@ -176,18 +176,58 @@ class _TabStyle extends AnimatedWidget {
176176
177177 final TextStyle ? labelStyle;
178178 final TextStyle ? unselectedLabelStyle;
179- final bool selected ;
179+ final bool isSelected ;
180180 final Color ? labelColor;
181181 final Color ? unselectedLabelColor;
182182 final Widget child;
183183
184+ MaterialStateColor _resolveWithLabelColor (BuildContext context) {
185+ final ThemeData themeData = Theme .of (context);
186+ final TabBarTheme tabBarTheme = TabBarTheme .of (context);
187+ final TabBarTheme defaults = themeData.useMaterial3 ? _TabsDefaultsM3 (context) : _TabsDefaultsM2 (context);
188+ final Animation <double > animation = listenable as Animation <double >;
189+
190+ // labelStyle.color (and tabBarTheme.labelStyle.color) is not considered
191+ // as it'll be a breaking change without a possible migration plan. for
192+ // details: https://github.com/flutter/flutter/pull/109541#issuecomment-1294241417
193+ Color selectedColor = labelColor
194+ ?? tabBarTheme.labelColor
195+ ?? defaults.labelColor! ;
196+
197+ final Color unselectedColor;
198+
199+ if (selectedColor is MaterialStateColor ) {
200+ unselectedColor = selectedColor.resolve (const < MaterialState > {});
201+ selectedColor = selectedColor.resolve (const < MaterialState > {MaterialState .selected});
202+ } else {
203+ // unselectedLabelColor and tabBarTheme.unselectedLabelColor are ignored
204+ // when labelColor is a MaterialStateColor.
205+ unselectedColor = unselectedLabelColor
206+ ?? tabBarTheme.unselectedLabelColor
207+ ?? (themeData.useMaterial3
208+ ? defaults.unselectedLabelColor!
209+ : selectedColor.withAlpha (0xB2 )); // 70% alpha
210+ }
211+
212+ return MaterialStateColor .resolveWith ((Set <MaterialState > states) {
213+ if (states.contains (MaterialState .selected)) {
214+ return Color .lerp (selectedColor, unselectedColor, animation.value)! ;
215+ }
216+ return Color .lerp (unselectedColor, selectedColor, animation.value)! ;
217+ });
218+ }
219+
184220 @override
185221 Widget build (BuildContext context) {
186222 final ThemeData themeData = Theme .of (context);
187223 final TabBarTheme tabBarTheme = TabBarTheme .of (context);
188224 final TabBarTheme defaults = themeData.useMaterial3 ? _TabsDefaultsM3 (context) : _TabsDefaultsM2 (context);
189225 final Animation <double > animation = listenable as Animation <double >;
190226
227+ final Set <MaterialState > states = isSelected
228+ ? const < MaterialState > {MaterialState .selected}
229+ : const < MaterialState > {};
230+
191231 // To enable TextStyle.lerp(style1, style2, value), both styles must have
192232 // the same value of inherit. Force that to be inherit=true here.
193233 final TextStyle defaultStyle = (labelStyle
@@ -199,21 +239,10 @@ class _TabStyle extends AnimatedWidget {
199239 ?? labelStyle
200240 ?? defaults.unselectedLabelStyle!
201241 ).copyWith (inherit: true );
202- final TextStyle textStyle = selected
242+ final TextStyle textStyle = isSelected
203243 ? TextStyle .lerp (defaultStyle, defaultUnselectedStyle, animation.value)!
204244 : TextStyle .lerp (defaultUnselectedStyle, defaultStyle, animation.value)! ;
205-
206- final Color selectedColor = labelColor
207- ?? tabBarTheme.labelColor
208- ?? defaults.labelColor! ;
209- final Color unselectedColor = unselectedLabelColor
210- ?? tabBarTheme.unselectedLabelColor
211- ?? (themeData.useMaterial3
212- ? defaults.unselectedLabelColor!
213- : selectedColor.withAlpha (0xB2 )); // 70% alpha
214- final Color color = selected
215- ? Color .lerp (selectedColor, unselectedColor, animation.value)!
216- : Color .lerp (unselectedColor, selectedColor, animation.value)! ;
245+ final Color color = _resolveWithLabelColor (context).resolve (states);
217246
218247 return DefaultTextStyle (
219248 style: textStyle.copyWith (color: color),
@@ -738,7 +767,8 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
738767 ///
739768 /// If [automaticIndicatorColorAdjustment] is true,
740769 /// then the [indicatorColor] will be automatically adjusted to [Colors.white]
741- /// when the [indicatorColor] is same as [Material.color] of the [Material] parent widget.
770+ /// when the [indicatorColor] is same as [Material.color] of the [Material]
771+ /// parent widget.
742772 final bool automaticIndicatorColorAdjustment;
743773
744774 /// Defines how the selected tab indicator's size is computed.
@@ -762,23 +792,50 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
762792
763793 /// The color of selected tab labels.
764794 ///
765- /// If [ThemeData.useMaterial3] is false, unselected tab labels are rendered with
766- /// the same color with 70% opacity unless [unselectedLabelColor] is non-null.
767- ///
768- /// If this property is null and [ThemeData.useMaterial3] is true, [ColorScheme.primary]
769- /// will be used, otherwise the color of the [ThemeData.primaryTextTheme] 's
795+ /// If null, then [TabBarTheme.labelColor] is used. If that is also null and
796+ /// [ThemeData.useMaterial3] is true, [ColorScheme.primary] will be used,
797+ /// otherwise the color of the [ThemeData.primaryTextTheme] 's
770798 /// [TextTheme.bodyLarge] text color is used.
799+ ///
800+ /// If [labelColor] (or, if null, [TabBarTheme.labelColor] ) is a
801+ /// [MaterialStateColor] , then the effective tab color will depend on the
802+ /// [MaterialState.selected] state, i.e. if the [Tab] is selected or not,
803+ /// ignoring [unselectedLabelColor] even if it's non-null.
804+ ///
805+ /// Note: [labelStyle] 's color and [TabBarTheme.labelStyle] 's color do not
806+ /// affect the effective [labelColor] .
807+ ///
808+ /// See also:
809+ ///
810+ /// * [unselectedLabelColor] , for color of unselected tab labels.
771811 final Color ? labelColor;
772812
773813 /// The color of unselected tab labels.
774814 ///
775- /// If this property is null and [ThemeData.useMaterial3] is true, [ColorScheme.onSurfaceVariant]
776- /// will be used, otherwise unselected tab labels are rendered with the
777- /// [labelColor] with 70% opacity.
815+ /// If [labelColor] (or, if null, [TabBarTheme.labelColor] ) is a
816+ /// [MaterialStateColor] , then the unselected tabs are rendered with
817+ /// that [MaterialStateColor] 's resolved color for unselected state, even if
818+ /// [unselectedLabelColor] is non-null.
819+ ///
820+ /// If null, then [TabBarTheme.unselectedLabelColor] is used. If that is also
821+ /// null and [ThemeData.useMaterial3] is true, [ColorScheme.onSurfaceVariant]
822+ /// will be used, otherwise unselected tab labels are rendered with
823+ /// [labelColor] at 70% opacity.
824+ ///
825+ /// Note: [unselectedLabelStyle] 's color and
826+ /// [TabBarTheme.unselectedLabelStyle] 's color are ignored in
827+ /// [unselectedLabelColor] 's precedence calculation.
828+ ///
829+ /// See also:
830+ ///
831+ /// * [labelColor] , for color of selected tab labels.
778832 final Color ? unselectedLabelColor;
779833
780834 /// The text style of the selected tab labels.
781835 ///
836+ /// This does not influence color of the tab labels even if [TextStyle.color]
837+ /// is non-null. Refer [labelColor] to color selected tab labels instead.
838+ ///
782839 /// If [unselectedLabelStyle] is null, then this text style will be used for
783840 /// both selected and unselected label styles.
784841 ///
@@ -787,6 +844,18 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
787844 /// [TextTheme.bodyLarge] definition is used.
788845 final TextStyle ? labelStyle;
789846
847+ /// The text style of the unselected tab labels.
848+ ///
849+ /// This does not influence color of the tab labels even if [TextStyle.color]
850+ /// is non-null. Refer [unselectedLabelColor] to color unselected tab labels
851+ /// instead.
852+ ///
853+ /// If this property is null and [ThemeData.useMaterial3] is true,
854+ /// [TextTheme.titleSmall] will be used, otherwise then the [labelStyle] value
855+ /// is used. If [labelStyle] is null, the text style of the
856+ /// [ThemeData.primaryTextTheme] 's [TextTheme.bodyLarge] definition is used.
857+ final TextStyle ? unselectedLabelStyle;
858+
790859 /// The padding added to each of the tab labels.
791860 ///
792861 /// If there are few tabs with both icon and text and few
@@ -796,14 +865,6 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
796865 /// If this property is null, then kTabLabelPadding is used.
797866 final EdgeInsetsGeometry ? labelPadding;
798867
799- /// The text style of the unselected tab labels.
800- ///
801- /// If this property is null and [ThemeData.useMaterial3] is true, [TextTheme.titleSmall]
802- /// will be used, otherwise then the [labelStyle] value is used. If [labelStyle]
803- /// is null, the text style of the [ThemeData.primaryTextTheme] 's
804- /// [TextTheme.bodyLarge] definition is used.
805- final TextStyle ? unselectedLabelStyle;
806-
807868 /// Defines the ink response focus, hover, and splash colors.
808869 ///
809870 /// If non-null, it is resolved against one of [MaterialState.focused] ,
@@ -1209,10 +1270,10 @@ class _TabBarState extends State<TabBar> {
12091270 widget.onTap? .call (index);
12101271 }
12111272
1212- Widget _buildStyledTab (Widget child, bool selected , Animation <double > animation) {
1273+ Widget _buildStyledTab (Widget child, bool isSelected , Animation <double > animation) {
12131274 return _TabStyle (
12141275 animation: animation,
1215- selected : selected ,
1276+ isSelected : isSelected ,
12161277 labelColor: widget.labelColor,
12171278 unselectedLabelColor: widget.unselectedLabelColor,
12181279 labelStyle: widget.labelStyle,
@@ -1368,7 +1429,7 @@ class _TabBarState extends State<TabBar> {
13681429 painter: _indicatorPainter,
13691430 child: _TabStyle (
13701431 animation: kAlwaysDismissedAnimation,
1371- selected : false ,
1432+ isSelected : false ,
13721433 labelColor: widget.labelColor,
13731434 unselectedLabelColor: widget.unselectedLabelColor,
13741435 labelStyle: widget.labelStyle,
0 commit comments