diff --git a/packages/flutter/lib/src/material/tab_controller.dart b/packages/flutter/lib/src/material/tab_controller.dart index 515ab907b24..f9dd965bcb9 100644 --- a/packages/flutter/lib/src/material/tab_controller.dart +++ b/packages/flutter/lib/src/material/tab_controller.dart @@ -140,13 +140,15 @@ class TabController extends ChangeNotifier { /// Creates a new [TabController] with `index`, `previousIndex`, `length`, and - /// `animationDuration` if they are non-null. + /// `animationDuration` if they are non-null, and disposes current instance. /// /// This method is used by [DefaultTabController]. /// /// When [DefaultTabController.length] is updated, this method is called to /// create a new [TabController] without creating a new [AnimationController]. - TabController _copyWith({ + /// Instead the [_animationController] is nulled in current instance and + /// passed to the new instance. + TabController _copyWithAndDispose({ required int? index, required int? length, required int? previousIndex, @@ -155,13 +157,16 @@ class TabController extends ChangeNotifier { if (index != null) { _animationController!.value = index.toDouble(); } - return TabController._( + final TabController result = TabController._( index: index ?? _index, length: length ?? this.length, animationController: _animationController, previousIndex: previousIndex ?? _previousIndex, animationDuration: animationDuration ?? _animationDuration, ); + _animationController = null; + dispose(); + return result; } /// An animation whose value represents the current position of the [TabBar]'s @@ -489,7 +494,7 @@ class _DefaultTabControllerState extends State with Single newIndex = math.max(0, widget.length - 1); previousIndex = _controller.index; } - _controller = _controller._copyWith( + _controller = _controller._copyWithAndDispose( length: widget.length, animationDuration: widget.animationDuration, index: newIndex, @@ -498,7 +503,7 @@ class _DefaultTabControllerState extends State with Single } if (oldWidget.animationDuration != widget.animationDuration) { - _controller = _controller._copyWith( + _controller = _controller._copyWithAndDispose( length: widget.length, animationDuration: widget.animationDuration, index: _controller.index, diff --git a/packages/flutter/lib/src/widgets/transitions.dart b/packages/flutter/lib/src/widgets/transitions.dart index 16bd0f0c58d..17c64f93fd9 100644 --- a/packages/flutter/lib/src/widgets/transitions.dart +++ b/packages/flutter/lib/src/widgets/transitions.dart @@ -126,6 +126,9 @@ class _AnimatedState extends State { } void _handleChange() { + if (!mounted) { + return; + } setState(() { // The listenable's state is our build state, and it changed already. }); diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index a4ba9d7777b..89f5a4f84a5 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../widgets/semantics_tester.dart'; import 'feedback_tester.dart'; @@ -121,9 +120,6 @@ Widget buildLeftRightApp({required List tabs, required String value, boo } void main() { - // TODO(polina-c): dispose TabController, https://github.com/flutter/flutter/issues/144910 [leaks-to-clean] - LeakTesting.settings = LeakTesting.settings.withIgnoredAll(); - setUp(() { debugResetSemanticsIdCounter(); });