Skip to content

Commit 237ddcc

Browse files
authored
Remove non needed controllers in SegmentedButton. (#134064)
1 parent 161c709 commit 237ddcc

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,32 @@ class SegmentedButton<T> extends StatefulWidget {
236236
final Widget? selectedIcon;
237237

238238
@override
239-
State<SegmentedButton<T>> createState() => _SegmentedButtonState<T>();
239+
State<SegmentedButton<T>> createState() => SegmentedButtonState<T>();
240240
}
241241

242-
class _SegmentedButtonState<T> extends State<SegmentedButton<T>> {
242+
/// State for [SegmentedButton].
243+
@visibleForTesting
244+
class SegmentedButtonState<T> extends State<SegmentedButton<T>> {
243245
bool get _enabled => widget.onSelectionChanged != null;
244-
final Map<ButtonSegment<T>, MaterialStatesController> _statesControllers = <ButtonSegment<T>, MaterialStatesController>{};
246+
247+
/// Controllers for the [ButtonSegment]s.
248+
@visibleForTesting
249+
final Map<ButtonSegment<T>, MaterialStatesController> statesControllers = <ButtonSegment<T>, MaterialStatesController>{};
250+
251+
@override
252+
void didUpdateWidget(covariant SegmentedButton<T> oldWidget) {
253+
super.didUpdateWidget(oldWidget);
254+
if (oldWidget != widget) {
255+
statesControllers.removeWhere((ButtonSegment<T> segment, MaterialStatesController controller) {
256+
if (widget.segments.contains(segment)) {
257+
return false;
258+
} else {
259+
controller.dispose();
260+
return true;
261+
}
262+
});
263+
}
264+
}
245265

246266
void _handleOnPressed(T segmentValue) {
247267
if (!_enabled) {
@@ -325,7 +345,7 @@ class _SegmentedButtonState<T> extends State<SegmentedButton<T>> {
325345
: segment.label != null
326346
? segment.icon
327347
: null;
328-
final MaterialStatesController controller = _statesControllers.putIfAbsent(segment, () => MaterialStatesController());
348+
final MaterialStatesController controller = statesControllers.putIfAbsent(segment, () => MaterialStatesController());
329349
controller.value = <MaterialState>{
330350
if (segmentSelected) MaterialState.selected,
331351
};
@@ -391,7 +411,7 @@ class _SegmentedButtonState<T> extends State<SegmentedButton<T>> {
391411

392412
@override
393413
void dispose() {
394-
for (final MaterialStatesController controller in _statesControllers.values) {
414+
for (final MaterialStatesController controller in statesControllers.values) {
395415
controller.dispose();
396416
}
397417
super.dispose();

packages/flutter/test/material/segmented_button_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,52 @@ Widget boilerplate({required Widget child}) {
2121
}
2222

2323
void main() {
24+
testWidgetsWithLeakTracking('SegmentedButton releases state controllers for deleted segments', (WidgetTester tester) async {
25+
final ThemeData theme = ThemeData(useMaterial3: true);
26+
final Key key = UniqueKey();
27+
28+
Widget buildApp(Widget button) {
29+
return MaterialApp(
30+
theme: theme,
31+
home: Scaffold(
32+
body: Center(
33+
child: button,
34+
),
35+
),
36+
);
37+
}
38+
39+
await tester.pumpWidget(
40+
buildApp(
41+
SegmentedButton<int>(
42+
key: key,
43+
segments: const <ButtonSegment<int>>[
44+
ButtonSegment<int>(value: 1, label: Text('1')),
45+
ButtonSegment<int>(value: 2, label: Text('2')),
46+
],
47+
selected: const <int>{2},
48+
),
49+
),
50+
);
51+
52+
await tester.pumpWidget(
53+
buildApp(
54+
SegmentedButton<int>(
55+
key: key,
56+
segments: const <ButtonSegment<int>>[
57+
ButtonSegment<int>(value: 2, label: Text('2')),
58+
ButtonSegment<int>(value: 3, label: Text('3')),
59+
],
60+
selected: const <int>{2},
61+
),
62+
),
63+
);
64+
65+
final SegmentedButtonState<int> state = tester.state(find.byType(SegmentedButton<int>));
66+
expect(state.statesControllers, hasLength(2));
67+
expect(state.statesControllers.keys.first.value, 2);
68+
expect(state.statesControllers.keys.last.value, 3);
69+
});
2470

2571
testWidgetsWithLeakTracking('SegmentedButton is built with Material of type MaterialType.transparency', (WidgetTester tester) async {
2672
final ThemeData theme = ThemeData(useMaterial3: true);

0 commit comments

Comments
 (0)