From 13e345fc886ae29a8fbb34d31010f17b415d1c15 Mon Sep 17 00:00:00 2001 From: Gil Nobrega <82336674+gilnobrega@users.noreply.github.com> Date: Fri, 24 Mar 2023 22:54:24 +0000 Subject: [PATCH 1/4] dont handle notification if controller invalid --- packages/flutter/lib/src/material/tabs.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 9686d1cda0ad..e2dd48a4aeb5 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -1893,6 +1893,10 @@ class _TabBarViewState extends State { return false; } + if (!_controllerIsValid) { + return false; + } + _scrollUnderwayCount += 1; if (notification is ScrollUpdateNotification && !_controller!.indexIsChanging) { final bool pageChanged = (_pageController.page! - _controller!.index).abs() > 1.0; From 93f816c5cf3015394c5c3a0964622999ad6b1e16 Mon Sep 17 00:00:00 2001 From: Gil Nobrega <82336674+gilnobrega@users.noreply.github.com> Date: Sat, 25 Mar 2023 02:08:18 +0000 Subject: [PATCH 2/4] test: replace tabcontroller while flinging --- packages/flutter/test/material/tabs_test.dart | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index 5072a74557b6..e7a9111f27cf 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -3818,6 +3818,68 @@ void main() { )); }); + testWidgets('TabController changes while flinging', (WidgetTester tester) async { + // This is a regression test for https://github.com/flutter/flutter/issues/34744 + + Widget buildFrame(TabController controller) { + + return MaterialApp( + theme: ThemeData(platform: TargetPlatform.iOS), + home: Scaffold( + appBar: AppBar( + title: const Text('tabs'), + bottom: TabBar( + controller: controller, + tabs: [ + Tab(text: 'A'), + Tab(text: 'B'), + if (controller.length == 3) + Tab(text: 'C'), + ], + ), + ), + body: TabBarView( + controller: controller, + children: [ + Center(child: Text('CHILD A')), + Center(child: Text('CHILD B')), + if (controller.length == 3) + Center(child: Text('CHILD C')), + ], + ), + ), + ); + } + + final TabController controller1 = TabController( + vsync: const TestVSync(), + length: 2, + ); + + final TabController controller2 = TabController( + vsync: const TestVSync(), + length: 3, + ); + + expect(controller1.index, 0); + expect(controller2.index, 0); + + await tester.pumpWidget(buildFrame(controller1)); + final flingStart = tester.getCenter(find.text('CHILD A')); + await tester.flingFrom(flingStart, const Offset(-200.0, 0.0), 10000.0); + await tester.pump(const Duration(milliseconds: 10)); // start the fling animation + + controller1.dispose(); + await tester.pump(const Duration(milliseconds: 10)); + + await tester.pumpWidget(buildFrame(controller2)); // replace controller + await tester.flingFrom(flingStart, const Offset(-200.0, 0.0), 10000.0); + await tester.pumpAndSettle(); // finish the fling animation + + expect(controller1.index, 0); + expect(controller2.index, 1); + }); + 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'); From fbf5a907108daf0a1d05c967357044223734a849 Mon Sep 17 00:00:00 2001 From: Gil Nobrega <82336674+gilnobrega@users.noreply.github.com> Date: Fri, 5 May 2023 16:43:52 +0100 Subject: [PATCH 3/4] remove unnecessary space --- packages/flutter/test/material/tabs_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index e7a9111f27cf..5d7dda2e135d 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -3870,7 +3870,7 @@ void main() { await tester.pump(const Duration(milliseconds: 10)); // start the fling animation controller1.dispose(); - await tester.pump(const Duration(milliseconds: 10)); + await tester.pump(const Duration(milliseconds: 10)); await tester.pumpWidget(buildFrame(controller2)); // replace controller await tester.flingFrom(flingStart, const Offset(-200.0, 0.0), 10000.0); From 52ffda6b3d5057cac6ecc45ae68dfca3fc921ef6 Mon Sep 17 00:00:00 2001 From: Gil Nobrega <82336674+gilnobrega@users.noreply.github.com> Date: Fri, 5 May 2023 17:18:07 +0100 Subject: [PATCH 4/4] fix analyzer issues --- packages/flutter/test/material/tabs_test.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index 5d7dda2e135d..7a9e8514d27c 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -3831,20 +3831,20 @@ void main() { bottom: TabBar( controller: controller, tabs: [ - Tab(text: 'A'), - Tab(text: 'B'), + const Tab(text: 'A'), + const Tab(text: 'B'), if (controller.length == 3) - Tab(text: 'C'), + const Tab(text: 'C'), ], ), ), body: TabBarView( controller: controller, children: [ - Center(child: Text('CHILD A')), - Center(child: Text('CHILD B')), + const Center(child: Text('CHILD A')), + const Center(child: Text('CHILD B')), if (controller.length == 3) - Center(child: Text('CHILD C')), + const Center(child: Text('CHILD C')), ], ), ), @@ -3865,7 +3865,7 @@ void main() { expect(controller2.index, 0); await tester.pumpWidget(buildFrame(controller1)); - final flingStart = tester.getCenter(find.text('CHILD A')); + final Offset flingStart = tester.getCenter(find.text('CHILD A')); await tester.flingFrom(flingStart, const Offset(-200.0, 0.0), 10000.0); await tester.pump(const Duration(milliseconds: 10)); // start the fling animation