@@ -682,7 +682,7 @@ void main() {
682682 await tester.pumpWidget (MaterialApp (home: treeView));
683683 await tester.pump ();
684684 expect (verticalController.position.pixels, 0.0 );
685- expect (verticalController.position.maxScrollExtent, 600 .0 );
685+ expect (verticalController.position.maxScrollExtent, 2200 .0 );
686686
687687 bool rowNeedsPaint (String row) {
688688 return find.text (row).evaluate ().first.renderObject! .debugNeedsPaint;
@@ -866,6 +866,121 @@ void main() {
866866 );
867867 });
868868 });
869+
870+ group ('Scroll bounds' , () {
871+ testWidgets (
872+ 'shrinking to 0 rows updates scroll bounds and does not crash' ,
873+ (WidgetTester tester) async {
874+ var rows = 10 ;
875+ late StateSetter setState;
876+ final controller = ScrollController ();
877+ await tester.pumpWidget (
878+ MaterialApp (
879+ home: Scaffold (
880+ body: SizedBox (
881+ height: 400 ,
882+ width: 400 ,
883+ child: StatefulBuilder (
884+ builder: (BuildContext context, StateSetter setter) {
885+ setState = setter;
886+ return TreeView <String >(
887+ verticalDetails: ScrollableDetails .vertical (
888+ controller: controller,
889+ ),
890+ tree: List <TreeViewNode <String >>.generate (
891+ rows,
892+ (int index) => TreeViewNode <String >('Row $index ' ),
893+ ),
894+ treeRowBuilder: (TreeViewNode <String > node) =>
895+ const TreeRow (extent: FixedTreeRowExtent (64.0 )),
896+ treeNodeBuilder:
897+ (
898+ BuildContext context,
899+ TreeViewNode <String > node,
900+ AnimationStyle toggleAnimationStyle,
901+ ) => Text (node.content),
902+ );
903+ },
904+ ),
905+ ),
906+ ),
907+ ),
908+ );
909+
910+ await tester.pump ();
911+ final double oldMax = controller.position.maxScrollExtent;
912+ expect (oldMax, greaterThan (0 ));
913+ controller.jumpTo (oldMax);
914+ await tester.pump ();
915+ expect (controller.offset, oldMax);
916+
917+ // Shrink to 0 rows.
918+ setState (() {
919+ rows = 0 ;
920+ });
921+ // This should not crash and should update scroll bounds.
922+ await tester.pump ();
923+
924+ expect (controller.position.maxScrollExtent, 0.0 );
925+ expect (controller.offset, 0.0 );
926+ },
927+ );
928+
929+ testWidgets (
930+ 'collapsing last node updates scroll bounds and does not crash' ,
931+ (WidgetTester tester) async {
932+ final treeController = TreeViewController ();
933+ final scrollController = ScrollController ();
934+
935+ final treeNodes = < TreeViewNode <String >> [
936+ TreeViewNode <String >(
937+ 'Root' ,
938+ expanded: true ,
939+ children: < TreeViewNode <String >> [TreeViewNode <String >('Child' )],
940+ ),
941+ ];
942+
943+ await tester.pumpWidget (
944+ MaterialApp (
945+ home: Scaffold (
946+ body: SizedBox (
947+ height: 100 ,
948+ width: 400 ,
949+ child: TreeView <String >(
950+ controller: treeController,
951+ verticalDetails: ScrollableDetails .vertical (
952+ controller: scrollController,
953+ ),
954+ tree: treeNodes,
955+ treeRowBuilder: (TreeViewNode <String > node) =>
956+ const TreeRow (extent: FixedTreeRowExtent (60.0 )),
957+ treeNodeBuilder:
958+ (
959+ BuildContext context,
960+ TreeViewNode <String > node,
961+ AnimationStyle toggleAnimationStyle,
962+ ) => Text (node.content),
963+ ),
964+ ),
965+ ),
966+ ),
967+ );
968+
969+ await tester.pump ();
970+ // Root (60) + Child (60) = 120. Viewport is 100.
971+ expect (scrollController.position.maxScrollExtent, 20.0 );
972+ scrollController.jumpTo (20.0 );
973+ await tester.pump ();
974+
975+ // Collapse Root. Now only Root (60) is visible.
976+ treeController.toggleNode (treeNodes[0 ]);
977+ await tester.pumpAndSettle ();
978+
979+ expect (scrollController.position.maxScrollExtent, 0.0 );
980+ expect (scrollController.offset, 0.0 );
981+ },
982+ );
983+ });
869984 });
870985}
871986
0 commit comments