diff --git a/packages/flutter/lib/src/material/reorderable_list.dart b/packages/flutter/lib/src/material/reorderable_list.dart index 809f1c05365..a9974e1ee0c 100644 --- a/packages/flutter/lib/src/material/reorderable_list.dart +++ b/packages/flutter/lib/src/material/reorderable_list.dart @@ -363,7 +363,7 @@ class _ReorderableListContentState extends State<_ReorderableListContent> with T // Handles up the logic for dragging and reordering items in the list. Widget _wrap(Widget toWrap, int index, BoxConstraints constraints) { assert(toWrap.key != null); - final GlobalObjectKey keyIndexGlobalKey = GlobalObjectKey(toWrap.key); + final _ReorderableListViewChildGlobalKey keyIndexGlobalKey = _ReorderableListViewChildGlobalKey(toWrap.key, this); // We pass the toWrapWithGlobalKey into the Draggable so that when a list // item gets dragged, the accessibility framework can preserve the selected // state of the dragging item. @@ -596,3 +596,30 @@ class _ReorderableListContentState extends State<_ReorderableListContent> with T }); } } + +// A global key that takes its identity from the object and uses a value of a +// particular type to identify itself. +// +// The difference with GlobalObjectKey is that it uses [==] instead of [identical] +// of the objects used to generate widgets. +@optionalTypeArgs +class _ReorderableListViewChildGlobalKey extends GlobalObjectKey { + + const _ReorderableListViewChildGlobalKey(this.subKey, this.state) : super(subKey); + + final Key subKey; + + final _ReorderableListContentState state; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is _ReorderableListViewChildGlobalKey + && other.subKey == subKey + && other.state == state; + } + + @override + int get hashCode => hashValues(subKey, state); +} diff --git a/packages/flutter/test/material/reorderable_list_test.dart b/packages/flutter/test/material/reorderable_list_test.dart index 831ec52438d..0a2af39b916 100644 --- a/packages/flutter/test/material/reorderable_list_test.dart +++ b/packages/flutter/test/material/reorderable_list_test.dart @@ -253,6 +253,35 @@ void main() { expect(findState(const Key('A')).checked, true); }); + testWidgets('Preserves children states when rebuilt', (WidgetTester tester) async { + const Key firstBox = Key('key'); + Widget build() { + return MaterialApp( + home: Directionality( + textDirection: TextDirection.ltr, + child: SizedBox( + width: 100, + height: 100, + child: ReorderableListView( + scrollDirection: Axis.vertical, + children: const [ + SizedBox(key: firstBox, width: 10, height: 10), + ], + onReorder: (_, __) {}, + ), + ), + ), + ); + } + + // When the widget is rebuilt, the state of child should be consistent. + await tester.pumpWidget(build()); + final Element e0 = tester.element(find.byKey(firstBox)); + await tester.pumpWidget(build()); + final Element e1 = tester.element(find.byKey(firstBox)); + expect(e0, equals(e1)); + }); + testWidgets('Uses the PrimaryScrollController when available', (WidgetTester tester) async { final ScrollController primary = ScrollController(); final Widget reorderableList = ReorderableListView( @@ -771,6 +800,35 @@ void main() { expect(findState(const Key('A')).checked, true); }); + testWidgets('Preserves children states when rebuilt', (WidgetTester tester) async { + const Key firstBox = Key('key'); + Widget build() { + return MaterialApp( + home: Directionality( + textDirection: TextDirection.ltr, + child: SizedBox( + width: 100, + height: 100, + child: ReorderableListView( + scrollDirection: Axis.horizontal, + children: const [ + SizedBox(key: firstBox, width: 10, height: 10), + ], + onReorder: (_, __) {}, + ), + ), + ), + ); + } + + // When the widget is rebuilt, the state of child should be consistent. + await tester.pumpWidget(build()); + final Element e0 = tester.element(find.byKey(firstBox)); + await tester.pumpWidget(build()); + final Element e1 = tester.element(find.byKey(firstBox)); + expect(e0, equals(e1)); + }); + group('Accessibility (a11y/Semantics)', () { Map getSemanticsActions(int index) { final Semantics semantics = find.ancestor(