Unverified Commit f314f1bc authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Update TabBarView children after a transition to an adjacent tab (#112168)

parent b31f41bd
......@@ -1512,6 +1512,10 @@ class _TabBarViewState extends State<TabBarView> {
_warpUnderwayCount += 1;
await _pageController.animateToPage(_currentIndex!, duration: duration, curve: Curves.ease);
_warpUnderwayCount -= 1;
if (mounted && widget.children != _children) {
setState(() { _updateChildren(); });
}
return Future<void>.value();
}
......
......@@ -859,20 +859,6 @@ void main() {
expect(tabController.indexIsChanging, false);
});
testWidgets('TabBarView child disposed during animation', (WidgetTester tester) async {
// This is a regression test for the scenario brought up here
// https://github.com/flutter/flutter/pull/7387#discussion_r95089191x
final List<String> tabs = <String>['LEFT', 'RIGHT'];
await tester.pumpWidget(buildLeftRightApp(tabs: tabs, value: 'LEFT'));
// Fling to the left, switch from the 'LEFT' tab to the 'RIGHT'
final Offset flingStart = tester.getCenter(find.text('LEFT CHILD'));
await tester.flingFrom(flingStart, const Offset(-200.0, 0.0), 10000.0);
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
});
testWidgets('TabBar unselectedLabelColor control test', (WidgetTester tester) async {
final TabController controller = TabController(
vsync: const TestVSync(),
......@@ -1563,6 +1549,95 @@ void main() {
await tester.pump(const Duration(milliseconds: 300));
});
group('TabBarView children updated', () {
Widget buildFrameWithMarker(List<String> log, String marker) {
return MaterialApp(
home: DefaultTabController(
animationDuration: const Duration(seconds: 1),
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: <Widget>[
Tab(text: 'A'),
Tab(text: 'B'),
Tab(text: 'C'),
],
),
title: const Text('Tabs Test'),
),
body: TabBarView(
children: <Widget>[
TabBody(index: 0, log: log, marker: marker),
TabBody(index: 1, log: log, marker: marker),
TabBody(index: 2, log: log, marker: marker),
],
),
),
),
);
}
testWidgets('TabBarView children can be updated during animation to an adjacent tab', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/107399
final List<String> log = <String>[];
const String initialMarker = 'before';
await tester.pumpWidget(buildFrameWithMarker(log, initialMarker));
expect(log, <String>['init: 0']);
expect(find.text('0-$initialMarker'), findsOneWidget);
// Select the second tab and wait until the transition starts
await tester.tap(find.text('B'));
await tester.pump(const Duration(milliseconds: 100));
// Check that both TabBody's are instantiated while the transition is animating
await tester.pump(const Duration(milliseconds: 400));
expect(log, <String>['init: 0', 'init: 1']);
// Update the TabBody's states while the transition is animating
const String updatedMarker = 'after';
await tester.pumpWidget(buildFrameWithMarker(log, updatedMarker));
// Wait until the transition ends
await tester.pumpAndSettle();
// The TabBody state of the second TabBar should have been updated
expect(find.text('1-$initialMarker'), findsNothing);
expect(find.text('1-$updatedMarker'), findsOneWidget);
});
testWidgets('TabBarView children can be updated during animation to a non adjacent tab', (WidgetTester tester) async {
final List<String> log = <String>[];
const String initialMarker = 'before';
await tester.pumpWidget(buildFrameWithMarker(log, initialMarker));
expect(log, <String>['init: 0']);
expect(find.text('0-$initialMarker'), findsOneWidget);
// Select the third tab and wait until the transition starts
await tester.tap(find.text('C'));
await tester.pump(const Duration(milliseconds: 100));
// Check that both TabBody's are instantiated while the transition is animating
await tester.pump(const Duration(milliseconds: 400));
expect(log, <String>['init: 0', 'init: 2']);
// Update the TabBody's states while the transition is animating
const String updatedMarker = 'after';
await tester.pumpWidget(buildFrameWithMarker(log, updatedMarker));
// Wait until the transition ends
await tester.pumpAndSettle();
// The TabBody state of the third TabBar should have been updated
expect(find.text('2-$initialMarker'), findsNothing);
expect(find.text('2-$updatedMarker'), findsOneWidget);
});
});
testWidgets('TabBarView scrolls end close to a new page', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/9375
......@@ -4976,10 +5051,11 @@ class TabBarDemo extends StatelessWidget {
class MockScrollMetrics extends Fake implements ScrollMetrics { }
class TabBody extends StatefulWidget {
const TabBody({ super.key, required this.index, required this.log });
const TabBody({ super.key, required this.index, required this.log, this.marker = '' });
final int index;
final List<String> log;
final String marker;
@override
State<TabBody> createState() => TabBodyState();
......@@ -5008,7 +5084,9 @@ class TabBodyState extends State<TabBody> {
@override
Widget build(BuildContext context) {
return Center(
child: Text('${widget.index}'),
child: widget.marker.isEmpty
? Text('${widget.index}')
: Text('${widget.index}-${widget.marker}'),
);
}
}
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