Skip to content

Commit 1d7c680

Browse files
authored
Re-land fix for not disposed TabController (#146745)
Fixes flutter/flutter#144910
1 parent d261a90 commit 1d7c680

File tree

3 files changed

+13
-9
lines changed

3 files changed

+13
-9
lines changed

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,15 @@ class TabController extends ChangeNotifier {
140140

141141

142142
/// Creates a new [TabController] with `index`, `previousIndex`, `length`, and
143-
/// `animationDuration` if they are non-null.
143+
/// `animationDuration` if they are non-null, and disposes current instance.
144144
///
145145
/// This method is used by [DefaultTabController].
146146
///
147147
/// When [DefaultTabController.length] is updated, this method is called to
148148
/// create a new [TabController] without creating a new [AnimationController].
149-
TabController _copyWith({
149+
/// Instead the [_animationController] is nulled in current instance and
150+
/// passed to the new instance.
151+
TabController _copyWithAndDispose({
150152
required int? index,
151153
required int? length,
152154
required int? previousIndex,
@@ -155,13 +157,16 @@ class TabController extends ChangeNotifier {
155157
if (index != null) {
156158
_animationController!.value = index.toDouble();
157159
}
158-
return TabController._(
160+
final TabController result = TabController._(
159161
index: index ?? _index,
160162
length: length ?? this.length,
161163
animationController: _animationController,
162164
previousIndex: previousIndex ?? _previousIndex,
163165
animationDuration: animationDuration ?? _animationDuration,
164166
);
167+
_animationController = null;
168+
dispose();
169+
return result;
165170
}
166171

167172
/// An animation whose value represents the current position of the [TabBar]'s
@@ -489,7 +494,7 @@ class _DefaultTabControllerState extends State<DefaultTabController> with Single
489494
newIndex = math.max(0, widget.length - 1);
490495
previousIndex = _controller.index;
491496
}
492-
_controller = _controller._copyWith(
497+
_controller = _controller._copyWithAndDispose(
493498
length: widget.length,
494499
animationDuration: widget.animationDuration,
495500
index: newIndex,
@@ -498,7 +503,7 @@ class _DefaultTabControllerState extends State<DefaultTabController> with Single
498503
}
499504

500505
if (oldWidget.animationDuration != widget.animationDuration) {
501-
_controller = _controller._copyWith(
506+
_controller = _controller._copyWithAndDispose(
502507
length: widget.length,
503508
animationDuration: widget.animationDuration,
504509
index: _controller.index,

packages/flutter/lib/src/widgets/transitions.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class _AnimatedState extends State<AnimatedWidget> {
126126
}
127127

128128
void _handleChange() {
129+
if (!mounted) {
130+
return;
131+
}
129132
setState(() {
130133
// The listenable's state is our build state, and it changed already.
131134
});

packages/flutter/test/material/tabs_test.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
77
import 'package:flutter/rendering.dart';
88
import 'package:flutter/services.dart';
99
import 'package:flutter_test/flutter_test.dart';
10-
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
1110

1211
import '../widgets/semantics_tester.dart';
1312
import 'feedback_tester.dart';
@@ -121,9 +120,6 @@ Widget buildLeftRightApp({required List<String> tabs, required String value, boo
121120
}
122121

123122
void main() {
124-
// TODO(polina-c): dispose TabController, https://github.com/flutter/flutter/issues/144910 [leaks-to-clean]
125-
LeakTesting.settings = LeakTesting.settings.withIgnoredAll();
126-
127123
setUp(() {
128124
debugResetSemanticsIdCounter();
129125
});

0 commit comments

Comments
 (0)