diff --git a/packages/two_dimensional_scrollables/CHANGELOG.md b/packages/two_dimensional_scrollables/CHANGELOG.md index cb4b895d584..1748b3fda73 100644 --- a/packages/two_dimensional_scrollables/CHANGELOG.md +++ b/packages/two_dimensional_scrollables/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.3.4 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Fixes a bug where collapsing a node in a TreeView with other offscreen nodes would dereference a null value. ## 0.3.3 diff --git a/packages/two_dimensional_scrollables/lib/src/tree_view/render_tree.dart b/packages/two_dimensional_scrollables/lib/src/tree_view/render_tree.dart index ab00f7cf7fa..49628937381 100644 --- a/packages/two_dimensional_scrollables/lib/src/tree_view/render_tree.dart +++ b/packages/two_dimensional_scrollables/lib/src/tree_view/render_tree.dart @@ -318,9 +318,12 @@ class RenderTreeViewport extends RenderTwoDimensionalViewport { ); _horizontalOverflows = maxHorizontalExtent > 0.0; + final double verticalLeadingExtent = verticalOffset.pixels; + final double verticalTrailingExtent = + _rowMetrics[_lastRow!]!.trailingOffset - viewportDimension.height; final double maxVerticalExtent = math.max( 0.0, - _rowMetrics[_lastRow!]!.trailingOffset - viewportDimension.height, + math.max(verticalLeadingExtent, verticalTrailingExtent), ); _verticalOverflows = maxVerticalExtent > 0.0; diff --git a/packages/two_dimensional_scrollables/pubspec.yaml b/packages/two_dimensional_scrollables/pubspec.yaml index 9cdd89917b3..2ca83524c45 100644 --- a/packages/two_dimensional_scrollables/pubspec.yaml +++ b/packages/two_dimensional_scrollables/pubspec.yaml @@ -1,6 +1,6 @@ name: two_dimensional_scrollables description: Widgets that scroll using the two dimensional scrolling foundation. -version: 0.3.3 +version: 0.3.4 repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+ diff --git a/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart b/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart index 5db48784272..5d7b47e12a0 100644 --- a/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart +++ b/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart @@ -886,6 +886,96 @@ void main() { expect(find.text('tres'), findsNothing); }); }); + + testWidgets('Expand then collapse with offscreen nodes (top)', + (WidgetTester tester) async { + final ScrollController verticalController = ScrollController(); + final TreeViewController controller = TreeViewController(); + addTearDown(verticalController.dispose); + + final List> tree = >[ + TreeViewNode( + 'alpha', + children: >[ + TreeViewNode('a'), + TreeViewNode('b'), + TreeViewNode('c'), + ], + ), + TreeViewNode( + 'beta', + children: >[ + TreeViewNode('d'), + TreeViewNode('e'), + TreeViewNode('f'), + ], + ), + TreeViewNode( + 'gamma', + children: >[ + TreeViewNode('g'), + TreeViewNode('h'), + TreeViewNode('i'), + ], + ), + TreeViewNode( + 'delta', + children: >[ + TreeViewNode('j'), + TreeViewNode('k'), + TreeViewNode('l'), + ], + ), + ]; + + await tester.pumpWidget(MaterialApp( + home: TreeView( + tree: tree, + controller: controller, + toggleAnimationStyle: AnimationStyle.noAnimation, + verticalDetails: ScrollableDetails.vertical( + controller: verticalController, + ), + treeNodeBuilder: ( + BuildContext context, + TreeViewNode node, + AnimationStyle animationStyle, + ) => + GestureDetector( + onTap: () => controller.toggleNode(node), + child: TreeView.defaultTreeNodeBuilder( + context, + node, + animationStyle, + ), + ), + ), + )); + + // Expand a few nodes + await tester.tap(find.text('alpha')); + await tester.tap(find.text('beta')); + await tester.tap(find.text('gamma')); + await tester.tap(find.text('delta')); + await tester.pumpAndSettle(); + + // Scroll down to hide some of them + expect(verticalController.position.pixels, 0.0); + const double defaultRowExtent = 40; + verticalController.jumpTo(3 * defaultRowExtent + 10); + await tester.pump(); + expect(find.text('alpha'), findsNothing); + + // Collapse the bottommost node + expect(find.text('j'), findsOneWidget); + expect(find.text('k'), findsOneWidget); + expect(find.text('l'), findsOneWidget); + await tester.tap(find.text('delta')); + await tester.pumpAndSettle(); + expect(find.text('j'), findsNothing); + expect(find.text('k'), findsNothing); + expect(find.text('l'), findsNothing); + }); }); group('TreeViewport', () {