diff --git a/packages/flutter/lib/src/material/tab_bar_theme.dart b/packages/flutter/lib/src/material/tab_bar_theme.dart index f046c86522c..48e86ebb3d5 100644 --- a/packages/flutter/lib/src/material/tab_bar_theme.dart +++ b/packages/flutter/lib/src/material/tab_bar_theme.dart @@ -29,7 +29,9 @@ class TabBarTheme extends Diagnosticable { this.indicator, this.indicatorSize, this.labelColor, + this.labelStyle, this.unselectedLabelColor, + this.unselectedLabelStyle, }); /// Default value for [TabBar.indicator]. @@ -41,22 +43,32 @@ class TabBarTheme extends Diagnosticable { /// Default value for [TabBar.labelColor]. final Color labelColor; + /// Default value for [TabBar.labelStyle]. + final TextStyle labelStyle; + /// Default value for [TabBar.unselectedLabelColor]. final Color unselectedLabelColor; + /// Default value for [TabBar.unselectedLabelStyle]. + final TextStyle unselectedLabelStyle; + /// Creates a copy of this object but with the given fields replaced with the /// new values. TabBarTheme copyWith({ Decoration indicator, TabBarIndicatorSize indicatorSize, Color labelColor, + TextStyle labelStyle, Color unselectedLabelColor, + TextStyle unselectedLabelStyle, }) { return TabBarTheme( - indicator: indicator ?? this.indicator, - indicatorSize: indicatorSize ?? this.indicatorSize, - labelColor: labelColor ?? this.labelColor, - unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor + indicator: indicator ?? this.indicator, + indicatorSize: indicatorSize ?? this.indicatorSize, + labelColor: labelColor ?? this.labelColor, + labelStyle: labelStyle ?? this.labelStyle, + unselectedLabelColor: unselectedLabelColor ?? this.unselectedLabelColor, + unselectedLabelStyle: unselectedLabelStyle ?? this.unselectedLabelStyle, ); } @@ -78,13 +90,22 @@ class TabBarTheme extends Diagnosticable { indicator: Decoration.lerp(a.indicator, b.indicator, t), indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize, labelColor: Color.lerp(a.labelColor, b.labelColor, t), - unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, 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), ); } @override int get hashCode { - return hashValues(indicator, indicatorSize, labelColor, unselectedLabelColor); + return hashValues( + indicator, + indicatorSize, + labelColor, + labelStyle, + unselectedLabelColor, + unselectedLabelStyle, + ); } @override @@ -97,6 +118,8 @@ class TabBarTheme extends Diagnosticable { return typedOther.indicator == indicator && typedOther.indicatorSize == indicatorSize && typedOther.labelColor == labelColor - && typedOther.unselectedLabelColor == unselectedLabelColor; + && typedOther.labelStyle == labelStyle + && typedOther.unselectedLabelColor == unselectedLabelColor + && typedOther.unselectedLabelStyle == unselectedLabelStyle; } } diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 79405796eda..381b6828486 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -154,20 +154,21 @@ class _TabStyle extends AnimatedWidget { final ThemeData themeData = Theme.of(context); final TabBarTheme tabBarTheme = TabBarTheme.of(context); - final TextStyle defaultStyle = labelStyle ?? themeData.primaryTextTheme.body2; - final TextStyle defaultUnselectedStyle = unselectedLabelStyle ?? labelStyle ?? themeData.primaryTextTheme.body2; + final TextStyle defaultStyle = labelStyle ?? tabBarTheme.labelStyle ?? themeData.primaryTextTheme.body2; + final TextStyle defaultUnselectedStyle = unselectedLabelStyle + ?? tabBarTheme.unselectedLabelStyle + ?? labelStyle + ?? themeData.primaryTextTheme.body2; final Animation animation = listenable; final TextStyle textStyle = selected ? TextStyle.lerp(defaultStyle, defaultUnselectedStyle, animation.value) : TextStyle.lerp(defaultUnselectedStyle, defaultStyle, animation.value); - final Color selectedColor = - labelColor - ?? tabBarTheme.labelColor - ?? themeData.primaryTextTheme.body2.color; - final Color unselectedColor = - unselectedLabelColor - ?? tabBarTheme.unselectedLabelColor - ?? selectedColor.withAlpha(0xB2); // 70% alpha + final Color selectedColor = labelColor + ?? tabBarTheme.labelColor + ?? themeData.primaryTextTheme.body2.color; + final Color unselectedColor = unselectedLabelColor + ?? tabBarTheme.unselectedLabelColor + ?? selectedColor.withAlpha(0xB2); // 70% alpha final Color color = selected ? Color.lerp(selectedColor, unselectedColor, animation.value) : Color.lerp(unselectedColor, selectedColor, animation.value); diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart index d8dfbc5d598..dce4d0b00e4 100644 --- a/packages/flutter/test/material/tab_bar_theme_test.dart +++ b/packages/flutter/test/material/tab_bar_theme_test.dart @@ -20,19 +20,18 @@ const List _tabs = [ Tab(text: _tab3Text, icon: Icon(Icons.looks_3)), ]; -Widget _buildTabBar({ List tabs = _tabs }) { - final TabController _tabController = TabController(length: 3, vsync: const TestVSync()); - - return RepaintBoundary( - key: _painterKey, - child: TabBar(tabs: tabs, controller: _tabController), - ); -} - Widget _withTheme(TabBarTheme theme) { return MaterialApp( theme: ThemeData(tabBarTheme: theme), - home: Scaffold(body: _buildTabBar()), + home: Scaffold( + body: RepaintBoundary( + key: _painterKey, + child: TabBar( + tabs: _tabs, + controller: TabController(length: _tabs.length, vsync: const TestVSync()), + ), + ), + ), ); } @@ -42,6 +41,19 @@ RenderParagraph _iconRenderObject(WidgetTester tester, IconData icon) { } void main() { + testWidgets('Tab bar defaults', (WidgetTester tester) async { + await tester.pumpWidget(_withTheme(null)); + + final RenderParagraph selectedRenderObject = tester.renderObject(find.text(_tab1Text)); + expect(selectedRenderObject.text.style.fontFamily, equals('Roboto')); + expect(selectedRenderObject.text.style.fontSize, equals(14.0)); + expect(selectedRenderObject.text.style.color, equals(Colors.white)); + final RenderParagraph unselectedRenderObject = tester.renderObject(find.text(_tab2Text)); + expect(unselectedRenderObject.text.style.fontFamily, equals('Roboto')); + expect(unselectedRenderObject.text.style.fontSize, equals(14.0)); + expect(unselectedRenderObject.text.style.color, equals(Colors.white.withAlpha(0xB2))); + }); + testWidgets('Tab bar theme overrides label color (selected)', (WidgetTester tester) async { const Color labelColor = Colors.black; const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor); @@ -54,6 +66,51 @@ void main() { expect(iconRenderObject.text.style.color, equals(labelColor)); }); + testWidgets('Tab bar theme overrides label styles', (WidgetTester tester) async { + const TextStyle labelStyle = TextStyle(fontFamily: 'foobar'); + const TextStyle unselectedLabelStyle = TextStyle(fontFamily: 'baz'); + const TabBarTheme tabBarTheme = TabBarTheme( + labelStyle: labelStyle, + unselectedLabelStyle: unselectedLabelStyle, + ); + + await tester.pumpWidget(_withTheme(tabBarTheme)); + + final RenderParagraph selectedRenderObject = tester.renderObject(find.text(_tab1Text)); + expect(selectedRenderObject.text.style.fontFamily, equals(labelStyle.fontFamily)); + final RenderParagraph unselectedRenderObject = tester.renderObject(find.text(_tab2Text)); + expect(unselectedRenderObject.text.style.fontFamily, equals(unselectedLabelStyle.fontFamily)); + }); + + testWidgets('Tab bar label styles override theme label styles', (WidgetTester tester) async { + const TextStyle labelStyle = TextStyle(fontFamily: '1'); + const TextStyle unselectedLabelStyle = TextStyle(fontFamily: '2'); + const TextStyle themeLabelStyle = TextStyle(fontFamily: '3'); + const TextStyle themeUnselectedLabelStyle = TextStyle(fontFamily: '4'); + const TabBarTheme tabBarTheme = TabBarTheme( + labelStyle: themeLabelStyle, + unselectedLabelStyle: themeUnselectedLabelStyle, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(tabBarTheme: tabBarTheme), + home: Scaffold(body: TabBar( + tabs: _tabs, + controller: TabController(length: _tabs.length, vsync: const TestVSync()), + labelStyle: labelStyle, + unselectedLabelStyle: unselectedLabelStyle, + ), + ) + ), + ); + + final RenderParagraph selectedRenderObject = tester.renderObject(find.text(_tab1Text)); + expect(selectedRenderObject.text.style.fontFamily, equals(labelStyle.fontFamily)); + final RenderParagraph unselectedRenderObject = tester.renderObject(find.text(_tab2Text)); + expect(unselectedRenderObject.text.style.fontFamily, equals(unselectedLabelStyle.fontFamily)); + }); + testWidgets('Tab bar theme overrides label color (unselected)', (WidgetTester tester) async { const Color unselectedLabelColor = Colors.black; const TabBarTheme tabBarTheme = TabBarTheme(unselectedLabelColor: unselectedLabelColor);