Check if `MultiChildRenderObjectElement` has an associated RO (#78854)
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index c85833e..313ab39 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -6176,6 +6176,35 @@
super.forgetChild(child);
}
+ bool _debugCheckHasAssociatedRenderObject(Element newChild) {
+ assert(() {
+ if (newChild.renderObject == null) {
+ FlutterError.reportError(
+ FlutterErrorDetails(
+ exception: FlutterError.fromParts(<DiagnosticsNode>[
+ ErrorSummary('The children of `MultiChildRenderObjectElement` must each has an associated render object.'),
+ ErrorHint(
+ 'This typically means that the `${newChild.widget}` or its children\n'
+ 'are not a subtype of `RenderObjectWidget`.'
+ ),
+ newChild.describeElement('The following element does not have an associated render object'),
+ DiagnosticsDebugCreator(DebugCreator(newChild)),
+ ]),
+ ),
+ );
+ }
+ return true;
+ }());
+ return true;
+ }
+
+ @override
+ Element inflateWidget(Widget newWidget, Object? newSlot) {
+ final Element newChild = super.inflateWidget(newWidget, newSlot);
+ assert(_debugCheckHasAssociatedRenderObject(newChild));
+ return newChild;
+ }
+
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
diff --git a/packages/flutter/test/widgets/framework_test.dart b/packages/flutter/test/widgets/framework_test.dart
index 2a4c3ed..972ace3 100644
--- a/packages/flutter/test/widgets/framework_test.dart
+++ b/packages/flutter/test/widgets/framework_test.dart
@@ -1172,6 +1172,64 @@
);
});
+ testWidgets('Can not attach a non-RenderObjectElement to the MultiChildRenderObjectElement - mount', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ Column(
+ children: <Widget>[
+ Container(),
+ const _EmptyWidget(),
+ ],
+ ),
+ );
+
+ final dynamic exception = tester.takeException();
+ expect(exception, isFlutterError);
+ expect(
+ exception.toString(),
+ equalsIgnoringHashCodes(
+ 'The children of `MultiChildRenderObjectElement` must each has an associated render object.\n'
+ 'This typically means that the `_EmptyWidget` or its children\n'
+ 'are not a subtype of `RenderObjectWidget`.\n'
+ 'The following element does not have an associated render object:\n'
+ ' _EmptyWidget\n'
+ 'debugCreator: _EmptyWidget ← Column ← [root]'
+ ),
+ );
+ });
+
+ testWidgets('Can not attach a non-RenderObjectElement to the MultiChildRenderObjectElement - update', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ Column(
+ children: <Widget>[
+ Container(),
+ ],
+ ),
+ );
+
+ await tester.pumpWidget(
+ Column(
+ children: <Widget>[
+ Container(),
+ const _EmptyWidget(),
+ ],
+ ),
+ );
+
+ final dynamic exception = tester.takeException();
+ expect(exception, isFlutterError);
+ expect(
+ exception.toString(),
+ equalsIgnoringHashCodes(
+ 'The children of `MultiChildRenderObjectElement` must each has an associated render object.\n'
+ 'This typically means that the `_EmptyWidget` or its children\n'
+ 'are not a subtype of `RenderObjectWidget`.\n'
+ 'The following element does not have an associated render object:\n'
+ ' _EmptyWidget\n'
+ 'debugCreator: _EmptyWidget ← Column ← [root]'
+ ),
+ );
+ });
+
testWidgets('Element diagnostics', (WidgetTester tester) async {
GlobalKey key0;
await tester.pumpWidget(Column(
@@ -1849,3 +1907,20 @@
class TestRenderObjectElement extends RenderObjectElement {
TestRenderObjectElement() : super(Table());
}
+
+class _EmptyWidget extends Widget {
+ const _EmptyWidget({Key? key}) : super(key: key);
+
+ @override
+ Element createElement() => _EmptyElement(this);
+}
+
+class _EmptyElement extends Element {
+ _EmptyElement(_EmptyWidget widget) : super(widget);
+
+ @override
+ bool get debugDoingBuild => false;
+
+ @override
+ void performRebuild() {}
+}