1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'binding.dart';
6library;
7
8import 'package:flutter/foundation.dart';
9import 'package:flutter/rendering.dart';
10
11import 'framework.dart';
12
13/// A bridge from a [RenderObject] to an [Element] tree.
14///
15/// The given container is the [RenderObject] that the [Element] tree should be
16/// inserted into. It must be a [RenderObject] that implements the
17/// [RenderObjectWithChildMixin] protocol. The type argument `T` is the kind of
18/// [RenderObject] that the container expects as its child.
19///
20/// The [RenderObjectToWidgetAdapter] is an alternative to [RootWidget] for
21/// bootstrapping an element tree. Unlike [RootWidget] it requires the
22/// existence of a render tree (the [container]) to attach the element tree to.
23class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
24 /// Creates a bridge from a [RenderObject] to an [Element] tree.
25 RenderObjectToWidgetAdapter({this.child, required this.container, this.debugShortDescription})
26 : super(key: GlobalObjectKey(container));
27
28 /// The widget below this widget in the tree.
29 ///
30 /// {@macro flutter.widgets.ProxyWidget.child}
31 final Widget? child;
32
33 /// The [RenderObject] that is the parent of the [Element] created by this widget.
34 final RenderObjectWithChildMixin<T> container;
35
36 /// A short description of this widget used by debugging aids.
37 final String? debugShortDescription;
38
39 @override
40 RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
41
42 @override
43 RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
44
45 @override
46 void updateRenderObject(BuildContext context, RenderObject renderObject) {}
47
48 /// Inflate this widget and actually set the resulting [RenderObject] as the
49 /// child of [container].
50 ///
51 /// If `element` is null, this function will create a new element. Otherwise,
52 /// the given element will have an update scheduled to switch to this widget.
53 RenderObjectToWidgetElement<T> attachToRenderTree(
54 BuildOwner owner, [
55 RenderObjectToWidgetElement<T>? element,
56 ]) {
57 if (element == null) {
58 owner.lockState(() {
59 element = createElement();
60 assert(element != null);
61 element!.assignOwner(owner);
62 });
63 owner.buildScope(element!, () {
64 element!.mount(null, null);
65 });
66 } else {
67 element._newWidget = this;
68 element.markNeedsBuild();
69 }
70 return element!;
71 }
72
73 @override
74 String toStringShort() => debugShortDescription ?? super.toStringShort();
75}
76
77/// The root of an element tree that is hosted by a [RenderObject].
78///
79/// This element class is the instantiation of a [RenderObjectToWidgetAdapter]
80/// widget. It can be used only as the root of an [Element] tree (it cannot be
81/// mounted into another [Element]; it's parent must be null).
82///
83/// In typical usage, it will be instantiated for a [RenderObjectToWidgetAdapter]
84/// whose container is the [RenderView].
85class RenderObjectToWidgetElement<T extends RenderObject> extends RenderTreeRootElement
86 with RootElementMixin {
87 /// Creates an element that is hosted by a [RenderObject].
88 ///
89 /// The [RenderObject] created by this element is not automatically set as a
90 /// child of the hosting [RenderObject]. To actually attach this element to
91 /// the render tree, call [RenderObjectToWidgetAdapter.attachToRenderTree].
92 RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> super.widget);
93
94 Element? _child;
95
96 static const Object _rootChildSlot = Object();
97
98 @override
99 void visitChildren(ElementVisitor visitor) {
100 if (_child != null) {
101 visitor(_child!);
102 }
103 }
104
105 @override
106 void forgetChild(Element child) {
107 assert(child == _child);
108 _child = null;
109 super.forgetChild(child);
110 }
111
112 @override
113 void mount(Element? parent, Object? newSlot) {
114 assert(parent == null);
115 super.mount(parent, newSlot);
116 _rebuild();
117 assert(_child != null);
118 }
119
120 @override
121 void update(RenderObjectToWidgetAdapter<T> newWidget) {
122 super.update(newWidget);
123 assert(widget == newWidget);
124 _rebuild();
125 }
126
127 // When we are assigned a new widget, we store it here
128 // until we are ready to update to it.
129 Widget? _newWidget;
130
131 @override
132 void performRebuild() {
133 if (_newWidget != null) {
134 // _newWidget can be null if, for instance, we were rebuilt
135 // due to a reassemble.
136 final Widget newWidget = _newWidget!;
137 _newWidget = null;
138 update(newWidget as RenderObjectToWidgetAdapter<T>);
139 }
140 super.performRebuild();
141 assert(_newWidget == null);
142 }
143
144 @pragma('vm:notify-debugger-on-exception')
145 void _rebuild() {
146 try {
147 _child = updateChild(
148 _child,
149 (widget as RenderObjectToWidgetAdapter<T>).child,
150 _rootChildSlot,
151 );
152 } catch (exception, stack) {
153 final FlutterErrorDetails details = FlutterErrorDetails(
154 exception: exception,
155 stack: stack,
156 library: 'widgets library',
157 context: ErrorDescription('attaching to the render tree'),
158 );
159 FlutterError.reportError(details);
160 final Widget error = ErrorWidget.builder(details);
161 _child = updateChild(null, error, _rootChildSlot);
162 }
163 }
164
165 @override
166 RenderObjectWithChildMixin<T> get renderObject =>
167 super.renderObject as RenderObjectWithChildMixin<T>;
168
169 @override
170 void insertRenderObjectChild(RenderObject child, Object? slot) {
171 assert(slot == _rootChildSlot);
172 assert(renderObject.debugValidateChild(child));
173 renderObject.child = child as T;
174 }
175
176 @override
177 void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
178 assert(false);
179 }
180
181 @override
182 void removeRenderObjectChild(RenderObject child, Object? slot) {
183 assert(renderObject.child == child);
184 renderObject.child = null;
185 }
186}
187