Skip to content

Commit 082ad11

Browse files
authored
Fix TabController throws build scheduled during frame error (#105442)
1 parent 40fc5e8 commit 082ad11

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

packages/flutter/lib/src/material/tabs.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,15 +1495,13 @@ class _TabBarViewState extends State<TabBarView> {
14951495
}
14961496

14971497
final Duration duration = _controller!.animationDuration;
1498-
1499-
if (duration == Duration.zero) {
1500-
_pageController.jumpToPage(_currentIndex!);
1501-
return Future<void>.value();
1502-
}
1503-
15041498
final int previousIndex = _controller!.previousIndex;
15051499

15061500
if ((_currentIndex! - previousIndex).abs() == 1) {
1501+
if (duration == Duration.zero) {
1502+
_pageController.jumpToPage(_currentIndex!);
1503+
return Future<void>.value();
1504+
}
15071505
_warpUnderwayCount += 1;
15081506
await _pageController.animateToPage(_currentIndex!, duration: duration, curve: Curves.ease);
15091507
_warpUnderwayCount -= 1;
@@ -1525,6 +1523,11 @@ class _TabBarViewState extends State<TabBarView> {
15251523
});
15261524
_pageController.jumpToPage(initialPage);
15271525

1526+
if (duration == Duration.zero) {
1527+
_pageController.jumpToPage(_currentIndex!);
1528+
return Future<void>.value();
1529+
}
1530+
15281531
await _pageController.animateToPage(_currentIndex!, duration: duration, curve: Curves.ease);
15291532
if (!mounted) {
15301533
return Future<void>.value();

packages/flutter/test/material/tabs_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,51 @@ void main() {
11911191
expect(controller.indexIsChanging, false);
11921192
});
11931193

1194+
testWidgets('TabBar should not throw when animation is disabled in controller', (WidgetTester tester) async {
1195+
// Regression test for https://github.com/flutter/flutter/issues/102600
1196+
final List<String> tabs = <String>['A'];
1197+
1198+
Widget buildWithTabBarView() {
1199+
return boilerplate(
1200+
child: DefaultTabController(
1201+
animationDuration: Duration.zero,
1202+
length: tabs.length,
1203+
child: Column(
1204+
children: <Widget>[
1205+
TabBar(
1206+
tabs: tabs.map<Widget>((String tab) => Tab(text: tab)).toList(),
1207+
isScrollable: true,
1208+
),
1209+
Flexible(
1210+
child: TabBarView(
1211+
children: List<Widget>.generate(
1212+
tabs.length,
1213+
(int index) => Text('Tab $index'),
1214+
),
1215+
),
1216+
),
1217+
],
1218+
),
1219+
),
1220+
);
1221+
}
1222+
1223+
await tester.pumpWidget(buildWithTabBarView());
1224+
TabController controller = DefaultTabController.of(tester.element(find.text('A')))!;
1225+
expect(controller.index, 0);
1226+
1227+
tabs.add('B');
1228+
await tester.pumpWidget(buildWithTabBarView());
1229+
tabs.add('C');
1230+
await tester.pumpWidget(buildWithTabBarView());
1231+
await tester.tap(find.text('C'));
1232+
await tester.pumpAndSettle();
1233+
controller = DefaultTabController.of(tester.element(find.text('A')))!;
1234+
expect(controller.index, 2);
1235+
1236+
expect(tester.takeException(), isNull);
1237+
});
1238+
11941239
testWidgets('TabBarView skips animation when disabled in controller', (WidgetTester tester) async {
11951240
final List<String> tabs = <String>['A', 'B', 'C'];
11961241
final TabController tabController = TabController(

0 commit comments

Comments
 (0)