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 | import 'package:flutter/foundation.dart'; |
6 | import 'package:flutter/rendering.dart'; |
7 | |
8 | import 'framework.dart'; |
9 | |
10 | /// A bridge from a [RenderObject] to an [Element] tree. |
11 | /// |
12 | /// The given container is the [RenderObject] that the [Element] tree should be |
13 | /// inserted into. It must be a [RenderObject] that implements the |
14 | /// [RenderObjectWithChildMixin] protocol. The type argument `T` is the kind of |
15 | /// [RenderObject] that the container expects as its child. |
16 | /// |
17 | /// The [RenderObjectToWidgetAdapter] is an alternative to [RootWidget] for |
18 | /// bootstrapping an element tree. Unlike [RootWidget] it requires the |
19 | /// existence of a render tree (the [container]) to attach the element tree to. |
20 | class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget { |
21 | /// Creates a bridge from a [RenderObject] to an [Element] tree. |
22 | RenderObjectToWidgetAdapter({ |
23 | this.child, |
24 | required this.container, |
25 | 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(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) { |
54 | if (element == null) { |
55 | owner.lockState(() { |
56 | element = createElement(); |
57 | assert(element != null); |
58 | element!.assignOwner(owner); |
59 | }); |
60 | owner.buildScope(element!, () { |
61 | element!.mount(null, null); |
62 | }); |
63 | } else { |
64 | element._newWidget = this; |
65 | element.markNeedsBuild(); |
66 | } |
67 | return element!; |
68 | } |
69 | |
70 | @override |
71 | String toStringShort() => debugShortDescription ?? super.toStringShort(); |
72 | } |
73 | |
74 | /// The root of an element tree that is hosted by a [RenderObject]. |
75 | /// |
76 | /// This element class is the instantiation of a [RenderObjectToWidgetAdapter] |
77 | /// widget. It can be used only as the root of an [Element] tree (it cannot be |
78 | /// mounted into another [Element]; it's parent must be null). |
79 | /// |
80 | /// In typical usage, it will be instantiated for a [RenderObjectToWidgetAdapter] |
81 | /// whose container is the [RenderView]. |
82 | class RenderObjectToWidgetElement<T extends RenderObject> extends RenderTreeRootElement with RootElementMixin { |
83 | /// Creates an element that is hosted by a [RenderObject]. |
84 | /// |
85 | /// The [RenderObject] created by this element is not automatically set as a |
86 | /// child of the hosting [RenderObject]. To actually attach this element to |
87 | /// the render tree, call [RenderObjectToWidgetAdapter.attachToRenderTree]. |
88 | RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> super.widget); |
89 | |
90 | Element? _child; |
91 | |
92 | static const Object _rootChildSlot = Object(); |
93 | |
94 | @override |
95 | void visitChildren(ElementVisitor visitor) { |
96 | if (_child != null) { |
97 | visitor(_child!); |
98 | } |
99 | } |
100 | |
101 | @override |
102 | void forgetChild(Element child) { |
103 | assert(child == _child); |
104 | _child = null; |
105 | super.forgetChild(child); |
106 | } |
107 | |
108 | @override |
109 | void mount(Element? parent, Object? newSlot) { |
110 | assert(parent == null); |
111 | super.mount(parent, newSlot); |
112 | _rebuild(); |
113 | assert(_child != null); |
114 | } |
115 | |
116 | @override |
117 | void update(RenderObjectToWidgetAdapter<T> newWidget) { |
118 | super.update(newWidget); |
119 | assert(widget == newWidget); |
120 | _rebuild(); |
121 | } |
122 | |
123 | // When we are assigned a new widget, we store it here |
124 | // until we are ready to update to it. |
125 | Widget? _newWidget; |
126 | |
127 | @override |
128 | void performRebuild() { |
129 | if (_newWidget != null) { |
130 | // _newWidget can be null if, for instance, we were rebuilt |
131 | // due to a reassemble. |
132 | final Widget newWidget = _newWidget!; |
133 | _newWidget = null; |
134 | update(newWidget as RenderObjectToWidgetAdapter<T>); |
135 | } |
136 | super.performRebuild(); |
137 | assert(_newWidget == null); |
138 | } |
139 | |
140 | @pragma('vm:notify-debugger-on-exception' ) |
141 | void _rebuild() { |
142 | try { |
143 | _child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot); |
144 | } catch (exception, stack) { |
145 | final FlutterErrorDetails details = FlutterErrorDetails( |
146 | exception: exception, |
147 | stack: stack, |
148 | library: 'widgets library' , |
149 | context: ErrorDescription('attaching to the render tree' ), |
150 | ); |
151 | FlutterError.reportError(details); |
152 | final Widget error = ErrorWidget.builder(details); |
153 | _child = updateChild(null, error, _rootChildSlot); |
154 | } |
155 | } |
156 | |
157 | @override |
158 | RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>; |
159 | |
160 | @override |
161 | void insertRenderObjectChild(RenderObject child, Object? slot) { |
162 | assert(slot == _rootChildSlot); |
163 | assert(renderObject.debugValidateChild(child)); |
164 | renderObject.child = child as T; |
165 | } |
166 | |
167 | @override |
168 | void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) { |
169 | assert(false); |
170 | } |
171 | |
172 | @override |
173 | void removeRenderObjectChild(RenderObject child, Object? slot) { |
174 | assert(renderObject.child == child); |
175 | renderObject.child = null; |
176 | } |
177 | } |
178 | |