Tweaked TabBar to provide uniform padding to all tabs in cases where only few tabs contain both icon and text (#80237)

This commit is contained in:
Chinmoy 2021-05-11 04:44:04 +05:30 committed by GitHub
parent 457f513f37
commit efd3cc5ca7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 144 additions and 11 deletions

View file

@ -45,6 +45,10 @@ class TabBarTheme with Diagnosticable {
final Color? labelColor;
/// Default value for [TabBar.labelPadding].
///
/// If there are few tabs with both icon and text and few
/// tabs with only icon or text, this padding is vertically
/// adjusted to provide uniform padding to all tabs.
final EdgeInsetsGeometry? labelPadding;
/// Default value for [TabBar.labelStyle].

View file

@ -833,6 +833,10 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
/// The padding added to each of the tab labels.
///
/// If there are few tabs with both icon and text and few
/// tabs with only icon or text, this padding is vertically
/// adjusted to provide uniform padding to all tabs.
///
/// If this property is null, then kTabLabelPadding is used.
final EdgeInsetsGeometry? labelPadding;
@ -910,6 +914,21 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
return Size.fromHeight(maxHeight + indicatorWeight);
}
/// Returns whether the [TabBar] contains a tab with both text and icon.
///
/// [TabBar] uses this to give uniform padding to all tabs in cases where
/// there are some tabs with both text and icon and some which contain only
/// text or icon.
bool get tabHasTextAndIcon {
for (final Widget item in tabs) {
if (item is PreferredSizeWidget) {
if (item.preferredSize.height == _kTextAndIconTabHeight)
return true;
}
}
return false;
}
@override
_TabBarState createState() => _TabBarState();
}
@ -1168,19 +1187,33 @@ class _TabBarState extends State<TabBar> {
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
final List<Widget> wrappedTabs = <Widget>[
for (int i = 0; i < widget.tabs.length; i += 1)
Center(
heightFactor: 1.0,
child: Padding(
padding: widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding,
child: KeyedSubtree(
key: _tabKeys[i],
child: widget.tabs[i],
),
final List<Widget> wrappedTabs = List<Widget>.generate(widget.tabs.length, (int index) {
const double verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight)/2.0;
EdgeInsetsGeometry? adjustedPadding;
if (widget.tabs[index] is PreferredSizeWidget) {
final PreferredSizeWidget tab = widget.tabs[index] as PreferredSizeWidget;
if (widget.tabHasTextAndIcon && tab.preferredSize.height == _kTabHeight) {
if (widget.labelPadding != null || tabBarTheme.labelPadding != null) {
adjustedPadding = (widget.labelPadding ?? tabBarTheme.labelPadding!).add(const EdgeInsets.symmetric(vertical: verticalAdjustment));
}
else {
adjustedPadding = const EdgeInsets.symmetric(vertical: verticalAdjustment, horizontal: 16.0);
}
}
}
return Center(
heightFactor: 1.0,
child: Padding(
padding: adjustedPadding ?? widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding,
child: KeyedSubtree(
key: _tabKeys[index],
child: widget.tabs[index],
),
),
];
);
});
// If the controller was provided by DefaultTabController and we're part
// of a Hero (typically the AppBar), then we will not be able to find the

View file

@ -3532,6 +3532,102 @@ void main() {
expect(tabBar.preferredSize, const Size.fromHeight(48.0));
});
testWidgets('Tabs are given uniform padding in case of few tabs having both text and icon', (WidgetTester tester) async {
const EdgeInsetsGeometry expectedPaddingAdjusted = EdgeInsets.symmetric(vertical: 13.0, horizontal: 16.0);
const EdgeInsetsGeometry expectedPaddingDefault = EdgeInsets.symmetric(vertical: 0.0, horizontal: 16.0);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: TabController(length: 3, vsync: const TestVSync()),
tabs: const <Widget>[
Tab(text: 'Tab 1', icon: Icon(Icons.plus_one)),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
),
),
);
final Padding tabOne = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 1').first);
final Padding tabTwo = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 2').first);
final Padding tabThree = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 3').first);
expect(tabOne.padding, expectedPaddingDefault);
expect(tabTwo.padding, expectedPaddingAdjusted);
expect(tabThree.padding, expectedPaddingAdjusted);
});
testWidgets('Tabs are given uniform padding when labelPadding is given', (WidgetTester tester) async {
const EdgeInsetsGeometry labelPadding = EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0);
const EdgeInsetsGeometry expectedPaddingAdjusted = EdgeInsets.symmetric(vertical: 23.0, horizontal: 20.0);
const EdgeInsetsGeometry expectedPaddingDefault = EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
labelPadding: labelPadding,
controller: TabController(length: 3, vsync: const TestVSync()),
tabs: const <Widget>[
Tab(text: 'Tab 1', icon: Icon(Icons.plus_one)),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
),
),
);
final Padding tabOne = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 1').first);
final Padding tabTwo = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 2').first);
final Padding tabThree = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 3').first);
expect(tabOne.padding, expectedPaddingDefault);
expect(tabTwo.padding, expectedPaddingAdjusted);
expect(tabThree.padding, expectedPaddingAdjusted);
});
testWidgets('Tabs are given uniform padding TabBarTheme.labelPadding is given', (WidgetTester tester) async {
const EdgeInsetsGeometry labelPadding = EdgeInsets.symmetric(vertical: 15.0, horizontal: 20);
const EdgeInsetsGeometry expectedPaddingAdjusted = EdgeInsets.symmetric(vertical: 28.0, horizontal: 20.0);
const EdgeInsetsGeometry expectedPaddingDefault = EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
tabBarTheme: const TabBarTheme(labelPadding: labelPadding),
),
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: TabController(length: 3, vsync: const TestVSync()),
tabs: const <Widget>[
Tab(text: 'Tab 1', icon: Icon(Icons.plus_one)),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
),
),
);
final Padding tabOne = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 1').first);
final Padding tabTwo = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 2').first);
final Padding tabThree = tester.widget<Padding>(find.widgetWithText(Padding, 'Tab 3').first);
expect(tabOne.padding, expectedPaddingDefault);
expect(tabTwo.padding, expectedPaddingAdjusted);
expect(tabThree.padding, expectedPaddingAdjusted);
});
}
class KeepAliveInk extends StatefulWidget {