Fixes issue, ReorderableListView destroys children even if their key-type wasn't changed (#64855)

This commit is contained in:
Haeseok Lee 2020-09-09 13:25:05 +09:00 committed by GitHub
parent 38834d36d3
commit 011331dce5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 1 deletions

View file

@ -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);
}

View file

@ -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 <Widget>[
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 <Widget>[
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<CustomSemanticsAction, VoidCallback> getSemanticsActions(int index) {
final Semantics semantics = find.ancestor(