Skip to content

Commit 74356f3

Browse files
authored
Introduce TabBar.textScaler for tab label upper text scale limit (#147232)
fixes [Tab is hardcoding the height of the icons and the text ](flutter/flutter#13322) ### Description This PR introduces `TabBar.textScaler` to provide upper text scale limit for tab label. ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @OverRide Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: MediaQuery( data: const MediaQueryData(textScaler: TextScaler.linear(3.0)), child: DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( title: const Text('Sample'), bottom: const TabBar( textScaler: TextScaler.linear(2.0), tabs: <Widget>[ Tab(text: 'Tab 1'), Tab(text: 'Tab 2'), Tab(text: 'Tab 3'), ], ), ), floatingActionButton: Builder(builder: (BuildContext context) { return FloatingActionButton( onPressed: () { print(MediaQuery.textScalerOf(context)); }, child: const Icon(Icons.add), ); }), ), ), ), ); } } ``` </details> ### Without `TabBar.textScaler` ![Screenshot 2024-04-30 at 13 46 10](https://github.com/flutter/flutter/assets/48603081/99db889a-b717-4ddf-b99e-89fdf7edb3ac) ### With `TabBar.textScaler` ```dart bottom: const TabBar( textScaler: TextScaler.linear(2.0), tabs: <Widget>[ Tab(text: 'Tab 1'), Tab(text: 'Tab 2'), Tab(text: 'Tab 3'), ], ), ``` ![Screenshot 2024-04-30 at 14 04 22](https://github.com/flutter/flutter/assets/48603081/92216cbb-eb89-4c0a-b2f2-feb6a33a3337)
1 parent 8796562 commit 74356f3

4 files changed

Lines changed: 110 additions & 2 deletions

File tree

packages/flutter/lib/src/material/tab_bar_theme.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class TabBarTheme with Diagnosticable {
4242
this.splashFactory,
4343
this.mouseCursor,
4444
this.tabAlignment,
45+
this.textScaler,
4546
});
4647

4748
/// Overrides the default value for [TabBar.indicator].
@@ -98,6 +99,9 @@ class TabBarTheme with Diagnosticable {
9899
/// Overrides the default value for [TabBar.tabAlignment].
99100
final TabAlignment? tabAlignment;
100101

102+
/// Overrides the default value for [TabBar.textScaler].
103+
final TextScaler? textScaler;
104+
101105
/// Creates a copy of this object but with the given fields replaced with the
102106
/// new values.
103107
TabBarTheme copyWith({
@@ -115,6 +119,7 @@ class TabBarTheme with Diagnosticable {
115119
InteractiveInkFeatureFactory? splashFactory,
116120
MaterialStateProperty<MouseCursor?>? mouseCursor,
117121
TabAlignment? tabAlignment,
122+
TextScaler? textScaler,
118123
}) {
119124
return TabBarTheme(
120125
indicator: indicator ?? this.indicator,
@@ -131,6 +136,7 @@ class TabBarTheme with Diagnosticable {
131136
splashFactory: splashFactory ?? this.splashFactory,
132137
mouseCursor: mouseCursor ?? this.mouseCursor,
133138
tabAlignment: tabAlignment ?? this.tabAlignment,
139+
textScaler: textScaler ?? this.textScaler,
134140
);
135141
}
136142

@@ -161,6 +167,7 @@ class TabBarTheme with Diagnosticable {
161167
splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory,
162168
mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor,
163169
tabAlignment: t < 0.5 ? a.tabAlignment : b.tabAlignment,
170+
textScaler: t < 0.5 ? a.textScaler : b.textScaler,
164171
);
165172
}
166173

@@ -180,6 +187,7 @@ class TabBarTheme with Diagnosticable {
180187
splashFactory,
181188
mouseCursor,
182189
tabAlignment,
190+
textScaler,
183191
);
184192

185193
@override
@@ -204,6 +212,7 @@ class TabBarTheme with Diagnosticable {
204212
&& other.overlayColor == overlayColor
205213
&& other.splashFactory == splashFactory
206214
&& other.mouseCursor == mouseCursor
207-
&& other.tabAlignment == tabAlignment;
215+
&& other.tabAlignment == tabAlignment
216+
&& other.textScaler == textScaler;
208217
}
209218
}

packages/flutter/lib/src/material/tabs.dart

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
864864
this.splashFactory,
865865
this.splashBorderRadius,
866866
this.tabAlignment,
867+
this.textScaler,
867868
}) : _isPrimary = true,
868869
assert(indicator != null || (indicatorWeight > 0.0));
869870

@@ -915,6 +916,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
915916
this.splashFactory,
916917
this.splashBorderRadius,
917918
this.tabAlignment,
919+
this.textScaler,
918920
}) : _isPrimary = false,
919921
assert(indicator != null || (indicatorWeight > 0.0));
920922

@@ -1246,6 +1248,16 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
12461248
/// otherwise [TabAlignment.fill] is used.
12471249
final TabAlignment? tabAlignment;
12481250

1251+
/// Specifies the text scaling behavior for the [Tab] label.
1252+
///
1253+
/// If this is null, then the value of [TabBarTheme.textScaler] is used. If that is
1254+
/// also null, then the text scaling behavior is determined by the [MediaQueryData.textScaler]
1255+
/// from the ambient [MediaQuery], or 1.0 if there is no [MediaQuery] in scope.
1256+
///
1257+
/// See also:
1258+
/// * [TextScaler], which is used to scale text based on the device's text scale factor.
1259+
final TextScaler? textScaler;
1260+
12491261
/// A size whose height depends on if the tabs have both icons and text.
12501262
///
12511263
/// [AppBar] uses this size to compute its own preferred size.
@@ -1828,7 +1840,10 @@ class _TabBarState extends State<TabBar> {
18281840
);
18291841
}
18301842

1831-
return tabBar;
1843+
return MediaQuery(
1844+
data: MediaQuery.of(context).copyWith(textScaler: widget.textScaler ?? tabBarTheme.textScaler),
1845+
child: tabBar,
1846+
);
18321847
}
18331848
}
18341849

packages/flutter/test/material/tab_bar_theme_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@Tags(<String>['reduced-test-set'])
88
library;
99

10+
import 'package:flutter/foundation.dart';
1011
import 'package:flutter/gestures.dart';
1112
import 'package:flutter/material.dart';
1213
import 'package:flutter/rendering.dart';
@@ -1539,4 +1540,48 @@ void main() {
15391540
expect(selectedTextStyle.color, selectedColor);
15401541
expect(unselectedTextStyle.color, unselectedColor);
15411542
});
1543+
1544+
testWidgets('TabBarTheme.textScaler overrides tab label text scale, textScaleFactor = noScaling, 1.75, 2.0', (WidgetTester tester) async {
1545+
final List<String> tabs = <String>['Tab 1', 'Tab 2'];
1546+
1547+
Widget buildTabs({ TextScaler? textScaler }) {
1548+
return MaterialApp(
1549+
theme: ThemeData(
1550+
tabBarTheme: TabBarTheme(
1551+
textScaler: textScaler,
1552+
),
1553+
),
1554+
home: MediaQuery(
1555+
data: const MediaQueryData(textScaler: TextScaler.linear(3.0)),
1556+
child: DefaultTabController(
1557+
length: tabs.length,
1558+
child: Scaffold(
1559+
appBar: AppBar(
1560+
bottom: TabBar(
1561+
tabs: tabs.map((String tab) => Tab(text: tab)).toList(),
1562+
),
1563+
),
1564+
),
1565+
),
1566+
),
1567+
);
1568+
}
1569+
1570+
await tester.pumpWidget(buildTabs(textScaler: TextScaler.noScaling));
1571+
1572+
Size labelSize = tester.getSize(find.text('Tab 1'));
1573+
expect(labelSize, equals(const Size(70.5, 20.0)));
1574+
1575+
await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(1.75)));
1576+
await tester.pumpAndSettle();
1577+
1578+
labelSize = tester.getSize(find.text('Tab 1'));
1579+
expect(labelSize, equals(const Size(123.0, 35.0)));
1580+
1581+
await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(2.0)));
1582+
await tester.pumpAndSettle();
1583+
1584+
labelSize = tester.getSize(find.text('Tab 1'));
1585+
expect(labelSize, equals(const Size(140.5, 40.0)));
1586+
}, skip: isBrowser && !isSkiaWeb); // https://github.com/flutter/flutter/issues/87543
15421587
}

packages/flutter/test/material/tabs_test.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:flutter/foundation.dart';
56
import 'package:flutter/gestures.dart';
67
import 'package:flutter/material.dart';
78
import 'package:flutter/rendering.dart';
@@ -7119,4 +7120,42 @@ void main() {
71197120
expect(config.textDirection, TextDirection.rtl);
71207121
expect(config.devicePixelRatio, 2.33);
71217122
});
7123+
7124+
testWidgets('TabBar.textScaler overrides tab label text scale, textScaleFactor = noScaling, 1.75, 2.0', (WidgetTester tester) async {
7125+
final List<String> tabs = <String>['Tab 1', 'Tab 2'];
7126+
7127+
Widget buildTabs({ TextScaler? textScaler }) {
7128+
return MaterialApp(
7129+
home: MediaQuery(
7130+
data: const MediaQueryData(textScaler: TextScaler.linear(3.0)),
7131+
child: DefaultTabController(
7132+
length: tabs.length,
7133+
child: Scaffold(
7134+
appBar: AppBar(
7135+
bottom: TabBar(
7136+
textScaler: textScaler,
7137+
tabs: tabs.map((String tab) => Tab(text: tab)).toList(),
7138+
),
7139+
),
7140+
),
7141+
),
7142+
),
7143+
);
7144+
}
7145+
7146+
await tester.pumpWidget(buildTabs(textScaler: TextScaler.noScaling));
7147+
7148+
Size labelSize = tester.getSize(find.text('Tab 1'));
7149+
expect(labelSize, equals(const Size(70.5, 20.0)));
7150+
7151+
await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(1.75)));
7152+
7153+
labelSize = tester.getSize(find.text('Tab 1'));
7154+
expect(labelSize, equals(const Size(123.0, 35.0)));
7155+
7156+
await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(2.0)));
7157+
7158+
labelSize = tester.getSize(find.text('Tab 1'));
7159+
expect(labelSize, equals(const Size(140.5, 40.0)));
7160+
}, skip: isBrowser && !isSkiaWeb); // https://github.com/flutter/flutter/issues/87543
71227161
}

0 commit comments

Comments
 (0)