@@ -149,95 +149,93 @@ class _FloatColumnElement extends RenderObjectElement
149149 @override
150150 RenderFloatColumn get renderObject => super .renderObject as RenderFloatColumn ;
151151
152- // We call `updateChild` at two different times:
153- // 1. When we ourselves are told to rebuild (see performRebuild).
154- // 2. When our render object needs a new child (see createChild).
155- // In both cases, we cache the results of calling into our delegate to get
156- // the child widget, so that if we do case 2 later, we don't call the builder
157- // again. Any time we do case 1, though, we reset the cache.
158-
159- /// A cache of widgets so that we don't have to rebuild every time.
160- final HashMap <int , Widget ?> _childWidgets = HashMap <int , Widget ?>();
152+ /// The current list of children of this element.
153+ ///
154+ /// This list is filtered to hide elements that have been forgotten (using
155+ /// [forgetChild] ).
156+ Iterable <Element > get children =>
157+ _children.where ((child) => ! _forgottenChildren.contains (child));
161158
162- /// The map containing all active child elements. SplayTreeMap is used so that
163- /// we have all elements ordered and iterable by their keys.
164- final SplayTreeMap < int , Element > _childElements =
165- SplayTreeMap < int , Element >();
159+ late List < Element > _children;
160+ // We keep a set of forgotten children to avoid O(n^2) work walking _children
161+ // repeatedly to remove children.
162+ final Set < Element > _forgottenChildren = HashSet < Element >();
166163
167164 @override
168- void update (FloatColumn newWidget) {
169- // dmPrint('_FloatColumnElement update');
170- final oldWidget = widget as FloatColumn ;
171- super .update (newWidget);
172- if (newWidget._textAndWidgets != oldWidget._textAndWidgets) {
173- performRebuild ();
174- renderObject.markNeedsLayout ();
165+ void mount (Element ? parent, Object ? newSlot) {
166+ super .mount (parent, newSlot);
167+ final floatColumnWidget = widget as FloatColumn ;
168+ final children = List <Element >.filled (
169+ floatColumnWidget._textAndWidgets.length, _NullElement .instance);
170+ Element ? previousChild;
171+ for (var i = 0 ; i < children.length; i += 1 ) {
172+ final textOrWidget = floatColumnWidget._textAndWidgets[i];
173+ final widget = textOrWidget is Widget
174+ ? textOrWidget
175+ : (textOrWidget as WrappableText ).toWidget ();
176+ final newChild =
177+ inflateWidget (widget, IndexedSlot <Element ?>(i, previousChild));
178+ children[i] = newChild;
179+ previousChild = newChild;
175180 }
181+ _children = children;
176182 }
177183
178184 @override
179- void performRebuild () {
180- // dmPrint('_FloatColumnElement performRebuild');
181- _childWidgets.clear ();
182- super .performRebuild ();
183- }
184-
185- @override
186- Element ? updateChild (Element ? child, Widget ? newWidget, Object ? newSlot) {
187- // dmPrint('_FloatColumnElement updateChild');
188- final oldParentData =
189- child? .renderObject? .parentData as FloatColumnParentData ? ;
190- final newChild = super .updateChild (child, newWidget, newSlot);
191- final newParentData =
192- newChild? .renderObject? .parentData as FloatColumnParentData ? ;
193- if (newParentData != null ) {
194- newParentData.index = newSlot! as int ;
195- if (oldParentData != null ) {
196- newParentData.offset = oldParentData.offset;
197- }
198- }
199-
200- return newChild;
185+ void update (FloatColumn newWidget) {
186+ super .update (newWidget);
187+ final floatColumnWidget = widget as FloatColumn ;
188+ _children = updateChildren (
189+ _children, floatColumnWidget._textAndWidgets.toWidgets (),
190+ forgottenChildren: _forgottenChildren);
191+ _forgottenChildren.clear ();
201192 }
202193
203194 @override
204- void insertRenderObjectChild (RenderObject child, int slot) {
205- // dmPrint('_FloatColumnElement insertRenderObjectChild');
206- final renderObject = this .renderObject;
195+ void insertRenderObjectChild (RenderObject child, IndexedSlot <Element ?> slot) {
196+ final ContainerRenderObjectMixin <RenderObject ,
197+ ContainerParentDataMixin <RenderObject >> renderObject =
198+ this .renderObject;
207199 assert (renderObject.debugValidateChild (child));
208- renderObject.insert (child as RenderBox ,
209- after: _childElements[slot - 1 ]? .renderObject as RenderBox ? );
200+ renderObject.insert (child, after: slot.value? .renderObject);
210201 assert (renderObject == this .renderObject);
211202 }
212203
213204 @override
214- void moveRenderObjectChild (RenderObject child, int oldSlot, int newSlot) {
215- // dmPrint('_FloatColumnElement moveRenderObjectChild');
216- const moveChildRenderObjectErrorMessage =
217- 'Currently we maintain the list in contiguous increasing order, so '
218- 'moving children around is not allowed.' ;
219- assert (false , moveChildRenderObjectErrorMessage);
205+ void moveRenderObjectChild (RenderObject child, IndexedSlot <Element ?> oldSlot,
206+ IndexedSlot <Element ?> newSlot) {
207+ final ContainerRenderObjectMixin <RenderObject ,
208+ ContainerParentDataMixin <RenderObject >> renderObject =
209+ this .renderObject;
210+ assert (child.parent == renderObject);
211+ renderObject.move (child, after: newSlot.value? .renderObject);
212+ assert (renderObject == this .renderObject);
220213 }
221214
222215 @override
223- void removeRenderObjectChild (RenderObject child, int slot) {
224- // dmPrint('_FloatColumnElement removeRenderObjectChild');
216+ void removeRenderObjectChild (RenderObject child, Object ? slot) {
217+ final ContainerRenderObjectMixin <RenderObject ,
218+ ContainerParentDataMixin <RenderObject >> renderObject =
219+ this .renderObject;
225220 assert (child.parent == renderObject);
226- renderObject.remove (child as RenderBox );
221+ renderObject.remove (child);
222+ assert (renderObject == this .renderObject);
227223 }
228224
229225 @override
230226 void visitChildren (ElementVisitor visitor) {
231- // dmPrint('_FloatColumnElement visitChildren');
232- _childElements.forEach ((key, child) {
233- visitor (child);
234- });
227+ for (final child in _children) {
228+ if (! _forgottenChildren.contains (child)) {
229+ visitor (child);
230+ }
231+ }
235232 }
236233
237234 @override
238235 void forgetChild (Element child) {
239- // dmPrint('_FloatColumnElement forgetChild');
240- _childElements.remove (child.slot);
236+ assert (_children.contains (child));
237+ assert (! _forgottenChildren.contains (child));
238+ _forgottenChildren.add (child);
241239 super .forgetChild (child);
242240 }
243241
@@ -248,69 +246,37 @@ class _FloatColumnElement extends RenderObjectElement
248246 @override
249247 List <Object > get textAndWidgets => (widget as FloatColumn )._textAndWidgets;
250248
251- @override
252- void addOrUpdateWidgetAt (int index, Widget widget) {
253- // dmPrint('_FloatColumnElement addOrUpdateWidgetAt');
254- _childWidgets[index] = widget;
255- }
256-
257249 @override
258250 RenderBox ? childAt (int index) {
259- // dmPrint('_FloatColumnElement childAt');
260- return _childElements[index]? .renderObject as RenderBox ? ;
251+ return _children.maybeElementAt (index)? .renderObject as RenderBox ? ;
261252 }
262253
263254 @override
264- RenderBox ? addOrUpdateChild (int index, { required RenderBox ? after} ) {
265- // dmPrint('_FloatColumnElement createChild ');
255+ RenderBox ? updateWidgetAt (int index, Widget widget ) {
256+ // dmPrint('_FloatColumnElement updateWidgetAt ');
266257 RenderBox ? child;
267258 owner! .buildScope (this , () {
268- final insertFirst = after == null ;
269- assert (insertFirst || _childElements[index - 1 ] != null );
270- final newChild =
271- updateChild (_childElements[index], _childWidgets[index], index);
259+ assert (index >= 0 && index < _children.length);
260+ final newChild = updateChild (_children[index], widget,
261+ IndexedSlot <Element ?>(index, _children.maybeElementAt (index - 1 )));
272262 if (newChild != null ) {
273263 child = newChild.renderObject == null
274264 ? null
275265 : newChild.renderObject! as RenderBox ;
276- _childElements [index] = newChild;
266+ _children [index] = newChild;
277267 } else {
278- _childElements.remove (index);
268+ if (! _forgottenChildren.contains (newChild)) {
269+ forgetChild (newChild! );
270+ }
279271 }
280272 });
281273 return child;
282274 }
275+ }
283276
284- @override
285- void removeChild (RenderBox child) {
286- // dmPrint('_FloatColumnElement removeChild');
287- final index = renderObject.indexOf (child);
288- owner! .buildScope (this , () {
289- assert (_childElements.containsKey (index));
290- final result = updateChild (_childElements[index], null , index);
291- assert (result == null );
292- _childElements.remove (index);
293- assert (! _childElements.containsKey (index));
294- });
295- }
296-
297- @override
298- void removeAllChildren () {
299- // dmPrint('_FloatColumnElement removeAllChildren');
300- owner! .buildScope (this , () {
301- _childElements
302- ..forEach ((index, child) {
303- final result = updateChild (child, null , index);
304- assert (result == null );
305- })
306- ..clear ();
307- // assert(_childElements.containsKey(index));
308- // final result = updateChild(_childElements[index], null, index);
309- // assert(result == null);
310- // _childElements.remove(index);
311- // assert(!_childElements.containsKey(index));
312- });
313- }
277+ extension _ExtOnList <T > on List <T > {
278+ /// Returns `i >= 0 && i < length ? this[i] : null` .
279+ T ? maybeElementAt (int i) => i >= 0 && i < length ? this [i] : null ;
314280}
315281
316282Iterable <Object > _expandToIncludeFloatedWidgetSpanChildren (
@@ -438,3 +404,26 @@ Key? _firstNonUniqueKey(Iterable<Object> children) {
438404 }
439405 return null ;
440406}
407+
408+ extension on List <Object > {
409+ List <Widget > toWidgets () =>
410+ map ((e) => e is Widget ? e : (e as WrappableText ).toWidget ()).toList ();
411+ }
412+
413+ /// Used as a placeholder in [List<Element>] objects when the actual
414+ /// elements are not yet determined.
415+ class _NullElement extends Element {
416+ _NullElement () : super (const _NullWidget ());
417+
418+ static _NullElement instance = _NullElement ();
419+
420+ @override
421+ bool get debugDoingBuild => throw UnimplementedError ();
422+ }
423+
424+ class _NullWidget extends Widget {
425+ const _NullWidget ();
426+
427+ @override
428+ Element createElement () => throw UnimplementedError ();
429+ }
0 commit comments