Fix ParentDataWidget crash for multi view scenarios (#142486)

Fixes https://github.com/flutter/flutter/issues/142480.

This fixes a crash occurring during hot reload when a `ViewAnchor` is used between a `ParentDataWidget` (like `Positioned`) and its closest `RenderObject` descendant. Prior to the fix, the `ParentDataWidget` was accidentally applying its parent data to the render object in the `ViewAnchor.view` slot, which crashed because that render object wasn't (and shouldn't be) setup to accept parent data (after all, it is in a different render tree). Instead, the parent data should only be applied to the render object in the `ViewAnchor.child` slot. Luckily, with `Element.renderObjectAttachingChild` we already have API in place to walk the widget tree such as that only `RenderObjectWidgets` from the same render tree are considered.
This commit is contained in:
Michael Goderbauer 2024-01-31 11:22:07 -08:00 committed by GitHub
parent efca21c0fb
commit 3da5ff5490
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 4 deletions

View file

@ -5855,11 +5855,13 @@ class ParentDataElement<T extends ParentData> extends ProxyElement {
void applyParentDataToChild(Element child) {
if (child is RenderObjectElement) {
child._updateParentData(widget);
} else {
child.visitChildren(applyParentDataToChild);
} else if (child.renderObjectAttachingChild != null) {
applyParentDataToChild(child.renderObjectAttachingChild!);
}
}
visitChildren(applyParentDataToChild);
if (renderObjectAttachingChild != null) {
applyParentDataToChild(renderObjectAttachingChild!);
}
}
/// Calls [ParentDataWidget.applyParentData] on the given widget, passing it
@ -6536,7 +6538,7 @@ abstract class RenderObjectElement extends Element {
ErrorSummary('Incorrect use of ParentDataWidget.'),
...parentDataWidget._debugDescribeIncorrectParentDataType(
parentData: renderObject.parentData,
parentDataCreator: _ancestorRenderObjectElement!.widget as RenderObjectWidget,
parentDataCreator: _ancestorRenderObjectElement?.widget as RenderObjectWidget?,
ownershipChain: ErrorDescription(debugGetCreatorChain(10)),
),
]);

View file

@ -0,0 +1,42 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'multi_view_testing.dart';
void main() {
testWidgets('Hot reload does not crash if ViewAnchor is used between ParentDataWidget and the render object it is applied to', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/142480.
Widget buildTest(String string) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
Positioned( // ParentDataWidget
right: 0,
bottom: 0,
child: ViewAnchor(
view: View(
view: FakeView(tester.view),
child: const Text('Side-view'),
),
child: Text(string), // Text's RenderObject is the target for the ParentDataWidget above.
),
),
],
),
);
}
await tester.pumpWidget(buildTest('bottom-right'));
expect(tester.getBottomRight(find.text('bottom-right')), const Offset(800, 600));
// Rebuild with a slightly different string to simulate a hot reload.
await tester.pumpWidget(buildTest('bottom-right-again'));
expect(find.text('bottom-right'), findsNothing);
expect(tester.getBottomRight(find.text('bottom-right-again')), const Offset(800, 600));
});
}