Skip to content

Commit

Permalink
Introduce TabBar.textScaler for tab label upper text scale limit (#…
Browse files Browse the repository at this point in the history
…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)
  • Loading branch information
TahaTesser authored May 7, 2024
1 parent 8796562 commit 74356f3
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 2 deletions.
11 changes: 10 additions & 1 deletion packages/flutter/lib/src/material/tab_bar_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class TabBarTheme with Diagnosticable {
this.splashFactory,
this.mouseCursor,
this.tabAlignment,
this.textScaler,
});

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

/// Overrides the default value for [TabBar.textScaler].
final TextScaler? textScaler;

/// Creates a copy of this object but with the given fields replaced with the
/// new values.
TabBarTheme copyWith({
Expand All @@ -115,6 +119,7 @@ class TabBarTheme with Diagnosticable {
InteractiveInkFeatureFactory? splashFactory,
MaterialStateProperty<MouseCursor?>? mouseCursor,
TabAlignment? tabAlignment,
TextScaler? textScaler,
}) {
return TabBarTheme(
indicator: indicator ?? this.indicator,
Expand All @@ -131,6 +136,7 @@ class TabBarTheme with Diagnosticable {
splashFactory: splashFactory ?? this.splashFactory,
mouseCursor: mouseCursor ?? this.mouseCursor,
tabAlignment: tabAlignment ?? this.tabAlignment,
textScaler: textScaler ?? this.textScaler,
);
}

Expand Down Expand Up @@ -161,6 +167,7 @@ class TabBarTheme with Diagnosticable {
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,
);
}

Expand All @@ -180,6 +187,7 @@ class TabBarTheme with Diagnosticable {
splashFactory,
mouseCursor,
tabAlignment,
textScaler,
);

@override
Expand All @@ -204,6 +212,7 @@ class TabBarTheme with Diagnosticable {
&& other.overlayColor == overlayColor
&& other.splashFactory == splashFactory
&& other.mouseCursor == mouseCursor
&& other.tabAlignment == tabAlignment;
&& other.tabAlignment == tabAlignment
&& other.textScaler == textScaler;
}
}
17 changes: 16 additions & 1 deletion packages/flutter/lib/src/material/tabs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.splashFactory,
this.splashBorderRadius,
this.tabAlignment,
this.textScaler,
}) : _isPrimary = true,
assert(indicator != null || (indicatorWeight > 0.0));

Expand Down Expand Up @@ -915,6 +916,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.splashFactory,
this.splashBorderRadius,
this.tabAlignment,
this.textScaler,
}) : _isPrimary = false,
assert(indicator != null || (indicatorWeight > 0.0));

Expand Down Expand Up @@ -1246,6 +1248,16 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// 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
/// 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.
///
/// See also:
/// * [TextScaler], which is used to scale text based on the device's text scale factor.
final TextScaler? textScaler;

/// A size whose height depends on if the tabs have both icons and text.
///
/// [AppBar] uses this size to compute its own preferred size.
Expand Down Expand Up @@ -1828,7 +1840,10 @@ class _TabBarState extends State<TabBar> {
);
}

return tabBar;
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaler: widget.textScaler ?? tabBarTheme.textScaler),
child: tabBar,
);
}
}

Expand Down
45 changes: 45 additions & 0 deletions packages/flutter/test/material/tab_bar_theme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@Tags(<String>['reduced-test-set'])
library;

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
Expand Down Expand Up @@ -1539,4 +1540,48 @@ void main() {
expect(selectedTextStyle.color, selectedColor);
expect(unselectedTextStyle.color, unselectedColor);
});

testWidgets('TabBarTheme.textScaler overrides tab label text scale, textScaleFactor = noScaling, 1.75, 2.0', (WidgetTester tester) async {
final List<String> tabs = <String>['Tab 1', 'Tab 2'];

Widget buildTabs({ TextScaler? textScaler }) {
return MaterialApp(
theme: ThemeData(
tabBarTheme: TabBarTheme(
textScaler: textScaler,
),
),
home: MediaQuery(
data: const MediaQueryData(textScaler: TextScaler.linear(3.0)),
child: DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: tabs.map((String tab) => Tab(text: tab)).toList(),
),
),
),
),
),
);
}

await tester.pumpWidget(buildTabs(textScaler: TextScaler.noScaling));

Size labelSize = tester.getSize(find.text('Tab 1'));
expect(labelSize, equals(const Size(70.5, 20.0)));

await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(1.75)));
await tester.pumpAndSettle();

labelSize = tester.getSize(find.text('Tab 1'));
expect(labelSize, equals(const Size(123.0, 35.0)));

await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(2.0)));
await tester.pumpAndSettle();

labelSize = tester.getSize(find.text('Tab 1'));
expect(labelSize, equals(const Size(140.5, 40.0)));
}, skip: isBrowser && !isSkiaWeb); // https://github.com/flutter/flutter/issues/87543
}
39 changes: 39 additions & 0 deletions packages/flutter/test/material/tabs_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
Expand Down Expand Up @@ -7119,4 +7120,42 @@ void main() {
expect(config.textDirection, TextDirection.rtl);
expect(config.devicePixelRatio, 2.33);
});

testWidgets('TabBar.textScaler overrides tab label text scale, textScaleFactor = noScaling, 1.75, 2.0', (WidgetTester tester) async {
final List<String> tabs = <String>['Tab 1', 'Tab 2'];

Widget buildTabs({ TextScaler? textScaler }) {
return MaterialApp(
home: MediaQuery(
data: const MediaQueryData(textScaler: TextScaler.linear(3.0)),
child: DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
textScaler: textScaler,
tabs: tabs.map((String tab) => Tab(text: tab)).toList(),
),
),
),
),
),
);
}

await tester.pumpWidget(buildTabs(textScaler: TextScaler.noScaling));

Size labelSize = tester.getSize(find.text('Tab 1'));
expect(labelSize, equals(const Size(70.5, 20.0)));

await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(1.75)));

labelSize = tester.getSize(find.text('Tab 1'));
expect(labelSize, equals(const Size(123.0, 35.0)));

await tester.pumpWidget(buildTabs(textScaler: const TextScaler.linear(2.0)));

labelSize = tester.getSize(find.text('Tab 1'));
expect(labelSize, equals(const Size(140.5, 40.0)));
}, skip: isBrowser && !isSkiaWeb); // https://github.com/flutter/flutter/issues/87543
}

0 comments on commit 74356f3

Please sign in to comment.