Skip to content

Commit 93d06eb

Browse files
authored
fix unpack freezing app with animation duration zero (#153890)
Fixes #153889 an issue where nodes were being removed incorrectly when using `AnimationStyle.noAnimation `or the animation duration was zero seconds, which previously caused the application to freeze due to hidden state updates. By skipping the animation and updating active nodes immediately in these cases, we avoid these issues and ensure smooth and accurate management of node states.
1 parent 4b5cf85 commit 93d06eb

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,15 @@ class _TreeSliverState<T> extends State<TreeSliver<T>> with TickerProviderStateM
872872
_currentAnimationForParent[node]!.animation.dispose();
873873
}
874874

875+
// If animation is disabled or the duration is zero, we skip the animation
876+
// and immediately update the active nodes. This prevents the app from freezing
877+
// due to the tree being incorrectly updated when the animation duration is zero.
878+
// This is because, in this case, the node's children are no longer active.
879+
if (widget.toggleAnimationStyle == AnimationStyle.noAnimation || widget.toggleAnimationStyle?.duration == Duration.zero) {
880+
_unpackActiveNodes();
881+
return;
882+
}
883+
875884
final AnimationController controller = _currentAnimationForParent[node]?.controller
876885
?? AnimationController(
877886
value: node._expanded ? 0.0 : 1.0,

packages/flutter/test/widgets/sliver_tree_test.dart

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,4 +788,99 @@ void main() {
788788
expect(find.text('Child 2:1'), findsNothing);
789789
expect(find.text('Root 3'), findsOneWidget);
790790
});
791+
792+
testWidgets('TreeSliverNode should close all children when collapsed when animation is disabled', (WidgetTester tester) async {
793+
// Regression test for https://github.com/flutter/flutter/issues/153889
794+
final TreeSliverController controller = TreeSliverController();
795+
final List<TreeSliverNode<String>> tree = <TreeSliverNode<String>>[
796+
TreeSliverNode<String>('First'),
797+
TreeSliverNode<String>(
798+
'Second',
799+
children: <TreeSliverNode<String>>[
800+
TreeSliverNode<String>(
801+
'alpha',
802+
children: <TreeSliverNode<String>>[
803+
TreeSliverNode<String>('uno'),
804+
TreeSliverNode<String>('dos'),
805+
TreeSliverNode<String>('tres'),
806+
],
807+
),
808+
TreeSliverNode<String>('beta'),
809+
TreeSliverNode<String>('kappa'),
810+
],
811+
),
812+
TreeSliverNode<String>(
813+
'Third',
814+
expanded: true,
815+
children: <TreeSliverNode<String>>[
816+
TreeSliverNode<String>('gamma'),
817+
TreeSliverNode<String>('delta'),
818+
TreeSliverNode<String>('epsilon'),
819+
],
820+
),
821+
TreeSliverNode<String>('Fourth'),
822+
];
823+
824+
await tester.pumpWidget(MaterialApp(
825+
home: CustomScrollView(
826+
slivers: <Widget>[
827+
TreeSliver<String>(
828+
tree: tree,
829+
controller: controller,
830+
toggleAnimationStyle: AnimationStyle.noAnimation,
831+
treeNodeBuilder: (
832+
BuildContext context,
833+
TreeSliverNode<Object?> node,
834+
AnimationStyle animationStyle,
835+
) {
836+
final Widget child = GestureDetector(
837+
behavior: HitTestBehavior.translucent,
838+
onTap: () => controller.toggleNode(node),
839+
child: TreeSliver.defaultTreeNodeBuilder(
840+
context,
841+
node,
842+
animationStyle,
843+
),
844+
);
845+
846+
return child;
847+
},
848+
),
849+
],
850+
),
851+
));
852+
853+
expect(find.text('First'), findsOneWidget);
854+
expect(find.text('Second'), findsOneWidget);
855+
expect(find.text('Third'), findsOneWidget);
856+
expect(find.text('Fourth'), findsOneWidget);
857+
expect(find.text('alpha'), findsNothing);
858+
expect(find.text('beta'), findsNothing);
859+
expect(find.text('kappa'), findsNothing);
860+
expect(find.text('gamma'), findsOneWidget);
861+
expect(find.text('delta'), findsOneWidget);
862+
expect(find.text('epsilon'), findsOneWidget);
863+
expect(find.text('uno'), findsNothing);
864+
expect(find.text('dos'), findsNothing);
865+
expect(find.text('tres'), findsNothing);
866+
867+
await tester.tap(find.text('Second'));
868+
await tester.pumpAndSettle();
869+
870+
expect(find.text('alpha'), findsOneWidget);
871+
872+
await tester.tap(find.text('alpha'));
873+
await tester.pumpAndSettle();
874+
875+
expect(find.text('uno'), findsOneWidget);
876+
expect(find.text('dos'), findsOneWidget);
877+
expect(find.text('tres'), findsOneWidget);
878+
879+
await tester.tap(find.text('alpha'));
880+
await tester.pumpAndSettle();
881+
882+
expect(find.text('uno'), findsNothing);
883+
expect(find.text('dos'), findsNothing);
884+
expect(find.text('tres'), findsNothing);
885+
});
791886
}

0 commit comments

Comments
 (0)