Skip to content

Commit 1c0cf2f

Browse files
committed
Merge branch 'master' of github.com:flutter/devtools into tooltipfollowup
2 parents 51fa4a6 + be950ac commit 1c0cf2f

5 files changed

Lines changed: 83 additions & 3 deletions

File tree

packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ To learn more about DevTools, check out the
1515

1616
## General updates
1717

18-
TODO: Remove this section if there are not any updates.
18+
* Fixed a `RangeError` thrown by `SplitPane` when the parent rebuilt the
19+
widget with a different number of children, for example when toggling a
20+
panel in or out of the layout. -
21+
[#9822](https://github.com/flutter/devtools/pull/9822)
1922

2023
## Inspector updates
2124

packages/devtools_app_shared/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ Copyright 2025 The Flutter Authors
33
Use of this source code is governed by a BSD-style license that can be
44
found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
55
-->
6+
## 0.5.2-wip
7+
* Fix a `RangeError` thrown by `SplitPane` when the number of children
8+
changes between rebuilds.
9+
610
## 0.5.1
711
* Add DevTools-styled text field `DevToolsTextField`.
812
* Updates `devtools_shared` constraint to `^13.0.0`.

packages/devtools_app_shared/lib/src/ui/split_pane.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ final class SplitPane extends StatefulWidget {
8888
}
8989

9090
final class _SplitPaneState extends State<SplitPane> {
91-
late final List<double> fractions;
91+
late List<double> fractions;
9292

9393
bool get isHorizontal => widget.axis == Axis.horizontal;
9494

@@ -98,6 +98,18 @@ final class _SplitPaneState extends State<SplitPane> {
9898
fractions = List.of(widget.initialFractions);
9999
}
100100

101+
@override
102+
void didUpdateWidget(SplitPane oldWidget) {
103+
super.didUpdateWidget(oldWidget);
104+
// When the number of children changes, the previously stored [fractions]
105+
// list will be out of sync with [widget.minSizes] and [widget.children],
106+
// which causes a RangeError during layout. Reset to the new
107+
// [initialFractions] when the child count changes.
108+
if (oldWidget.children.length != widget.children.length) {
109+
fractions = List.of(widget.initialFractions);
110+
}
111+
}
112+
101113
@override
102114
Widget build(BuildContext context) {
103115
return LayoutBuilder(builder: _buildLayout);

packages/devtools_app_shared/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
44
name: devtools_app_shared
55
description: Package of Dart & Flutter structures shared between devtools_app and devtools extensions.
6-
version: 0.5.1
6+
version: 0.5.2-wip
77
repository: https://github.com/flutter/devtools/tree/master/packages/devtools_app_shared
88

99
environment:

packages/devtools_app_shared/test/ui/split_pane_test.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,67 @@ void main() {
11541154
);
11551155
});
11561156

1157+
group('rebuilds with a different number of children', () {
1158+
testWidgets(
1159+
'does not throw a RangeError when child count shrinks',
1160+
(WidgetTester tester) async {
1161+
final threeChildSplit = buildSplitPane(
1162+
Axis.horizontal,
1163+
children: const [_w1, _w2, _w3],
1164+
initialFractions: const [0.2, 0.4, 0.4],
1165+
minSizes: const [50.0, 50.0, 50.0],
1166+
);
1167+
await tester.pumpWidget(wrap(threeChildSplit));
1168+
expect(find.byKey(_k1), findsOneWidget);
1169+
expect(find.byKey(_k2), findsOneWidget);
1170+
expect(find.byKey(_k3), findsOneWidget);
1171+
1172+
final twoChildSplit = buildSplitPane(
1173+
Axis.horizontal,
1174+
children: const [_w1, _w2],
1175+
initialFractions: const [0.5, 0.5],
1176+
minSizes: const [50.0, 50.0],
1177+
);
1178+
await tester.pumpWidget(wrap(twoChildSplit));
1179+
await tester.pumpAndSettle();
1180+
1181+
expect(tester.takeException(), isNull);
1182+
expect(find.byKey(_k1), findsOneWidget);
1183+
expect(find.byKey(_k2), findsOneWidget);
1184+
expect(find.byKey(_k3), findsNothing);
1185+
},
1186+
);
1187+
1188+
testWidgets(
1189+
'does not throw a RangeError when child count grows',
1190+
(WidgetTester tester) async {
1191+
final twoChildSplit = buildSplitPane(
1192+
Axis.horizontal,
1193+
children: const [_w1, _w2],
1194+
initialFractions: const [0.5, 0.5],
1195+
minSizes: const [50.0, 50.0],
1196+
);
1197+
await tester.pumpWidget(wrap(twoChildSplit));
1198+
expect(find.byKey(_k1), findsOneWidget);
1199+
expect(find.byKey(_k2), findsOneWidget);
1200+
1201+
final threeChildSplit = buildSplitPane(
1202+
Axis.horizontal,
1203+
children: const [_w1, _w2, _w3],
1204+
initialFractions: const [0.2, 0.4, 0.4],
1205+
minSizes: const [50.0, 50.0, 50.0],
1206+
);
1207+
await tester.pumpWidget(wrap(threeChildSplit));
1208+
await tester.pumpAndSettle();
1209+
1210+
expect(tester.takeException(), isNull);
1211+
expect(find.byKey(_k1), findsOneWidget);
1212+
expect(find.byKey(_k2), findsOneWidget);
1213+
expect(find.byKey(_k3), findsOneWidget);
1214+
},
1215+
);
1216+
});
1217+
11571218
group('axisFor', () {
11581219
testWidgetsWithWindowSize(
11591220
'return Axis.horizontal',

0 commit comments

Comments
 (0)