Unverified Commit af616a0b authored by nero's avatar nero Committed by GitHub

[Tabs] Fix tab indicator flies off issue (#65463)

parent be4cc764
......@@ -411,29 +411,14 @@ class _IndicatorPainter extends CustomPainter {
_needsPaint = false;
_painter ??= indicator.createBoxPainter(markNeedsPaint);
if (controller.indexIsChanging) {
// The user tapped on a tab, the tab controller's animation is running.
final Rect targetRect = indicatorRect(size, controller.index);
_currentRect = Rect.lerp(targetRect, _currentRect ?? targetRect, _indexChangeProgress(controller));
} else {
// The user is dragging the TabBarView's PageView left or right.
final int currentIndex = controller.index;
final Rect? previous = currentIndex > 0 ? indicatorRect(size, currentIndex - 1) : null;
final Rect middle = indicatorRect(size, currentIndex);
final Rect? next = currentIndex < maxTabIndex ? indicatorRect(size, currentIndex + 1) : null;
final double index = controller.index.toDouble();
final double value = controller.animation!.value;
if (value == index - 1.0)
_currentRect = previous ?? middle;
else if (value == index + 1.0)
_currentRect = next ?? middle;
else if (value == index)
_currentRect = middle;
else if (value < index)
_currentRect = previous == null ? middle : Rect.lerp(middle, previous, index - value);
else
_currentRect = next == null ? middle : Rect.lerp(middle, next, value - index);
}
final double index = controller.index.toDouble();
final double value = controller.animation!.value;
final bool ltr = index > value;
final int from = (ltr ? value.floor() : value.ceil()).clamp(0, maxTabIndex).toInt();
final int to = (ltr ? from + 1 : from - 1).clamp(0, maxTabIndex).toInt();
final Rect fromRect = indicatorRect(size, from);
final Rect toRect = indicatorRect(size, to);
_currentRect = Rect.lerp(fromRect, toRect, (value - from).abs());
assert(_currentRect != null);
final ImageConfiguration configuration = ImageConfiguration(
......
......@@ -1955,11 +1955,10 @@ void main() {
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
// The x coordinates of p1 and p2 were derived empirically, not analytically.
expect(tabBarBox, paints..line(
strokeWidth: indicatorWeight,
p1: const Offset(2476.0, indicatorY),
p2: const Offset(2574.0, indicatorY),
p1: const Offset(4951.0, indicatorY),
p2: const Offset(5049.0, indicatorY),
));
await tester.pump(const Duration(milliseconds: 501));
......@@ -1974,6 +1973,82 @@ void main() {
));
});
testWidgets('Tab indicator animation test', (WidgetTester tester) async {
const double indicatorWeight = 8.0;
final List<Widget> tabs = List<Widget>.generate(4, (int index) {
return Tab(text: 'Tab $index');
});
final TabController controller = TabController(
vsync: const TestVSync(),
length: tabs.length,
);
await tester.pumpWidget(
boilerplate(
child: Container(
alignment: Alignment.topLeft,
child: TabBar(
indicatorWeight: indicatorWeight,
controller: controller,
tabs: tabs,
),
),
),
);
final RenderBox tabBarBox = tester.firstRenderObject<RenderBox>(find.byType(TabBar));
// Initial indicator position.
const double indicatorY = 54.0 - indicatorWeight / 2.0;
double indicatorLeft = indicatorWeight / 2.0;
double indicatorRight = 200.0 - (indicatorWeight / 2.0);
expect(tabBarBox, paints..line(
strokeWidth: indicatorWeight,
p1: Offset(indicatorLeft, indicatorY),
p2: Offset(indicatorRight, indicatorY),
));
// Select tab 1.
controller.animateTo(1, duration: const Duration(milliseconds: 1000), curve: Curves.linear);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
indicatorLeft = 100.0 + indicatorWeight / 2.0;
indicatorRight = 300.0 - (indicatorWeight / 2.0);
expect(tabBarBox, paints..line(
strokeWidth: indicatorWeight,
p1: Offset(indicatorLeft, indicatorY),
p2: Offset(indicatorRight, indicatorY),
));
// Select tab 2 when animation is running.
controller.animateTo(2, duration: const Duration(milliseconds: 1000), curve: Curves.linear);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
indicatorLeft = 250.0 + indicatorWeight / 2.0;
indicatorRight = 450.0 - (indicatorWeight / 2.0);
expect(tabBarBox, paints..line(
strokeWidth: indicatorWeight,
p1: Offset(indicatorLeft, indicatorY),
p2: Offset(indicatorRight, indicatorY),
));
// Final indicator position.
await tester.pumpAndSettle();
indicatorLeft = 400.0 + indicatorWeight / 2.0;
indicatorRight = 600.0 - (indicatorWeight / 2.0);
expect(tabBarBox, paints..line(
strokeWidth: indicatorWeight,
p1: Offset(indicatorLeft, indicatorY),
p2: Offset(indicatorRight, indicatorY),
));
});
testWidgets('correct semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment