[go: nahoru, domu]

Skip to content

Commit

Permalink
TabBar should adjust scroll position when Controller is changed (#116…
Browse files Browse the repository at this point in the history
…019)

Co-authored-by: Bruno Leroux <bruno.leroux@gmail.com>
  • Loading branch information
bleroux and bleroux committed Nov 28, 2022
1 parent 2703a2b commit d841d32
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 11 deletions.
38 changes: 27 additions & 11 deletions packages/flutter/lib/src/material/tabs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -519,26 +519,35 @@ class _TabBarScrollPosition extends ScrollPositionWithSingleContext {

final _TabBarState tabBar;

bool? _initialViewportDimensionWasZero;
bool _viewportDimensionWasNonZero = false;

// Position should be adjusted at least once.
bool _needsPixelsCorrection = true;

@override
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
bool result = true;
if (_initialViewportDimensionWasZero != true) {
// If the viewport never had a non-zero dimension, we just want to jump
// to the initial scroll position to avoid strange scrolling effects in
// release mode: In release mode, the viewport temporarily may have a
// dimension of zero before the actual dimension is calculated. In that
// scenario, setting the actual dimension would cause a strange scroll
// effect without this guard because the super call below would starts a
// ballistic scroll activity.
assert(viewportDimension != null);
_initialViewportDimensionWasZero = viewportDimension != 0.0;
if (!_viewportDimensionWasNonZero) {
_viewportDimensionWasNonZero = viewportDimension != 0.0;
}
// If the viewport never had a non-zero dimension, we just want to jump
// to the initial scroll position to avoid strange scrolling effects in
// release mode: In release mode, the viewport temporarily may have a
// dimension of zero before the actual dimension is calculated. In that
// scenario, setting the actual dimension would cause a strange scroll
// effect without this guard because the super call below would starts a
// ballistic scroll activity.
if (!_viewportDimensionWasNonZero || _needsPixelsCorrection) {
_needsPixelsCorrection = false;
correctPixels(tabBar._initialScrollOffset(viewportDimension, minScrollExtent, maxScrollExtent));
result = false;
}
return super.applyContentDimensions(minScrollExtent, maxScrollExtent) && result;
}

void markNeedsPixelsCorrection() {
_needsPixelsCorrection = true;
}
}

// This class, and TabBarScrollPosition, only exist to handle the case
Expand Down Expand Up @@ -1027,6 +1036,13 @@ class _TabBarState extends State<TabBar> {
if (widget.controller != oldWidget.controller) {
_updateTabController();
_initIndicatorPainter();
// Adjust scroll position.
if (_scrollController != null) {
final ScrollPosition position = _scrollController!.position;
if (position is _TabBarScrollPosition) {
position.markNeedsPixelsCorrection();
}
}
} else if (widget.indicatorColor != oldWidget.indicatorColor ||
widget.indicatorWeight != oldWidget.indicatorWeight ||
widget.indicatorSize != oldWidget.indicatorSize ||
Expand Down
43 changes: 43 additions & 0 deletions packages/flutter/test/material/tabs_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3420,6 +3420,49 @@ void main() {
));
});

testWidgets('TabController changes with different initialIndex', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/115917
const Key lastTabKey = Key('Last Tab');
TabController? controller;

Widget buildFrame(int length) {
controller = TabController(
vsync: const TestVSync(),
length: length,
initialIndex: length - 1,
);
return boilerplate(
child: TabBar(
labelPadding: EdgeInsets.zero,
controller: controller,
isScrollable: true,
tabs: List<Widget>.generate(
length,
(int index) {
return SizedBox(
width: 100,
child: Tab(
key: index == length - 1 ? lastTabKey : null,
text: 'Tab $index',
),
);
},
),
),
);
}

await tester.pumpWidget(buildFrame(10));
expect(controller!.index, 9);
expect(tester.getCenter(find.byKey(lastTabKey)).dx, equals(750.0));

// Rebuild with a new controller with more tabs and last tab selected.
// Last tab should be visible and on the right of the window.
await tester.pumpWidget(buildFrame(15));
expect(controller!.index, 14);
expect(tester.getCenter(find.byKey(lastTabKey)).dx, equals(750.0));
});

testWidgets('Default tab indicator color is white', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/15958
final List<String> tabs = <String>['LEFT', 'RIGHT'];
Expand Down

0 comments on commit d841d32

Please sign in to comment.