diff --git a/packages/flutter/lib/src/material/tab_controller.dart b/packages/flutter/lib/src/material/tab_controller.dart index dc2c272461..dbf04f5409 100644 --- a/packages/flutter/lib/src/material/tab_controller.dart +++ b/packages/flutter/lib/src/material/tab_controller.dart @@ -180,6 +180,9 @@ class TabController extends ChangeNotifier { required int? length, required int? previousIndex, }) { + if (index != null) { + _animationController!.value = index.toDouble(); + } return TabController._( index: index ?? _index, length: length ?? this.length, diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index 8e524dbc8b..e2ffb3307a 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -3401,6 +3401,62 @@ void main() { // No other tab got instantiated during the animation. expect(log, ['init: 0', 'init: 3', 'dispose: 0']); }); + + testWidgets('TabController\'s animation value should be updated when TabController\'s index >= tabs\'s length', (WidgetTester tester) async { + // This is a regression test for the issue brought up here + // https://github.com/flutter/flutter/issues/79226 + + final List tabs = ['A', 'B', 'C']; + await tester.pumpWidget( + MaterialApp( + home: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return DefaultTabController( + length: tabs.length, + child: Scaffold( + appBar: AppBar( + bottom: TabBar( + tabs: tabs.map((String tab) => Tab(text: tab)).toList(), + ), + actions: [ + TextButton( + child: const Text('Remove Last Tab'), + onPressed: () { + setState(() { + tabs.removeLast(); + }); + }, + ), + ], + ), + body: TabBarView( + children: tabs.map((String tab) => Tab(text: 'Tab child $tab')).toList(), + ), + ) + ); + }, + ), + ), + ); + + TabController getController() => DefaultTabController.of(tester.element(find.text('B')))!; + TabController controller = getController(); + + controller.animateTo(2, duration: const Duration(milliseconds: 200), curve: Curves.linear); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 300)); + + controller = getController(); + expect(controller.index, 2); + expect(controller.animation!.value, 2); + + await tester.tap(find.text('Remove Last Tab')); + await tester.pumpAndSettle(); + + controller = getController(); + expect(controller.index, 1); + expect(controller.animation!.value, 1); + }); } class KeepAliveInk extends StatefulWidget {