Fix wrong position of TabBarIndicator when it's label size and has label padding (#116398)
This commit is contained in:
parent
e6ec087112
commit
2ffc5bc17e
@ -332,6 +332,7 @@ class _IndicatorPainter extends CustomPainter {
|
|||||||
required this.tabKeys,
|
required this.tabKeys,
|
||||||
required _IndicatorPainter? old,
|
required _IndicatorPainter? old,
|
||||||
required this.indicatorPadding,
|
required this.indicatorPadding,
|
||||||
|
required this.labelPaddings,
|
||||||
this.dividerColor,
|
this.dividerColor,
|
||||||
}) : assert(controller != null),
|
}) : assert(controller != null),
|
||||||
assert(indicator != null),
|
assert(indicator != null),
|
||||||
@ -347,6 +348,7 @@ class _IndicatorPainter extends CustomPainter {
|
|||||||
final EdgeInsetsGeometry indicatorPadding;
|
final EdgeInsetsGeometry indicatorPadding;
|
||||||
final List<GlobalKey> tabKeys;
|
final List<GlobalKey> tabKeys;
|
||||||
final Color? dividerColor;
|
final Color? dividerColor;
|
||||||
|
final List<EdgeInsetsGeometry> labelPaddings;
|
||||||
|
|
||||||
// _currentTabOffsets and _currentTextDirection are set each time TabBar
|
// _currentTabOffsets and _currentTextDirection are set each time TabBar
|
||||||
// layout is completed. These values can be null when TabBar contains no
|
// layout is completed. These values can be null when TabBar contains no
|
||||||
@ -402,9 +404,11 @@ class _IndicatorPainter extends CustomPainter {
|
|||||||
|
|
||||||
if (indicatorSize == TabBarIndicatorSize.label) {
|
if (indicatorSize == TabBarIndicatorSize.label) {
|
||||||
final double tabWidth = tabKeys[tabIndex].currentContext!.size!.width;
|
final double tabWidth = tabKeys[tabIndex].currentContext!.size!.width;
|
||||||
final double delta = ((tabRight - tabLeft) - tabWidth) / 2.0;
|
final EdgeInsetsGeometry labelPadding = labelPaddings[tabIndex];
|
||||||
tabLeft += delta;
|
final EdgeInsets insets = labelPadding.resolve(_currentTextDirection);
|
||||||
tabRight -= delta;
|
final double delta = ((tabRight - tabLeft) - (tabWidth + insets.horizontal)) / 2.0;
|
||||||
|
tabLeft += delta + insets.left;
|
||||||
|
tabRight = tabLeft + tabWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
final EdgeInsets insets = indicatorPadding.resolve(_currentTextDirection);
|
final EdgeInsets insets = indicatorPadding.resolve(_currentTextDirection);
|
||||||
@ -952,6 +956,7 @@ class _TabBarState extends State<TabBar> {
|
|||||||
int? _currentIndex;
|
int? _currentIndex;
|
||||||
late double _tabStripWidth;
|
late double _tabStripWidth;
|
||||||
late List<GlobalKey> _tabKeys;
|
late List<GlobalKey> _tabKeys;
|
||||||
|
late List<EdgeInsetsGeometry> _labelPaddings;
|
||||||
bool _debugHasScheduledValidTabsCountCheck = false;
|
bool _debugHasScheduledValidTabsCountCheck = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -960,6 +965,7 @@ class _TabBarState extends State<TabBar> {
|
|||||||
// If indicatorSize is TabIndicatorSize.label, _tabKeys[i] is used to find
|
// If indicatorSize is TabIndicatorSize.label, _tabKeys[i] is used to find
|
||||||
// the width of tab widget i. See _IndicatorPainter.indicatorRect().
|
// the width of tab widget i. See _IndicatorPainter.indicatorRect().
|
||||||
_tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();
|
_tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();
|
||||||
|
_labelPaddings = List<EdgeInsetsGeometry>.filled(widget.tabs.length, EdgeInsets.zero, growable: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Decoration _getIndicator() {
|
Decoration _getIndicator() {
|
||||||
@ -1063,6 +1069,7 @@ class _TabBarState extends State<TabBar> {
|
|||||||
tabKeys: _tabKeys,
|
tabKeys: _tabKeys,
|
||||||
old: _indicatorPainter,
|
old: _indicatorPainter,
|
||||||
dividerColor: theme.useMaterial3 ? widget.dividerColor ?? defaults.dividerColor : null,
|
dividerColor: theme.useMaterial3 ? widget.dividerColor ?? defaults.dividerColor : null,
|
||||||
|
labelPaddings: _labelPaddings,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1098,8 +1105,10 @@ class _TabBarState extends State<TabBar> {
|
|||||||
if (widget.tabs.length > _tabKeys.length) {
|
if (widget.tabs.length > _tabKeys.length) {
|
||||||
final int delta = widget.tabs.length - _tabKeys.length;
|
final int delta = widget.tabs.length - _tabKeys.length;
|
||||||
_tabKeys.addAll(List<GlobalKey>.generate(delta, (int n) => GlobalKey()));
|
_tabKeys.addAll(List<GlobalKey>.generate(delta, (int n) => GlobalKey()));
|
||||||
|
_labelPaddings.addAll(List<EdgeInsetsGeometry>.filled(delta, EdgeInsets.zero));
|
||||||
} else if (widget.tabs.length < _tabKeys.length) {
|
} else if (widget.tabs.length < _tabKeys.length) {
|
||||||
_tabKeys.removeRange(widget.tabs.length, _tabKeys.length);
|
_tabKeys.removeRange(widget.tabs.length, _tabKeys.length);
|
||||||
|
_labelPaddings.removeRange(widget.tabs.length, _tabKeys.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1274,10 +1283,12 @@ class _TabBarState extends State<TabBar> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_labelPaddings[index] = adjustedPadding ?? widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding;
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
heightFactor: 1.0,
|
heightFactor: 1.0,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: adjustedPadding ?? widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding,
|
padding: _labelPaddings[index],
|
||||||
child: KeyedSubtree(
|
child: KeyedSubtree(
|
||||||
key: _tabKeys[index],
|
key: _tabKeys[index],
|
||||||
child: widget.tabs[index],
|
child: widget.tabs[index],
|
||||||
|
@ -2788,6 +2788,77 @@ void main() {
|
|||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('TabBar with labelPadding(TabBarIndicatorSize.label)', (WidgetTester tester) async {
|
||||||
|
const double indicatorWeight = 2.0; // default indicator weight
|
||||||
|
const EdgeInsets labelPadding = EdgeInsets.only(left: 7.0, right: 4.0);
|
||||||
|
const EdgeInsets indicatorPadding = EdgeInsets.only(left: 3.0, right: 7.0);
|
||||||
|
|
||||||
|
final List<Widget> tabs = <Widget>[
|
||||||
|
SizedBox(key: UniqueKey(), width: 130.0, height: 30.0),
|
||||||
|
SizedBox(key: UniqueKey(), width: 140.0, height: 40.0),
|
||||||
|
SizedBox(key: UniqueKey(), width: 150.0, height: 50.0),
|
||||||
|
];
|
||||||
|
|
||||||
|
final TabController controller = TabController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
length: tabs.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
boilerplate(
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: TabBar(
|
||||||
|
labelPadding: labelPadding,
|
||||||
|
indicatorPadding: indicatorPadding,
|
||||||
|
isScrollable: true,
|
||||||
|
controller: controller,
|
||||||
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
tabs: tabs,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final RenderBox tabBarBox = tester.firstRenderObject<RenderBox>(find.byType(TabBar));
|
||||||
|
const double tabBarHeight = 50.0 + indicatorWeight; // 50 = max tab height
|
||||||
|
expect(tabBarBox.size.height, tabBarHeight);
|
||||||
|
|
||||||
|
// Tab0 width = 130, height = 30
|
||||||
|
double tabLeft = labelPadding.left;
|
||||||
|
double tabRight = tabLeft + 130.0;
|
||||||
|
double tabTop = (tabBarHeight - indicatorWeight - 30.0) / 2.0;
|
||||||
|
double tabBottom = tabTop + 30.0;
|
||||||
|
Rect tabRect = Rect.fromLTRB(tabLeft, tabTop, tabRight, tabBottom);
|
||||||
|
expect(tester.getRect(find.byKey(tabs[0].key!)), tabRect);
|
||||||
|
|
||||||
|
// Tab1 width = 140, height = 40
|
||||||
|
tabLeft = tabRight + labelPadding.right + labelPadding.left;
|
||||||
|
tabRight = tabLeft + 140.0;
|
||||||
|
tabTop = (tabBarHeight - indicatorWeight - 40.0) / 2.0;
|
||||||
|
tabBottom = tabTop + 40.0;
|
||||||
|
tabRect = Rect.fromLTRB(tabLeft, tabTop, tabRight, tabBottom);
|
||||||
|
expect(tester.getRect(find.byKey(tabs[1].key!)), tabRect);
|
||||||
|
|
||||||
|
// Tab2 width = 150, height = 50
|
||||||
|
tabLeft = tabRight + labelPadding.right + labelPadding.left;
|
||||||
|
tabRight = tabLeft + 150.0;
|
||||||
|
tabTop = (tabBarHeight - indicatorWeight - 50.0) / 2.0;
|
||||||
|
tabBottom = tabTop + 50.0;
|
||||||
|
tabRect = Rect.fromLTRB(tabLeft, tabTop, tabRight, tabBottom);
|
||||||
|
expect(tester.getRect(find.byKey(tabs[2].key!)), tabRect);
|
||||||
|
|
||||||
|
// Tab 0 selected
|
||||||
|
final double indicatorLeft = indicatorPadding.left + labelPadding.left + indicatorWeight / 2.0;
|
||||||
|
final double indicatorRight = labelPadding.left + 130.0 - indicatorPadding.right - indicatorWeight / 2.0;
|
||||||
|
final double indicatorY = tabBottom + indicatorWeight / 2.0;
|
||||||
|
expect(tabBarBox, paints..line(
|
||||||
|
strokeWidth: indicatorWeight,
|
||||||
|
p1: Offset(indicatorLeft, indicatorY),
|
||||||
|
p2: Offset(indicatorRight, indicatorY),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Overflowing RTL tab bar', (WidgetTester tester) async {
|
testWidgets('Overflowing RTL tab bar', (WidgetTester tester) async {
|
||||||
final List<Widget> tabs = List<Widget>.filled(100,
|
final List<Widget> tabs = List<Widget>.filled(100,
|
||||||
// For convenience padded width of each tab will equal 100:
|
// For convenience padded width of each tab will equal 100:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user