Skip to content

Commit f4604fe

Browse files
authored
Fix SliverReorderableList item dispose (#105097)
1 parent b0edb5e commit f4604fe

File tree

2 files changed

+93
-23
lines changed

2 files changed

+93
-23
lines changed

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

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,7 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
603603

604604
@override
605605
void dispose() {
606-
_dragInfo?.dispose();
607-
_autoScroller?.stopAutoScroll();
606+
_dragReset();
608607
super.dispose();
609608
}
610609

@@ -658,7 +657,9 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
658657
///
659658
/// If no drag is active, this will do nothing.
660659
void cancelReorder() {
661-
_dragReset();
660+
setState(() {
661+
_dragReset();
662+
});
662663
}
663664

664665
void _registerItem(_ReorderableItemState item) {
@@ -724,7 +725,9 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
724725
}
725726

726727
void _dragCancel(_DragInfo item) {
727-
_dragReset();
728+
setState(() {
729+
_dragReset();
730+
});
728731
}
729732

730733
void _dragEnd(_DragInfo item) {
@@ -753,29 +756,29 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
753756
if (fromIndex != toIndex) {
754757
widget.onReorder.call(fromIndex, toIndex);
755758
}
756-
_dragReset();
759+
setState(() {
760+
_dragReset();
761+
});
757762
}
758763

759764
void _dragReset() {
760-
setState(() {
761-
if (_dragInfo != null) {
762-
if (_dragIndex != null && _items.containsKey(_dragIndex)) {
763-
final _ReorderableItemState dragItem = _items[_dragIndex!]!;
764-
dragItem._dragging = false;
765-
dragItem.rebuild();
766-
_dragIndex = null;
767-
}
768-
_dragInfo?.dispose();
769-
_dragInfo = null;
770-
_autoScroller?.stopAutoScroll();
771-
_resetItemGap();
772-
_recognizer?.dispose();
773-
_recognizer = null;
774-
_overlayEntry?.remove();
775-
_overlayEntry = null;
776-
_finalDropPosition = null;
765+
if (_dragInfo != null) {
766+
if (_dragIndex != null && _items.containsKey(_dragIndex)) {
767+
final _ReorderableItemState dragItem = _items[_dragIndex!]!;
768+
dragItem._dragging = false;
769+
dragItem.rebuild();
770+
_dragIndex = null;
777771
}
778-
});
772+
_dragInfo?.dispose();
773+
_dragInfo = null;
774+
_autoScroller?.stopAutoScroll();
775+
_resetItemGap();
776+
_recognizer?.dispose();
777+
_recognizer = null;
778+
_overlayEntry?.remove();
779+
_overlayEntry = null;
780+
_finalDropPosition = null;
781+
}
779782
}
780783

781784
void _resetItemGap() {

packages/flutter/test/widgets/reorderable_list_test.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,73 @@ void main() {
10051005
expect(items, orderedEquals(<int>[0, 1, 2, 3, 4]));
10061006
});
10071007
});
1008+
1009+
testWidgets('SliverReorderableList properly disposes items', (WidgetTester tester) async {
1010+
// Regression test for https://github.com/flutter/flutter/issues/105010
1011+
const int itemCount = 5;
1012+
final List<int> items = List<int>.generate(itemCount, (int index) => index);
1013+
1014+
await tester.pumpWidget(
1015+
MaterialApp(
1016+
home: Scaffold(
1017+
appBar: AppBar(),
1018+
drawer: Drawer(
1019+
child: Builder(
1020+
builder: (BuildContext context) {
1021+
return Column(
1022+
children: <Widget>[
1023+
Expanded(
1024+
child: CustomScrollView(
1025+
slivers: <Widget>[
1026+
SliverReorderableList(
1027+
itemCount: itemCount,
1028+
itemBuilder: (BuildContext context, int index) {
1029+
return Material(
1030+
key: ValueKey<String>('item-$index'),
1031+
child: ReorderableDragStartListener(
1032+
index: index,
1033+
child: ListTile(
1034+
title: Text('item ${items[index]}'),
1035+
),
1036+
),
1037+
);
1038+
},
1039+
onReorder: (int oldIndex, int newIndex) {},
1040+
),
1041+
],
1042+
),
1043+
),
1044+
TextButton(
1045+
onPressed: () {
1046+
Scaffold.of(context).closeDrawer();
1047+
},
1048+
child: const Text('Close drawer'),
1049+
),
1050+
],
1051+
);
1052+
}
1053+
),
1054+
),
1055+
),
1056+
),
1057+
);
1058+
1059+
await tester.tap(find.byIcon(Icons.menu));
1060+
await tester.pumpAndSettle();
1061+
1062+
final Finder item0 = find.text('item 0');
1063+
expect(item0, findsOneWidget);
1064+
1065+
// Start gesture on first item without drag up event.
1066+
final TestGesture drag = await tester.startGesture(tester.getCenter(item0));
1067+
await drag.moveBy(const Offset(0, 200));
1068+
await tester.pump();
1069+
1070+
await tester.tap(find.text('Close drawer'));
1071+
await tester.pumpAndSettle();
1072+
1073+
expect(item0, findsNothing);
1074+
});
10081075
}
10091076

10101077
class TestList extends StatefulWidget {

0 commit comments

Comments
 (0)