Fix assertion failure when reordering two dimensional children (#141504)

It fixes assertion failure due to unstable state of children list during reordering in `RenderTwoDimensionalViewport.parentDataOf`. This changes the assertion to check debug orphan list and `keepAlive` bucket in addition to children list to determine whether child belongs to this render object or not.

- Fixes #141101
This commit is contained in:
Amir Panahandeh 2024-01-27 02:07:45 +03:30 committed by GitHub
parent aaa3eba12d
commit a5ad088f7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 2 deletions

View file

@ -779,8 +779,11 @@ abstract class RenderTwoDimensionalViewport extends RenderBox implements RenderA
/// Children must have a [ParentData] of type
/// [TwoDimensionalViewportParentData], or a subclass thereof.
@protected
@mustCallSuper
TwoDimensionalViewportParentData parentDataOf(RenderBox child) {
assert(_children.containsValue(child));
assert(_children.containsValue(child) ||
_keepAliveBucket.containsValue(child) ||
_debugOrphans!.contains(child));
return child.parentData! as TwoDimensionalViewportParentData;
}

View file

@ -195,7 +195,7 @@ class RenderSimpleBuilderTableViewport extends RenderTwoDimensionalViewport {
@override
TestExtendedParentData parentDataOf(RenderBox child) {
return child.parentData! as TestExtendedParentData;
return super.parentDataOf(child) as TestExtendedParentData;
}
@override

View file

@ -2711,6 +2711,54 @@ void main() {
);
});
});
testWidgets('correctly reorders children and wont throw assertion failure',
(WidgetTester tester) async {
final TwoDimensionalChildBuilderDelegate delegate1 =
TwoDimensionalChildBuilderDelegate(
maxXIndex: 5,
maxYIndex: 5,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
builder: (BuildContext context, ChildVicinity vicinity) {
ValueKey<int>? key;
if (vicinity == const ChildVicinity(xIndex: 1, yIndex: 1)) {
key = const ValueKey<int>(1);
} else if (vicinity ==
const ChildVicinity(xIndex: 1, yIndex: 2)) {
key = const ValueKey<int>(2);
}
return SizedBox.square(key: key, dimension: 200);
});
final TwoDimensionalChildBuilderDelegate delegate2 =
TwoDimensionalChildBuilderDelegate(
maxXIndex: 5,
maxYIndex: 5,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
builder: (BuildContext context, ChildVicinity vicinity) {
ValueKey<int>? key;
if (vicinity == const ChildVicinity(xIndex: 0, yIndex: 0)) {
key = const ValueKey<int>(1);
} else if (vicinity ==
const ChildVicinity(xIndex: 1, yIndex: 1)) {
key = const ValueKey<int>(2);
}
return SizedBox.square(key: key, dimension: 200);
});
addTearDown(delegate1.dispose);
addTearDown(delegate2.dispose);
await tester.pumpWidget(simpleBuilderTest(delegate: delegate1));
expect(tester.getRect(find.byKey(const ValueKey<int>(1))),
const Rect.fromLTWH(200.0, 200.0, 200.0, 200.0));
await tester.pumpWidget(simpleBuilderTest(delegate: delegate2));
expect(tester.getRect(find.byKey(const ValueKey<int>(1))),
const Rect.fromLTWH(0.0, 0.0, 200.0, 200.0));
await tester.pumpWidget(simpleBuilderTest(delegate: delegate1));
expect(tester.getRect(find.byKey(const ValueKey<int>(1))),
const Rect.fromLTWH(200.0, 200.0, 200.0, 200.0));
}, variant: TargetPlatformVariant.all());
});
}