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 'dart:ui' as ui show PictureRecorder; |
6 | |
7 | import 'package:flutter/animation.dart'; |
8 | import 'package:flutter/foundation.dart'; |
9 | import 'package:flutter/gestures.dart'; |
10 | import 'package:flutter/painting.dart'; |
11 | import 'package:flutter/scheduler.dart'; |
12 | import 'package:flutter/semantics.dart'; |
13 | |
14 | import 'debug.dart'; |
15 | import 'layer.dart'; |
16 | |
17 | export 'package:flutter/foundation.dart' show |
18 | DiagnosticPropertiesBuilder, |
19 | DiagnosticsNode, |
20 | DiagnosticsProperty, |
21 | DoubleProperty, |
22 | EnumProperty, |
23 | ErrorDescription, |
24 | ErrorHint, |
25 | ErrorSummary, |
26 | FlagProperty, |
27 | FlutterError, |
28 | InformationCollector, |
29 | IntProperty, |
30 | StringProperty; |
31 | export 'package:flutter/gestures.dart' show HitTestEntry, HitTestResult; |
32 | export 'package:flutter/painting.dart'; |
33 | |
34 | /// Base class for data associated with a [RenderObject] by its parent. |
35 | /// |
36 | /// Some render objects wish to store data on their children, such as the |
37 | /// children's input parameters to the parent's layout algorithm or the |
38 | /// children's position relative to other children. |
39 | /// |
40 | /// See also: |
41 | /// |
42 | /// * [RenderObject.setupParentData], which [RenderObject] subclasses may |
43 | /// override to attach specific types of parent data to children. |
44 | class ParentData { |
45 | /// Called when the RenderObject is removed from the tree. |
46 | @protected |
47 | @mustCallSuper |
48 | void detach() { } |
49 | |
50 | @override |
51 | String toString() => '<none>' ; |
52 | } |
53 | |
54 | /// Signature for painting into a [PaintingContext]. |
55 | /// |
56 | /// The `offset` argument is the offset from the origin of the coordinate system |
57 | /// of the [PaintingContext.canvas] to the coordinate system of the callee. |
58 | /// |
59 | /// Used by many of the methods of [PaintingContext]. |
60 | typedef PaintingContextCallback = void Function(PaintingContext context, Offset offset); |
61 | |
62 | /// A place to paint. |
63 | /// |
64 | /// Rather than holding a canvas directly, [RenderObject]s paint using a painting |
65 | /// context. The painting context has a [Canvas], which receives the |
66 | /// individual draw operations, and also has functions for painting child |
67 | /// render objects. |
68 | /// |
69 | /// When painting a child render object, the canvas held by the painting context |
70 | /// can change because the draw operations issued before and after painting the |
71 | /// child might be recorded in separate compositing layers. For this reason, do |
72 | /// not hold a reference to the canvas across operations that might paint |
73 | /// child render objects. |
74 | /// |
75 | /// New [PaintingContext] objects are created automatically when using |
76 | /// [PaintingContext.repaintCompositedChild] and [pushLayer]. |
77 | class PaintingContext extends ClipContext { |
78 | |
79 | /// Creates a painting context. |
80 | /// |
81 | /// Typically only called by [PaintingContext.repaintCompositedChild] |
82 | /// and [pushLayer]. |
83 | @protected |
84 | PaintingContext(this._containerLayer, this.estimatedBounds); |
85 | |
86 | final ContainerLayer _containerLayer; |
87 | |
88 | /// An estimate of the bounds within which the painting context's [canvas] |
89 | /// will record painting commands. This can be useful for debugging. |
90 | /// |
91 | /// The canvas will allow painting outside these bounds. |
92 | /// |
93 | /// The [estimatedBounds] rectangle is in the [canvas] coordinate system. |
94 | final Rect estimatedBounds; |
95 | |
96 | /// Repaint the given render object. |
97 | /// |
98 | /// The render object must be attached to a [PipelineOwner], must have a |
99 | /// composited layer, and must be in need of painting. The render object's |
100 | /// layer, if any, is re-used, along with any layers in the subtree that don't |
101 | /// need to be repainted. |
102 | /// |
103 | /// See also: |
104 | /// |
105 | /// * [RenderObject.isRepaintBoundary], which determines if a [RenderObject] |
106 | /// has a composited layer. |
107 | static void repaintCompositedChild(RenderObject child, { bool debugAlsoPaintedParent = false }) { |
108 | assert(child._needsPaint); |
109 | _repaintCompositedChild( |
110 | child, |
111 | debugAlsoPaintedParent: debugAlsoPaintedParent, |
112 | ); |
113 | } |
114 | |
115 | static void _repaintCompositedChild( |
116 | RenderObject child, { |
117 | bool debugAlsoPaintedParent = false, |
118 | PaintingContext? childContext, |
119 | }) { |
120 | assert(child.isRepaintBoundary); |
121 | assert(() { |
122 | // register the call for RepaintBoundary metrics |
123 | child.debugRegisterRepaintBoundaryPaint( |
124 | includedParent: debugAlsoPaintedParent, |
125 | includedChild: true, |
126 | ); |
127 | return true; |
128 | }()); |
129 | OffsetLayer? childLayer = child._layerHandle.layer as OffsetLayer?; |
130 | if (childLayer == null) { |
131 | assert(debugAlsoPaintedParent); |
132 | assert(child._layerHandle.layer == null); |
133 | |
134 | // Not using the `layer` setter because the setter asserts that we not |
135 | // replace the layer for repaint boundaries. That assertion does not |
136 | // apply here because this is exactly the place designed to create a |
137 | // layer for repaint boundaries. |
138 | final OffsetLayer layer = child.updateCompositedLayer(oldLayer: null); |
139 | child._layerHandle.layer = childLayer = layer; |
140 | } else { |
141 | assert(debugAlsoPaintedParent || childLayer.attached); |
142 | Offset? debugOldOffset; |
143 | assert(() { |
144 | debugOldOffset = childLayer!.offset; |
145 | return true; |
146 | }()); |
147 | childLayer.removeAllChildren(); |
148 | final OffsetLayer updatedLayer = child.updateCompositedLayer(oldLayer: childLayer); |
149 | assert(identical(updatedLayer, childLayer), |
150 | ' $child created a new layer instance $updatedLayer instead of reusing the ' |
151 | 'existing layer $childLayer. See the documentation of RenderObject.updateCompositedLayer ' |
152 | 'for more information on how to correctly implement this method.' |
153 | ); |
154 | assert(debugOldOffset == updatedLayer.offset); |
155 | } |
156 | child._needsCompositedLayerUpdate = false; |
157 | |
158 | assert(identical(childLayer, child._layerHandle.layer)); |
159 | assert(child._layerHandle.layer is OffsetLayer); |
160 | assert(() { |
161 | childLayer!.debugCreator = child.debugCreator ?? child.runtimeType; |
162 | return true; |
163 | }()); |
164 | |
165 | childContext ??= PaintingContext(childLayer, child.paintBounds); |
166 | child._paintWithContext(childContext, Offset.zero); |
167 | |
168 | // Double-check that the paint method did not replace the layer (the first |
169 | // check is done in the [layer] setter itself). |
170 | assert(identical(childLayer, child._layerHandle.layer)); |
171 | childContext.stopRecordingIfNeeded(); |
172 | } |
173 | |
174 | /// Update the composited layer of [child] without repainting its children. |
175 | /// |
176 | /// The render object must be attached to a [PipelineOwner], must have a |
177 | /// composited layer, and must be in need of a composited layer update but |
178 | /// not in need of painting. The render object's layer is re-used, and none |
179 | /// of its children are repaint or their layers updated. |
180 | /// |
181 | /// See also: |
182 | /// |
183 | /// * [RenderObject.isRepaintBoundary], which determines if a [RenderObject] |
184 | /// has a composited layer. |
185 | static void updateLayerProperties(RenderObject child) { |
186 | assert(child.isRepaintBoundary && child._wasRepaintBoundary); |
187 | assert(!child._needsPaint); |
188 | assert(child._layerHandle.layer != null); |
189 | |
190 | final OffsetLayer childLayer = child._layerHandle.layer! as OffsetLayer; |
191 | Offset? debugOldOffset; |
192 | assert(() { |
193 | debugOldOffset = childLayer.offset; |
194 | return true; |
195 | }()); |
196 | final OffsetLayer updatedLayer = child.updateCompositedLayer(oldLayer: childLayer); |
197 | assert(identical(updatedLayer, childLayer), |
198 | ' $child created a new layer instance $updatedLayer instead of reusing the ' |
199 | 'existing layer $childLayer. See the documentation of RenderObject.updateCompositedLayer ' |
200 | 'for more information on how to correctly implement this method.' |
201 | ); |
202 | assert(debugOldOffset == updatedLayer.offset); |
203 | child._needsCompositedLayerUpdate = false; |
204 | } |
205 | |
206 | /// In debug mode, repaint the given render object using a custom painting |
207 | /// context that can record the results of the painting operation in addition |
208 | /// to performing the regular paint of the child. |
209 | /// |
210 | /// See also: |
211 | /// |
212 | /// * [repaintCompositedChild], for repainting a composited child without |
213 | /// instrumentation. |
214 | static void debugInstrumentRepaintCompositedChild( |
215 | RenderObject child, { |
216 | bool debugAlsoPaintedParent = false, |
217 | required PaintingContext customContext, |
218 | }) { |
219 | assert(() { |
220 | _repaintCompositedChild( |
221 | child, |
222 | debugAlsoPaintedParent: debugAlsoPaintedParent, |
223 | childContext: customContext, |
224 | ); |
225 | return true; |
226 | }()); |
227 | } |
228 | |
229 | /// Paint a child [RenderObject]. |
230 | /// |
231 | /// If the child has its own composited layer, the child will be composited |
232 | /// into the layer subtree associated with this painting context. Otherwise, |
233 | /// the child will be painted into the current PictureLayer for this context. |
234 | void paintChild(RenderObject child, Offset offset) { |
235 | assert(() { |
236 | debugOnProfilePaint?.call(child); |
237 | return true; |
238 | }()); |
239 | |
240 | if (child.isRepaintBoundary) { |
241 | stopRecordingIfNeeded(); |
242 | _compositeChild(child, offset); |
243 | // If a render object was a repaint boundary but no longer is one, this |
244 | // is where the framework managed layer is automatically disposed. |
245 | } else if (child._wasRepaintBoundary) { |
246 | assert(child._layerHandle.layer is OffsetLayer); |
247 | child._layerHandle.layer = null; |
248 | child._paintWithContext(this, offset); |
249 | } else { |
250 | child._paintWithContext(this, offset); |
251 | } |
252 | } |
253 | |
254 | void _compositeChild(RenderObject child, Offset offset) { |
255 | assert(!_isRecording); |
256 | assert(child.isRepaintBoundary); |
257 | assert(_canvas == null || _canvas!.getSaveCount() == 1); |
258 | |
259 | // Create a layer for our child, and paint the child into it. |
260 | if (child._needsPaint || !child._wasRepaintBoundary) { |
261 | repaintCompositedChild(child, debugAlsoPaintedParent: true); |
262 | } else { |
263 | if (child._needsCompositedLayerUpdate) { |
264 | updateLayerProperties(child); |
265 | } |
266 | assert(() { |
267 | // register the call for RepaintBoundary metrics |
268 | child.debugRegisterRepaintBoundaryPaint(); |
269 | child._layerHandle.layer!.debugCreator = child.debugCreator ?? child; |
270 | return true; |
271 | }()); |
272 | } |
273 | assert(child._layerHandle.layer is OffsetLayer); |
274 | final OffsetLayer childOffsetLayer = child._layerHandle.layer! as OffsetLayer; |
275 | childOffsetLayer.offset = offset; |
276 | appendLayer(childOffsetLayer); |
277 | } |
278 | |
279 | /// Adds a layer to the recording requiring that the recording is already |
280 | /// stopped. |
281 | /// |
282 | /// Do not call this function directly: call [addLayer] or [pushLayer] |
283 | /// instead. This function is called internally when all layers not |
284 | /// generated from the [canvas] are added. |
285 | /// |
286 | /// Subclasses that need to customize how layers are added should override |
287 | /// this method. |
288 | @protected |
289 | void appendLayer(Layer layer) { |
290 | assert(!_isRecording); |
291 | layer.remove(); |
292 | _containerLayer.append(layer); |
293 | } |
294 | |
295 | bool get _isRecording { |
296 | final bool hasCanvas = _canvas != null; |
297 | assert(() { |
298 | if (hasCanvas) { |
299 | assert(_currentLayer != null); |
300 | assert(_recorder != null); |
301 | assert(_canvas != null); |
302 | } else { |
303 | assert(_currentLayer == null); |
304 | assert(_recorder == null); |
305 | assert(_canvas == null); |
306 | } |
307 | return true; |
308 | }()); |
309 | return hasCanvas; |
310 | } |
311 | |
312 | // Recording state |
313 | PictureLayer? _currentLayer; |
314 | ui.PictureRecorder? _recorder; |
315 | Canvas? _canvas; |
316 | |
317 | /// The canvas on which to paint. |
318 | /// |
319 | /// The current canvas can change whenever you paint a child using this |
320 | /// context, which means it's fragile to hold a reference to the canvas |
321 | /// returned by this getter. |
322 | @override |
323 | Canvas get canvas { |
324 | if (_canvas == null) { |
325 | _startRecording(); |
326 | } |
327 | assert(_currentLayer != null); |
328 | return _canvas!; |
329 | } |
330 | |
331 | void _startRecording() { |
332 | assert(!_isRecording); |
333 | _currentLayer = PictureLayer(estimatedBounds); |
334 | _recorder = ui.PictureRecorder(); |
335 | _canvas = Canvas(_recorder!); |
336 | _containerLayer.append(_currentLayer!); |
337 | } |
338 | |
339 | /// Adds a [CompositionCallback] for the current [ContainerLayer] used by this |
340 | /// context. |
341 | /// |
342 | /// Composition callbacks are called whenever the layer tree containing the |
343 | /// current layer of this painting context gets composited, or when it gets |
344 | /// detached and will not be rendered again. This happens regardless of |
345 | /// whether the layer is added via retained rendering or not. |
346 | /// |
347 | /// {@macro flutter.rendering.Layer.compositionCallbacks} |
348 | /// |
349 | /// See also: |
350 | /// * [Layer.addCompositionCallback]. |
351 | VoidCallback addCompositionCallback(CompositionCallback callback) { |
352 | return _containerLayer.addCompositionCallback(callback); |
353 | } |
354 | |
355 | /// Stop recording to a canvas if recording has started. |
356 | /// |
357 | /// Do not call this function directly: functions in this class will call |
358 | /// this method as needed. This function is called internally to ensure that |
359 | /// recording is stopped before adding layers or finalizing the results of a |
360 | /// paint. |
361 | /// |
362 | /// Subclasses that need to customize how recording to a canvas is performed |
363 | /// should override this method to save the results of the custom canvas |
364 | /// recordings. |
365 | @protected |
366 | @mustCallSuper |
367 | void stopRecordingIfNeeded() { |
368 | if (!_isRecording) { |
369 | return; |
370 | } |
371 | assert(() { |
372 | if (debugRepaintRainbowEnabled) { |
373 | final Paint paint = Paint() |
374 | ..style = PaintingStyle.stroke |
375 | ..strokeWidth = 6.0 |
376 | ..color = debugCurrentRepaintColor.toColor(); |
377 | canvas.drawRect(estimatedBounds.deflate(3.0), paint); |
378 | } |
379 | if (debugPaintLayerBordersEnabled) { |
380 | final Paint paint = Paint() |
381 | ..style = PaintingStyle.stroke |
382 | ..strokeWidth = 1.0 |
383 | ..color = const Color(0xFFFF9800); |
384 | canvas.drawRect(estimatedBounds, paint); |
385 | } |
386 | return true; |
387 | }()); |
388 | _currentLayer!.picture = _recorder!.endRecording(); |
389 | _currentLayer = null; |
390 | _recorder = null; |
391 | _canvas = null; |
392 | } |
393 | |
394 | /// Hints that the painting in the current layer is complex and would benefit |
395 | /// from caching. |
396 | /// |
397 | /// If this hint is not set, the compositor will apply its own heuristics to |
398 | /// decide whether the current layer is complex enough to benefit from |
399 | /// caching. |
400 | /// |
401 | /// Calling this ensures a [Canvas] is available. Only draw calls on the |
402 | /// current canvas will be hinted; the hint is not propagated to new canvases |
403 | /// created after a new layer is added to the painting context (e.g. with |
404 | /// [addLayer] or [pushLayer]). |
405 | void setIsComplexHint() { |
406 | if (_currentLayer == null) { |
407 | _startRecording(); |
408 | } |
409 | _currentLayer!.isComplexHint = true; |
410 | } |
411 | |
412 | /// Hints that the painting in the current layer is likely to change next frame. |
413 | /// |
414 | /// This hint tells the compositor not to cache the current layer because the |
415 | /// cache will not be used in the future. If this hint is not set, the |
416 | /// compositor will apply its own heuristics to decide whether the current |
417 | /// layer is likely to be reused in the future. |
418 | /// |
419 | /// Calling this ensures a [Canvas] is available. Only draw calls on the |
420 | /// current canvas will be hinted; the hint is not propagated to new canvases |
421 | /// created after a new layer is added to the painting context (e.g. with |
422 | /// [addLayer] or [pushLayer]). |
423 | void setWillChangeHint() { |
424 | if (_currentLayer == null) { |
425 | _startRecording(); |
426 | } |
427 | _currentLayer!.willChangeHint = true; |
428 | } |
429 | |
430 | /// Adds a composited leaf layer to the recording. |
431 | /// |
432 | /// After calling this function, the [canvas] property will change to refer to |
433 | /// a new [Canvas] that draws on top of the given layer. |
434 | /// |
435 | /// A [RenderObject] that uses this function is very likely to require its |
436 | /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs |
437 | /// ancestor render objects that this render object will include a composited |
438 | /// layer, which, for example, causes them to use composited clips. |
439 | /// |
440 | /// See also: |
441 | /// |
442 | /// * [pushLayer], for adding a layer and painting further contents within |
443 | /// it. |
444 | void addLayer(Layer layer) { |
445 | stopRecordingIfNeeded(); |
446 | appendLayer(layer); |
447 | } |
448 | |
449 | /// Appends the given layer to the recording, and calls the `painter` callback |
450 | /// with that layer, providing the `childPaintBounds` as the estimated paint |
451 | /// bounds of the child. The `childPaintBounds` can be used for debugging but |
452 | /// have no effect on painting. |
453 | /// |
454 | /// The given layer must be an unattached orphan. (Providing a newly created |
455 | /// object, rather than reusing an existing layer, satisfies that |
456 | /// requirement.) |
457 | /// |
458 | /// {@template flutter.rendering.PaintingContext.pushLayer.offset} |
459 | /// The `offset` is the offset to pass to the `painter`. In particular, it is |
460 | /// not an offset applied to the layer itself. Layers conceptually by default |
461 | /// have no position or size, though they can transform their contents. For |
462 | /// example, an [OffsetLayer] applies an offset to its children. |
463 | /// {@endtemplate} |
464 | /// |
465 | /// If the `childPaintBounds` are not specified then the current layer's paint |
466 | /// bounds are used. This is appropriate if the child layer does not apply any |
467 | /// transformation or clipping to its contents. The `childPaintBounds`, if |
468 | /// specified, must be in the coordinate system of the new layer (i.e. as seen |
469 | /// by its children after it applies whatever transform to its contents), and |
470 | /// should not go outside the current layer's paint bounds. |
471 | /// |
472 | /// See also: |
473 | /// |
474 | /// * [addLayer], for pushing a layer without painting further contents |
475 | /// within it. |
476 | void pushLayer(ContainerLayer childLayer, PaintingContextCallback painter, Offset offset, { Rect? childPaintBounds }) { |
477 | // If a layer is being reused, it may already contain children. We remove |
478 | // them so that `painter` can add children that are relevant for this frame. |
479 | if (childLayer.hasChildren) { |
480 | childLayer.removeAllChildren(); |
481 | } |
482 | stopRecordingIfNeeded(); |
483 | appendLayer(childLayer); |
484 | final PaintingContext childContext = createChildContext(childLayer, childPaintBounds ?? estimatedBounds); |
485 | |
486 | painter(childContext, offset); |
487 | childContext.stopRecordingIfNeeded(); |
488 | } |
489 | |
490 | /// Creates a painting context configured to paint into [childLayer]. |
491 | /// |
492 | /// The `bounds` are estimated paint bounds for debugging purposes. |
493 | @protected |
494 | PaintingContext createChildContext(ContainerLayer childLayer, Rect bounds) { |
495 | return PaintingContext(childLayer, bounds); |
496 | } |
497 | |
498 | /// Clip further painting using a rectangle. |
499 | /// |
500 | /// {@template flutter.rendering.PaintingContext.pushClipRect.needsCompositing} |
501 | /// The `needsCompositing` argument specifies whether the child needs |
502 | /// compositing. Typically this matches the value of |
503 | /// [RenderObject.needsCompositing] for the caller. If false, this method |
504 | /// returns null, indicating that a layer is no longer necessary. If a render |
505 | /// object calling this method stores the `oldLayer` in its |
506 | /// [RenderObject.layer] field, it should set that field to null. |
507 | /// |
508 | /// When `needsCompositing` is false, this method will use a more efficient |
509 | /// way to apply the layer effect than actually creating a layer. |
510 | /// {@endtemplate} |
511 | /// |
512 | /// {@template flutter.rendering.PaintingContext.pushClipRect.offset} |
513 | /// The `offset` argument is the offset from the origin of the canvas' |
514 | /// coordinate system to the origin of the caller's coordinate system. |
515 | /// {@endtemplate} |
516 | /// |
517 | /// The `clipRect` is the rectangle (in the caller's coordinate system) to use |
518 | /// to clip the painting done by [painter]. It should not include the |
519 | /// `offset`. |
520 | /// |
521 | /// The `painter` callback will be called while the `clipRect` is applied. It |
522 | /// is called synchronously during the call to [pushClipRect]. |
523 | /// |
524 | /// The `clipBehavior` argument controls how the rectangle is clipped. |
525 | /// |
526 | /// {@template flutter.rendering.PaintingContext.pushClipRect.oldLayer} |
527 | /// For the `oldLayer` argument, specify the layer created in the previous |
528 | /// frame. This gives the engine more information for performance |
529 | /// optimizations. Typically this is the value of [RenderObject.layer] that a |
530 | /// render object creates once, then reuses for all subsequent frames until a |
531 | /// layer is no longer needed (e.g. the render object no longer needs |
532 | /// compositing) or until the render object changes the type of the layer |
533 | /// (e.g. from opacity layer to a clip rect layer). |
534 | /// {@endtemplate} |
535 | ClipRectLayer? pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.hardEdge, ClipRectLayer? oldLayer }) { |
536 | if (clipBehavior == Clip.none) { |
537 | painter(this, offset); |
538 | return null; |
539 | } |
540 | final Rect offsetClipRect = clipRect.shift(offset); |
541 | if (needsCompositing) { |
542 | final ClipRectLayer layer = oldLayer ?? ClipRectLayer(); |
543 | layer |
544 | ..clipRect = offsetClipRect |
545 | ..clipBehavior = clipBehavior; |
546 | pushLayer(layer, painter, offset, childPaintBounds: offsetClipRect); |
547 | return layer; |
548 | } else { |
549 | clipRectAndPaint(offsetClipRect, clipBehavior, offsetClipRect, () => painter(this, offset)); |
550 | return null; |
551 | } |
552 | } |
553 | |
554 | /// Clip further painting using a rounded rectangle. |
555 | /// |
556 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.needsCompositing} |
557 | /// |
558 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.offset} |
559 | /// |
560 | /// The `bounds` argument is used to specify the region of the canvas (in the |
561 | /// caller's coordinate system) into which `painter` will paint. |
562 | /// |
563 | /// The `clipRRect` argument specifies the rounded-rectangle (in the caller's |
564 | /// coordinate system) to use to clip the painting done by `painter`. It |
565 | /// should not include the `offset`. |
566 | /// |
567 | /// The `painter` callback will be called while the `clipRRect` is applied. It |
568 | /// is called synchronously during the call to [pushClipRRect]. |
569 | /// |
570 | /// The `clipBehavior` argument controls how the rounded rectangle is clipped. |
571 | /// |
572 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer} |
573 | ClipRRectLayer? pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.antiAlias, ClipRRectLayer? oldLayer }) { |
574 | if (clipBehavior == Clip.none) { |
575 | painter(this, offset); |
576 | return null; |
577 | } |
578 | final Rect offsetBounds = bounds.shift(offset); |
579 | final RRect offsetClipRRect = clipRRect.shift(offset); |
580 | if (needsCompositing) { |
581 | final ClipRRectLayer layer = oldLayer ?? ClipRRectLayer(); |
582 | layer |
583 | ..clipRRect = offsetClipRRect |
584 | ..clipBehavior = clipBehavior; |
585 | pushLayer(layer, painter, offset, childPaintBounds: offsetBounds); |
586 | return layer; |
587 | } else { |
588 | clipRRectAndPaint(offsetClipRRect, clipBehavior, offsetBounds, () => painter(this, offset)); |
589 | return null; |
590 | } |
591 | } |
592 | |
593 | /// Clip further painting using a path. |
594 | /// |
595 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.needsCompositing} |
596 | /// |
597 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.offset} |
598 | /// |
599 | /// The `bounds` argument is used to specify the region of the canvas (in the |
600 | /// caller's coordinate system) into which `painter` will paint. |
601 | /// |
602 | /// The `clipPath` argument specifies the [Path] (in the caller's coordinate |
603 | /// system) to use to clip the painting done by `painter`. It should not |
604 | /// include the `offset`. |
605 | /// |
606 | /// The `painter` callback will be called while the `clipPath` is applied. It |
607 | /// is called synchronously during the call to [pushClipPath]. |
608 | /// |
609 | /// The `clipBehavior` argument controls how the path is clipped. |
610 | /// |
611 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer} |
612 | ClipPathLayer? pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter, { Clip clipBehavior = Clip.antiAlias, ClipPathLayer? oldLayer }) { |
613 | if (clipBehavior == Clip.none) { |
614 | painter(this, offset); |
615 | return null; |
616 | } |
617 | final Rect offsetBounds = bounds.shift(offset); |
618 | final Path offsetClipPath = clipPath.shift(offset); |
619 | if (needsCompositing) { |
620 | final ClipPathLayer layer = oldLayer ?? ClipPathLayer(); |
621 | layer |
622 | ..clipPath = offsetClipPath |
623 | ..clipBehavior = clipBehavior; |
624 | pushLayer(layer, painter, offset, childPaintBounds: offsetBounds); |
625 | return layer; |
626 | } else { |
627 | clipPathAndPaint(offsetClipPath, clipBehavior, offsetBounds, () => painter(this, offset)); |
628 | return null; |
629 | } |
630 | } |
631 | |
632 | /// Blend further painting with a color filter. |
633 | /// |
634 | /// {@macro flutter.rendering.PaintingContext.pushLayer.offset} |
635 | /// |
636 | /// The `colorFilter` argument is the [ColorFilter] value to use when blending |
637 | /// the painting done by `painter`. |
638 | /// |
639 | /// The `painter` callback will be called while the `colorFilter` is applied. |
640 | /// It is called synchronously during the call to [pushColorFilter]. |
641 | /// |
642 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer} |
643 | /// |
644 | /// A [RenderObject] that uses this function is very likely to require its |
645 | /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs |
646 | /// ancestor render objects that this render object will include a composited |
647 | /// layer, which, for example, causes them to use composited clips. |
648 | ColorFilterLayer pushColorFilter(Offset offset, ColorFilter colorFilter, PaintingContextCallback painter, { ColorFilterLayer? oldLayer }) { |
649 | final ColorFilterLayer layer = oldLayer ?? ColorFilterLayer(); |
650 | layer.colorFilter = colorFilter; |
651 | pushLayer(layer, painter, offset); |
652 | return layer; |
653 | } |
654 | |
655 | /// Transform further painting using a matrix. |
656 | /// |
657 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.needsCompositing} |
658 | /// |
659 | /// The `offset` argument is the offset to pass to `painter` and the offset to |
660 | /// the origin used by `transform`. |
661 | /// |
662 | /// The `transform` argument is the [Matrix4] with which to transform the |
663 | /// coordinate system while calling `painter`. It should not include `offset`. |
664 | /// It is applied effectively after applying `offset`. |
665 | /// |
666 | /// The `painter` callback will be called while the `transform` is applied. It |
667 | /// is called synchronously during the call to [pushTransform]. |
668 | /// |
669 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer} |
670 | TransformLayer? pushTransform(bool needsCompositing, Offset offset, Matrix4 transform, PaintingContextCallback painter, { TransformLayer? oldLayer }) { |
671 | final Matrix4 effectiveTransform = Matrix4.translationValues(offset.dx, offset.dy, 0.0) |
672 | ..multiply(transform)..translate(-offset.dx, -offset.dy); |
673 | if (needsCompositing) { |
674 | final TransformLayer layer = oldLayer ?? TransformLayer(); |
675 | layer.transform = effectiveTransform; |
676 | pushLayer( |
677 | layer, |
678 | painter, |
679 | offset, |
680 | childPaintBounds: MatrixUtils.inverseTransformRect(effectiveTransform, estimatedBounds), |
681 | ); |
682 | return layer; |
683 | } else { |
684 | canvas |
685 | ..save() |
686 | ..transform(effectiveTransform.storage); |
687 | painter(this, offset); |
688 | canvas.restore(); |
689 | return null; |
690 | } |
691 | } |
692 | |
693 | /// Blend further painting with an alpha value. |
694 | /// |
695 | /// The `offset` argument indicates an offset to apply to all the children |
696 | /// (the rendering created by `painter`). |
697 | /// |
698 | /// The `alpha` argument is the alpha value to use when blending the painting |
699 | /// done by `painter`. An alpha value of 0 means the painting is fully |
700 | /// transparent and an alpha value of 255 means the painting is fully opaque. |
701 | /// |
702 | /// The `painter` callback will be called while the `alpha` is applied. It |
703 | /// is called synchronously during the call to [pushOpacity]. |
704 | /// |
705 | /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer} |
706 | /// |
707 | /// A [RenderObject] that uses this function is very likely to require its |
708 | /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs |
709 | /// ancestor render objects that this render object will include a composited |
710 | /// layer, which, for example, causes them to use composited clips. |
711 | OpacityLayer pushOpacity(Offset offset, int alpha, PaintingContextCallback painter, { OpacityLayer? oldLayer }) { |
712 | final OpacityLayer layer = oldLayer ?? OpacityLayer(); |
713 | layer |
714 | ..alpha = alpha |
715 | ..offset = offset; |
716 | pushLayer(layer, painter, Offset.zero); |
717 | return layer; |
718 | } |
719 | |
720 | @override |
721 | String toString() => ' ${objectRuntimeType(this, 'PaintingContext' )}# $hashCode(layer: $_containerLayer, canvas bounds: $estimatedBounds)' ; |
722 | } |
723 | |
724 | /// An abstract set of layout constraints. |
725 | /// |
726 | /// Concrete layout models (such as box) will create concrete subclasses to |
727 | /// communicate layout constraints between parents and children. |
728 | /// |
729 | /// ## Writing a Constraints subclass |
730 | /// |
731 | /// When creating a new [RenderObject] subclass with a new layout protocol, one |
732 | /// will usually need to create a new [Constraints] subclass to express the |
733 | /// input to the layout algorithms. |
734 | /// |
735 | /// A [Constraints] subclass should be immutable (all fields final). There are |
736 | /// several members to implement, in addition to whatever fields, constructors, |
737 | /// and helper methods one may find useful for a particular layout protocol: |
738 | /// |
739 | /// * The [isTight] getter, which should return true if the object represents a |
740 | /// case where the [RenderObject] class has no choice for how to lay itself |
741 | /// out. For example, [BoxConstraints] returns true for [isTight] when both |
742 | /// the minimum and maximum widths and the minimum and maximum heights are |
743 | /// equal. |
744 | /// |
745 | /// * The [isNormalized] getter, which should return true if the object |
746 | /// represents its data in its canonical form. Sometimes, it is possible for |
747 | /// fields to be redundant with each other, such that several different |
748 | /// representations have the same implications. For example, a |
749 | /// [BoxConstraints] instance with its minimum width greater than its maximum |
750 | /// width is equivalent to one where the maximum width is set to that minimum |
751 | /// width (`2<w<1` is equivalent to `2<w<2`, since minimum constraints have |
752 | /// priority). This getter is used by the default implementation of |
753 | /// [debugAssertIsValid]. |
754 | /// |
755 | /// * The [debugAssertIsValid] method, which should assert if there's anything |
756 | /// wrong with the constraints object. (We use this approach rather than |
757 | /// asserting in constructors so that our constructors can be `const` and so |
758 | /// that it is possible to create invalid constraints temporarily while |
759 | /// building valid ones.) See the implementation of |
760 | /// [BoxConstraints.debugAssertIsValid] for an example of the detailed checks |
761 | /// that can be made. |
762 | /// |
763 | /// * The [==] operator and the [hashCode] getter, so that constraints can be |
764 | /// compared for equality. If a render object is given constraints that are |
765 | /// equal, then the rendering library will avoid laying the object out again |
766 | /// if it is not dirty. |
767 | /// |
768 | /// * The [toString] method, which should describe the constraints so that they |
769 | /// appear in a usefully readable form in the output of [debugDumpRenderTree]. |
770 | @immutable |
771 | abstract class Constraints { |
772 | /// Abstract const constructor. This constructor enables subclasses to provide |
773 | /// const constructors so that they can be used in const expressions. |
774 | const Constraints(); |
775 | |
776 | /// Whether there is exactly one size possible given these constraints. |
777 | bool get isTight; |
778 | |
779 | /// Whether the constraint is expressed in a consistent manner. |
780 | bool get isNormalized; |
781 | |
782 | /// Asserts that the constraints are valid. |
783 | /// |
784 | /// This might involve checks more detailed than [isNormalized]. |
785 | /// |
786 | /// For example, the [BoxConstraints] subclass verifies that the constraints |
787 | /// are not [double.nan]. |
788 | /// |
789 | /// If the `isAppliedConstraint` argument is true, then even stricter rules |
790 | /// are enforced. This argument is set to true when checking constraints that |
791 | /// are about to be applied to a [RenderObject] during layout, as opposed to |
792 | /// constraints that may be further affected by other constraints. For |
793 | /// example, the asserts for verifying the validity of |
794 | /// [RenderConstrainedBox.additionalConstraints] do not set this argument, but |
795 | /// the asserts for verifying the argument passed to the [RenderObject.layout] |
796 | /// method do. |
797 | /// |
798 | /// The `informationCollector` argument takes an optional callback which is |
799 | /// called when an exception is to be thrown. The collected information is |
800 | /// then included in the message after the error line. |
801 | /// |
802 | /// Returns the same as [isNormalized] if asserts are disabled. |
803 | bool debugAssertIsValid({ |
804 | bool isAppliedConstraint = false, |
805 | InformationCollector? informationCollector, |
806 | }) { |
807 | assert(isNormalized); |
808 | return isNormalized; |
809 | } |
810 | } |
811 | |
812 | /// Signature for a function that is called for each [RenderObject]. |
813 | /// |
814 | /// Used by [RenderObject.visitChildren] and [RenderObject.visitChildrenForSemantics]. |
815 | typedef RenderObjectVisitor = void Function(RenderObject child); |
816 | |
817 | /// Signature for a function that is called during layout. |
818 | /// |
819 | /// Used by [RenderObject.invokeLayoutCallback]. |
820 | typedef LayoutCallback<T extends Constraints> = void Function(T constraints); |
821 | |
822 | class _LocalSemanticsHandle implements SemanticsHandle { |
823 | _LocalSemanticsHandle._(PipelineOwner owner, this.listener) |
824 | : _owner = owner { |
825 | // TODO(polina-c): stop duplicating code across disposables |
826 | // https://github.com/flutter/flutter/issues/137435 |
827 | if (kFlutterMemoryAllocationsEnabled) { |
828 | FlutterMemoryAllocations.instance.dispatchObjectCreated( |
829 | library: 'package:flutter/rendering.dart' , |
830 | className: ' $_LocalSemanticsHandle' , |
831 | object: this, |
832 | ); |
833 | } |
834 | |
835 | if (listener != null) { |
836 | _owner.semanticsOwner!.addListener(listener!); |
837 | } |
838 | } |
839 | |
840 | final PipelineOwner _owner; |
841 | |
842 | /// The callback that will be notified when the semantics tree updates. |
843 | final VoidCallback? listener; |
844 | |
845 | @override |
846 | void dispose() { |
847 | // TODO(polina-c): stop duplicating code across disposables |
848 | // https://github.com/flutter/flutter/issues/137435 |
849 | if (kFlutterMemoryAllocationsEnabled) { |
850 | FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); |
851 | } |
852 | |
853 | if (listener != null) { |
854 | _owner.semanticsOwner!.removeListener(listener!); |
855 | } |
856 | _owner._didDisposeSemanticsHandle(); |
857 | } |
858 | } |
859 | |
860 | /// The pipeline owner manages the rendering pipeline. |
861 | /// |
862 | /// The pipeline owner provides an interface for driving the rendering pipeline |
863 | /// and stores the state about which render objects have requested to be visited |
864 | /// in each stage of the pipeline. To flush the pipeline, call the following |
865 | /// functions in order: |
866 | /// |
867 | /// 1. [flushLayout] updates any render objects that need to compute their |
868 | /// layout. During this phase, the size and position of each render |
869 | /// object is calculated. Render objects might dirty their painting or |
870 | /// compositing state during this phase. |
871 | /// 2. [flushCompositingBits] updates any render objects that have dirty |
872 | /// compositing bits. During this phase, each render object learns whether |
873 | /// any of its children require compositing. This information is used during |
874 | /// the painting phase when selecting how to implement visual effects such as |
875 | /// clipping. If a render object has a composited child, it needs to use a |
876 | /// [Layer] to create the clip in order for the clip to apply to the |
877 | /// composited child (which will be painted into its own [Layer]). |
878 | /// 3. [flushPaint] visits any render objects that need to paint. During this |
879 | /// phase, render objects get a chance to record painting commands into |
880 | /// [PictureLayer]s and construct other composited [Layer]s. |
881 | /// 4. Finally, if semantics are enabled, [flushSemantics] will compile the |
882 | /// semantics for the render objects. This semantic information is used by |
883 | /// assistive technology to improve the accessibility of the render tree. |
884 | /// |
885 | /// The [RendererBinding] holds the pipeline owner for the render objects that |
886 | /// are visible on screen. You can create other pipeline owners to manage |
887 | /// off-screen objects, which can flush their pipelines independently of the |
888 | /// on-screen render objects. |
889 | /// |
890 | /// [PipelineOwner]s can be organized in a tree to manage multiple render trees, |
891 | /// where each [PipelineOwner] is responsible for one of the render trees. To |
892 | /// build or modify the tree, call [adoptChild] or [dropChild]. During each of |
893 | /// the different flush phases described above, a [PipelineOwner] will first |
894 | /// perform the phase on the nodes it manages in its own render tree before |
895 | /// calling the same flush method on its children. No assumption must be made |
896 | /// about the order in which child [PipelineOwner]s are flushed. |
897 | /// |
898 | /// A [PipelineOwner] may also be [attach]ed to a [PipelineManifold], which |
899 | /// gives it access to platform functionality usually exposed by the bindings |
900 | /// without tying it to a specific binding implementation. All [PipelineOwner]s |
901 | /// in a given tree must be attached to the same [PipelineManifold]. This |
902 | /// happens automatically during [adoptChild]. |
903 | class PipelineOwner with DiagnosticableTreeMixin { |
904 | /// Creates a pipeline owner. |
905 | /// |
906 | /// Typically created by the binding (e.g., [RendererBinding]), but can be |
907 | /// created separately from the binding to drive off-screen render objects |
908 | /// through the rendering pipeline. |
909 | PipelineOwner({ |
910 | this.onNeedVisualUpdate, |
911 | this.onSemanticsOwnerCreated, |
912 | this.onSemanticsUpdate, |
913 | this.onSemanticsOwnerDisposed, |
914 | }){ |
915 | // TODO(polina-c): stop duplicating code across disposables |
916 | // https://github.com/flutter/flutter/issues/137435 |
917 | if (kFlutterMemoryAllocationsEnabled) { |
918 | FlutterMemoryAllocations.instance.dispatchObjectCreated( |
919 | library: 'package:flutter/rendering.dart' , |
920 | className: ' $PipelineOwner' , |
921 | object: this, |
922 | ); |
923 | } |
924 | } |
925 | |
926 | /// Called when a render object associated with this pipeline owner wishes to |
927 | /// update its visual appearance. |
928 | /// |
929 | /// Typical implementations of this function will schedule a task to flush the |
930 | /// various stages of the pipeline. This function might be called multiple |
931 | /// times in quick succession. Implementations should take care to discard |
932 | /// duplicate calls quickly. |
933 | /// |
934 | /// When the [PipelineOwner] is attached to a [PipelineManifold] and |
935 | /// [onNeedVisualUpdate] is provided, the [onNeedVisualUpdate] callback is |
936 | /// invoked instead of calling [PipelineManifold.requestVisualUpdate]. |
937 | final VoidCallback? onNeedVisualUpdate; |
938 | |
939 | /// Called whenever this pipeline owner creates a semantics object. |
940 | /// |
941 | /// Typical implementations will schedule the creation of the initial |
942 | /// semantics tree. |
943 | final VoidCallback? onSemanticsOwnerCreated; |
944 | |
945 | /// Called whenever this pipeline owner's semantics owner emits a [SemanticsUpdate]. |
946 | /// |
947 | /// Typical implementations will delegate the [SemanticsUpdate] to a [FlutterView] |
948 | /// that can handle the [SemanticsUpdate]. |
949 | final SemanticsUpdateCallback? onSemanticsUpdate; |
950 | |
951 | /// Called whenever this pipeline owner disposes its semantics owner. |
952 | /// |
953 | /// Typical implementations will tear down the semantics tree. |
954 | final VoidCallback? onSemanticsOwnerDisposed; |
955 | |
956 | /// Calls [onNeedVisualUpdate] if [onNeedVisualUpdate] is not null. |
957 | /// |
958 | /// Used to notify the pipeline owner that an associated render object wishes |
959 | /// to update its visual appearance. |
960 | void requestVisualUpdate() { |
961 | if (onNeedVisualUpdate != null) { |
962 | onNeedVisualUpdate!(); |
963 | } else { |
964 | _manifold?.requestVisualUpdate(); |
965 | } |
966 | } |
967 | |
968 | /// The unique object managed by this pipeline that has no parent. |
969 | RenderObject? get rootNode => _rootNode; |
970 | RenderObject? _rootNode; |
971 | set rootNode(RenderObject? value) { |
972 | if (_rootNode == value) { |
973 | return; |
974 | } |
975 | _rootNode?.detach(); |
976 | _rootNode = value; |
977 | _rootNode?.attach(this); |
978 | } |
979 | |
980 | // Whether the current [flushLayout] call should pause to incorporate the |
981 | // [RenderObject]s in `_nodesNeedingLayout` into the current dirty list, |
982 | // before continuing to process dirty relayout boundaries. |
983 | // |
984 | // This flag is set to true when a [RenderObject.invokeLayoutCallback] |
985 | // returns, to avoid laying out dirty relayout boundaries in an incorrect |
986 | // order and causing them to be laid out more than once per frame. See |
987 | // layout_builder_mutations_test.dart for an example. |
988 | // |
989 | // The new dirty nodes are not immediately merged after a |
990 | // [RenderObject.invokeLayoutCallback] call because we may encounter multiple |
991 | // such calls while processing a single relayout boundary in [flushLayout]. |
992 | // Batching new dirty nodes can reduce the number of merges [flushLayout] |
993 | // has to perform. |
994 | bool _shouldMergeDirtyNodes = false; |
995 | List<RenderObject> _nodesNeedingLayout = <RenderObject>[]; |
996 | |
997 | /// Whether this pipeline is currently in the layout phase. |
998 | /// |
999 | /// Specifically, whether [flushLayout] is currently running. |
1000 | /// |
1001 | /// Only valid when asserts are enabled; in release builds, this |
1002 | /// always returns false. |
1003 | bool get debugDoingLayout => _debugDoingLayout; |
1004 | bool _debugDoingLayout = false; |
1005 | bool _debugDoingChildLayout = false; |
1006 | |
1007 | /// Update the layout information for all dirty render objects. |
1008 | /// |
1009 | /// This function is one of the core stages of the rendering pipeline. Layout |
1010 | /// information is cleaned prior to painting so that render objects will |
1011 | /// appear on screen in their up-to-date locations. |
1012 | /// |
1013 | /// See [RendererBinding] for an example of how this function is used. |
1014 | void flushLayout() { |
1015 | if (!kReleaseMode) { |
1016 | Map<String, String>? debugTimelineArguments; |
1017 | assert(() { |
1018 | if (debugEnhanceLayoutTimelineArguments) { |
1019 | debugTimelineArguments = <String, String>{ |
1020 | 'dirty count' : ' ${_nodesNeedingLayout.length}' , |
1021 | 'dirty list' : ' $_nodesNeedingLayout' , |
1022 | }; |
1023 | } |
1024 | return true; |
1025 | }()); |
1026 | FlutterTimeline.startSync( |
1027 | 'LAYOUT $_debugRootSuffixForTimelineEventNames' , |
1028 | arguments: debugTimelineArguments, |
1029 | ); |
1030 | } |
1031 | assert(() { |
1032 | _debugDoingLayout = true; |
1033 | return true; |
1034 | }()); |
1035 | try { |
1036 | while (_nodesNeedingLayout.isNotEmpty) { |
1037 | assert(!_shouldMergeDirtyNodes); |
1038 | final List<RenderObject> dirtyNodes = _nodesNeedingLayout; |
1039 | _nodesNeedingLayout = <RenderObject>[]; |
1040 | dirtyNodes.sort((RenderObject a, RenderObject b) => a.depth - b.depth); |
1041 | for (int i = 0; i < dirtyNodes.length; i++) { |
1042 | if (_shouldMergeDirtyNodes) { |
1043 | _shouldMergeDirtyNodes = false; |
1044 | if (_nodesNeedingLayout.isNotEmpty) { |
1045 | _nodesNeedingLayout.addAll(dirtyNodes.getRange(i, dirtyNodes.length)); |
1046 | break; |
1047 | } |
1048 | } |
1049 | final RenderObject node = dirtyNodes[i]; |
1050 | if (node._needsLayout && node.owner == this) { |
1051 | node._layoutWithoutResize(); |
1052 | } |
1053 | } |
1054 | // No need to merge dirty nodes generated from processing the last |
1055 | // relayout boundary back. |
1056 | _shouldMergeDirtyNodes = false; |
1057 | } |
1058 | |
1059 | assert(() { |
1060 | _debugDoingChildLayout = true; |
1061 | return true; |
1062 | }()); |
1063 | for (final PipelineOwner child in _children) { |
1064 | child.flushLayout(); |
1065 | } |
1066 | assert(_nodesNeedingLayout.isEmpty, 'Child PipelineOwners must not dirty nodes in their parent.' ); |
1067 | } finally { |
1068 | _shouldMergeDirtyNodes = false; |
1069 | assert(() { |
1070 | _debugDoingLayout = false; |
1071 | _debugDoingChildLayout = false; |
1072 | return true; |
1073 | }()); |
1074 | if (!kReleaseMode) { |
1075 | FlutterTimeline.finishSync(); |
1076 | } |
1077 | } |
1078 | } |
1079 | |
1080 | // This flag is used to allow the kinds of mutations performed by GlobalKey |
1081 | // reparenting while a LayoutBuilder is being rebuilt and in so doing tries to |
1082 | // move a node from another LayoutBuilder subtree that hasn't been updated |
1083 | // yet. To set this, call [_enableMutationsToDirtySubtrees], which is called |
1084 | // by [RenderObject.invokeLayoutCallback]. |
1085 | bool _debugAllowMutationsToDirtySubtrees = false; |
1086 | |
1087 | // See [RenderObject.invokeLayoutCallback]. |
1088 | void _enableMutationsToDirtySubtrees(VoidCallback callback) { |
1089 | assert(_debugDoingLayout); |
1090 | bool? oldState; |
1091 | assert(() { |
1092 | oldState = _debugAllowMutationsToDirtySubtrees; |
1093 | _debugAllowMutationsToDirtySubtrees = true; |
1094 | return true; |
1095 | }()); |
1096 | try { |
1097 | callback(); |
1098 | } finally { |
1099 | _shouldMergeDirtyNodes = true; |
1100 | assert(() { |
1101 | _debugAllowMutationsToDirtySubtrees = oldState!; |
1102 | return true; |
1103 | }()); |
1104 | } |
1105 | } |
1106 | |
1107 | final List<RenderObject> _nodesNeedingCompositingBitsUpdate = <RenderObject>[]; |
1108 | /// Updates the [RenderObject.needsCompositing] bits. |
1109 | /// |
1110 | /// Called as part of the rendering pipeline after [flushLayout] and before |
1111 | /// [flushPaint]. |
1112 | void flushCompositingBits() { |
1113 | if (!kReleaseMode) { |
1114 | FlutterTimeline.startSync('UPDATING COMPOSITING BITS $_debugRootSuffixForTimelineEventNames' ); |
1115 | } |
1116 | _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth); |
1117 | for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) { |
1118 | if (node._needsCompositingBitsUpdate && node.owner == this) { |
1119 | node._updateCompositingBits(); |
1120 | } |
1121 | } |
1122 | _nodesNeedingCompositingBitsUpdate.clear(); |
1123 | for (final PipelineOwner child in _children) { |
1124 | child.flushCompositingBits(); |
1125 | } |
1126 | assert(_nodesNeedingCompositingBitsUpdate.isEmpty, 'Child PipelineOwners must not dirty nodes in their parent.' ); |
1127 | if (!kReleaseMode) { |
1128 | FlutterTimeline.finishSync(); |
1129 | } |
1130 | } |
1131 | |
1132 | List<RenderObject> _nodesNeedingPaint = <RenderObject>[]; |
1133 | |
1134 | /// Whether this pipeline is currently in the paint phase. |
1135 | /// |
1136 | /// Specifically, whether [flushPaint] is currently running. |
1137 | /// |
1138 | /// Only valid when asserts are enabled. In release builds, |
1139 | /// this always returns false. |
1140 | bool get debugDoingPaint => _debugDoingPaint; |
1141 | bool _debugDoingPaint = false; |
1142 | |
1143 | /// Update the display lists for all render objects. |
1144 | /// |
1145 | /// This function is one of the core stages of the rendering pipeline. |
1146 | /// Painting occurs after layout and before the scene is recomposited so that |
1147 | /// scene is composited with up-to-date display lists for every render object. |
1148 | /// |
1149 | /// See [RendererBinding] for an example of how this function is used. |
1150 | void flushPaint() { |
1151 | if (!kReleaseMode) { |
1152 | Map<String, String>? debugTimelineArguments; |
1153 | assert(() { |
1154 | if (debugEnhancePaintTimelineArguments) { |
1155 | debugTimelineArguments = <String, String>{ |
1156 | 'dirty count' : ' ${_nodesNeedingPaint.length}' , |
1157 | 'dirty list' : ' $_nodesNeedingPaint' , |
1158 | }; |
1159 | } |
1160 | return true; |
1161 | }()); |
1162 | FlutterTimeline.startSync( |
1163 | 'PAINT $_debugRootSuffixForTimelineEventNames' , |
1164 | arguments: debugTimelineArguments, |
1165 | ); |
1166 | } |
1167 | try { |
1168 | assert(() { |
1169 | _debugDoingPaint = true; |
1170 | return true; |
1171 | }()); |
1172 | final List<RenderObject> dirtyNodes = _nodesNeedingPaint; |
1173 | _nodesNeedingPaint = <RenderObject>[]; |
1174 | |
1175 | // Sort the dirty nodes in reverse order (deepest first). |
1176 | for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) { |
1177 | assert(node._layerHandle.layer != null); |
1178 | if ((node._needsPaint || node._needsCompositedLayerUpdate) && node.owner == this) { |
1179 | if (node._layerHandle.layer!.attached) { |
1180 | assert(node.isRepaintBoundary); |
1181 | if (node._needsPaint) { |
1182 | PaintingContext.repaintCompositedChild(node); |
1183 | } else { |
1184 | PaintingContext.updateLayerProperties(node); |
1185 | } |
1186 | } else { |
1187 | node._skippedPaintingOnLayer(); |
1188 | } |
1189 | } |
1190 | } |
1191 | for (final PipelineOwner child in _children) { |
1192 | child.flushPaint(); |
1193 | } |
1194 | assert(_nodesNeedingPaint.isEmpty, 'Child PipelineOwners must not dirty nodes in their parent.' ); |
1195 | } finally { |
1196 | assert(() { |
1197 | _debugDoingPaint = false; |
1198 | return true; |
1199 | }()); |
1200 | if (!kReleaseMode) { |
1201 | FlutterTimeline.finishSync(); |
1202 | } |
1203 | } |
1204 | } |
1205 | |
1206 | /// The object that is managing semantics for this pipeline owner, if any. |
1207 | /// |
1208 | /// An owner is created by [ensureSemantics] or when the [PipelineManifold] to |
1209 | /// which this owner is connected has [PipelineManifold.semanticsEnabled] set |
1210 | /// to true. The owner is valid for as long as |
1211 | /// [PipelineManifold.semanticsEnabled] remains true or while there are |
1212 | /// outstanding [SemanticsHandle]s from calls to [ensureSemantics]. The |
1213 | /// [semanticsOwner] field will revert to null once both conditions are no |
1214 | /// longer met. |
1215 | /// |
1216 | /// When [semanticsOwner] is null, the [PipelineOwner] skips all steps |
1217 | /// relating to semantics. |
1218 | SemanticsOwner? get semanticsOwner => _semanticsOwner; |
1219 | SemanticsOwner? _semanticsOwner; |
1220 | |
1221 | /// The number of clients registered to listen for semantics. |
1222 | /// |
1223 | /// The number is increased whenever [ensureSemantics] is called and decreased |
1224 | /// when [SemanticsHandle.dispose] is called. |
1225 | int get debugOutstandingSemanticsHandles => _outstandingSemanticsHandles; |
1226 | int _outstandingSemanticsHandles = 0; |
1227 | |
1228 | /// Opens a [SemanticsHandle] and calls [listener] whenever the semantics tree |
1229 | /// generated from the render tree owned by this [PipelineOwner] updates. |
1230 | /// |
1231 | /// Calling this method only ensures that this particular [PipelineOwner] will |
1232 | /// generate a semantics tree. Consider calling |
1233 | /// [SemanticsBinding.ensureSemantics] instead to turn on semantics globally |
1234 | /// for the entire app. |
1235 | /// |
1236 | /// The [PipelineOwner] updates the semantics tree only when there are clients |
1237 | /// that wish to use the semantics tree. These clients express their interest |
1238 | /// by holding [SemanticsHandle] objects that notify them whenever the |
1239 | /// semantics tree updates. |
1240 | /// |
1241 | /// Clients can close their [SemanticsHandle] by calling |
1242 | /// [SemanticsHandle.dispose]. Once all the outstanding [SemanticsHandle] |
1243 | /// objects for a given [PipelineOwner] are closed, the [PipelineOwner] stops |
1244 | /// maintaining the semantics tree. |
1245 | SemanticsHandle ensureSemantics({ VoidCallback? listener }) { |
1246 | _outstandingSemanticsHandles += 1; |
1247 | _updateSemanticsOwner(); |
1248 | return _LocalSemanticsHandle._(this, listener); |
1249 | } |
1250 | |
1251 | void _updateSemanticsOwner() { |
1252 | if ((_manifold?.semanticsEnabled ?? false) || _outstandingSemanticsHandles > 0) { |
1253 | if (_semanticsOwner == null) { |
1254 | assert(onSemanticsUpdate != null, 'Attempted to enable semantics without configuring an onSemanticsUpdate callback.' ); |
1255 | _semanticsOwner = SemanticsOwner(onSemanticsUpdate: onSemanticsUpdate!); |
1256 | onSemanticsOwnerCreated?.call(); |
1257 | } |
1258 | } else if (_semanticsOwner != null) { |
1259 | _semanticsOwner?.dispose(); |
1260 | _semanticsOwner = null; |
1261 | onSemanticsOwnerDisposed?.call(); |
1262 | } |
1263 | } |
1264 | |
1265 | void _didDisposeSemanticsHandle() { |
1266 | assert(_semanticsOwner != null); |
1267 | _outstandingSemanticsHandles -= 1; |
1268 | _updateSemanticsOwner(); |
1269 | } |
1270 | |
1271 | bool _debugDoingSemantics = false; |
1272 | final Set<RenderObject> _nodesNeedingSemantics = <RenderObject>{}; |
1273 | |
1274 | /// Update the semantics for render objects marked as needing a semantics |
1275 | /// update. |
1276 | /// |
1277 | /// Initially, only the root node, as scheduled by |
1278 | /// [RenderObject.scheduleInitialSemantics], needs a semantics update. |
1279 | /// |
1280 | /// This function is one of the core stages of the rendering pipeline. The |
1281 | /// semantics are compiled after painting and only after |
1282 | /// [RenderObject.scheduleInitialSemantics] has been called. |
1283 | /// |
1284 | /// See [RendererBinding] for an example of how this function is used. |
1285 | void flushSemantics() { |
1286 | if (_semanticsOwner == null) { |
1287 | return; |
1288 | } |
1289 | if (!kReleaseMode) { |
1290 | FlutterTimeline.startSync('SEMANTICS $_debugRootSuffixForTimelineEventNames' ); |
1291 | } |
1292 | assert(_semanticsOwner != null); |
1293 | assert(() { |
1294 | _debugDoingSemantics = true; |
1295 | return true; |
1296 | }()); |
1297 | try { |
1298 | final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList() |
1299 | ..sort((RenderObject a, RenderObject b) => a.depth - b.depth); |
1300 | _nodesNeedingSemantics.clear(); |
1301 | for (final RenderObject node in nodesToProcess) { |
1302 | if (node._needsSemanticsUpdate && node.owner == this) { |
1303 | node._updateSemantics(); |
1304 | } |
1305 | } |
1306 | _semanticsOwner!.sendSemanticsUpdate(); |
1307 | for (final PipelineOwner child in _children) { |
1308 | child.flushSemantics(); |
1309 | } |
1310 | assert(_nodesNeedingSemantics.isEmpty, 'Child PipelineOwners must not dirty nodes in their parent.' ); |
1311 | } finally { |
1312 | assert(() { |
1313 | _debugDoingSemantics = false; |
1314 | return true; |
1315 | }()); |
1316 | if (!kReleaseMode) { |
1317 | FlutterTimeline.finishSync(); |
1318 | } |
1319 | } |
1320 | } |
1321 | |
1322 | @override |
1323 | List<DiagnosticsNode> debugDescribeChildren() { |
1324 | return <DiagnosticsNode>[ |
1325 | for (final PipelineOwner child in _children) |
1326 | child.toDiagnosticsNode(), |
1327 | ]; |
1328 | } |
1329 | |
1330 | @override |
1331 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1332 | super.debugFillProperties(properties); |
1333 | properties.add(DiagnosticsProperty<RenderObject>('rootNode' , rootNode, defaultValue: null)); |
1334 | } |
1335 | |
1336 | // TREE MANAGEMENT |
1337 | |
1338 | final Set<PipelineOwner> _children = <PipelineOwner>{}; |
1339 | PipelineManifold? _manifold; |
1340 | |
1341 | PipelineOwner? _debugParent; |
1342 | bool _debugSetParent(PipelineOwner child, PipelineOwner? parent) { |
1343 | child._debugParent = parent; |
1344 | return true; |
1345 | } |
1346 | |
1347 | String get _debugRootSuffixForTimelineEventNames => _debugParent == null ? ' (root)' : '' ; |
1348 | |
1349 | /// Mark this [PipelineOwner] as attached to the given [PipelineManifold]. |
1350 | /// |
1351 | /// Typically, this is only called directly on the root [PipelineOwner]. |
1352 | /// Children are automatically attached to their parent's [PipelineManifold] |
1353 | /// when [adoptChild] is called. |
1354 | void attach(PipelineManifold manifold) { |
1355 | assert(_manifold == null); |
1356 | _manifold = manifold; |
1357 | _manifold!.addListener(_updateSemanticsOwner); |
1358 | _updateSemanticsOwner(); |
1359 | |
1360 | for (final PipelineOwner child in _children) { |
1361 | child.attach(manifold); |
1362 | } |
1363 | } |
1364 | |
1365 | /// Mark this [PipelineOwner] as detached. |
1366 | /// |
1367 | /// Typically, this is only called directly on the root [PipelineOwner]. |
1368 | /// Children are automatically detached from their parent's [PipelineManifold] |
1369 | /// when [dropChild] is called. |
1370 | void detach() { |
1371 | assert(_manifold != null); |
1372 | _manifold!.removeListener(_updateSemanticsOwner); |
1373 | _manifold = null; |
1374 | // Not updating the semantics owner here to not disrupt any of its clients |
1375 | // in case we get re-attached. If necessary, semantics owner will be updated |
1376 | // in "attach", or disposed in "dispose", if not reattached. |
1377 | |
1378 | for (final PipelineOwner child in _children) { |
1379 | child.detach(); |
1380 | } |
1381 | } |
1382 | |
1383 | // In theory, child list modifications are also disallowed between |
1384 | // _debugDoingChildrenLayout and _debugDoingPaint as well as between |
1385 | // _debugDoingPaint and _debugDoingSemantics. However, since the associated |
1386 | // flush methods are usually called back to back, this gets us close enough. |
1387 | bool get _debugAllowChildListModifications => !_debugDoingChildLayout && !_debugDoingPaint && !_debugDoingSemantics; |
1388 | |
1389 | /// Adds `child` to this [PipelineOwner]. |
1390 | /// |
1391 | /// During the phases of frame production (see [RendererBinding.drawFrame]), |
1392 | /// the parent [PipelineOwner] will complete a phase for the nodes it owns |
1393 | /// directly before invoking the flush method corresponding to the current |
1394 | /// phase on its child [PipelineOwner]s. For example, during layout, the |
1395 | /// parent [PipelineOwner] will first lay out its own nodes before calling |
1396 | /// [flushLayout] on its children. During paint, it will first paint its own |
1397 | /// nodes before calling [flushPaint] on its children. This order also applies |
1398 | /// for all the other phases. |
1399 | /// |
1400 | /// No assumptions must be made about the order in which child |
1401 | /// [PipelineOwner]s are flushed. |
1402 | /// |
1403 | /// No new children may be added after the [PipelineOwner] has started calling |
1404 | /// [flushLayout] on any of its children until the end of the current frame. |
1405 | /// |
1406 | /// To remove a child, call [dropChild]. |
1407 | void adoptChild(PipelineOwner child) { |
1408 | assert(child._debugParent == null); |
1409 | assert(!_children.contains(child)); |
1410 | assert(_debugAllowChildListModifications, 'Cannot modify child list after layout.' ); |
1411 | _children.add(child); |
1412 | if (!kReleaseMode) { |
1413 | _debugSetParent(child, this); |
1414 | } |
1415 | if (_manifold != null) { |
1416 | child.attach(_manifold!); |
1417 | } |
1418 | } |
1419 | |
1420 | /// Removes a child [PipelineOwner] previously added via [adoptChild]. |
1421 | /// |
1422 | /// This node will cease to call the flush methods on the `child` during frame |
1423 | /// production. |
1424 | /// |
1425 | /// No children may be removed after the [PipelineOwner] has started calling |
1426 | /// [flushLayout] on any of its children until the end of the current frame. |
1427 | void dropChild(PipelineOwner child) { |
1428 | assert(child._debugParent == this); |
1429 | assert(_children.contains(child)); |
1430 | assert(_debugAllowChildListModifications, 'Cannot modify child list after layout.' ); |
1431 | _children.remove(child); |
1432 | if (!kReleaseMode) { |
1433 | _debugSetParent(child, null); |
1434 | } |
1435 | if (_manifold != null) { |
1436 | child.detach(); |
1437 | } |
1438 | } |
1439 | |
1440 | /// Calls `visitor` for each immediate child of this [PipelineOwner]. |
1441 | /// |
1442 | /// See also: |
1443 | /// |
1444 | /// * [adoptChild] to add a child. |
1445 | /// * [dropChild] to remove a child. |
1446 | void visitChildren(PipelineOwnerVisitor visitor) { |
1447 | _children.forEach(visitor); |
1448 | } |
1449 | |
1450 | /// Release any resources held by this pipeline owner. |
1451 | /// |
1452 | /// Prior to calling this method the pipeline owner must be removed from the |
1453 | /// pipeline owner tree, i.e. it must have neither a parent nor any children |
1454 | /// (see [dropChild]). It also must be [detach]ed from any [PipelineManifold]. |
1455 | /// |
1456 | /// The object is no longer usable after calling dispose. |
1457 | void dispose() { |
1458 | assert(_children.isEmpty); |
1459 | assert(rootNode == null); |
1460 | assert(_manifold == null); |
1461 | assert(_debugParent == null); |
1462 | if (kFlutterMemoryAllocationsEnabled) { |
1463 | FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); |
1464 | } |
1465 | _semanticsOwner?.dispose(); |
1466 | _semanticsOwner = null; |
1467 | _nodesNeedingLayout.clear(); |
1468 | _nodesNeedingCompositingBitsUpdate.clear(); |
1469 | _nodesNeedingPaint.clear(); |
1470 | _nodesNeedingSemantics.clear(); |
1471 | } |
1472 | } |
1473 | |
1474 | /// Signature for the callback to [PipelineOwner.visitChildren]. |
1475 | /// |
1476 | /// The argument is the child being visited. |
1477 | typedef PipelineOwnerVisitor = void Function(PipelineOwner child); |
1478 | |
1479 | /// Manages a tree of [PipelineOwner]s. |
1480 | /// |
1481 | /// All [PipelineOwner]s within a tree are attached to the same |
1482 | /// [PipelineManifold], which gives them access to shared functionality such |
1483 | /// as requesting a visual update (by calling [requestVisualUpdate]). As such, |
1484 | /// the [PipelineManifold] gives the [PipelineOwner]s access to functionality |
1485 | /// usually provided by the bindings without tying the [PipelineOwner]s to a |
1486 | /// particular binding implementation. |
1487 | /// |
1488 | /// The root of the [PipelineOwner] tree is attached to a [PipelineManifold] by |
1489 | /// passing the manifold to [PipelineOwner.attach]. Children are attached to the |
1490 | /// same [PipelineManifold] as their parent when they are adopted via |
1491 | /// [PipelineOwner.adoptChild]. |
1492 | /// |
1493 | /// [PipelineOwner]s can register listeners with the [PipelineManifold] to be |
1494 | /// informed when certain values provided by the [PipelineManifold] change. |
1495 | abstract class PipelineManifold implements Listenable { |
1496 | /// Whether [PipelineOwner]s connected to this [PipelineManifold] should |
1497 | /// collect semantics information and produce a semantics tree. |
1498 | /// |
1499 | /// The [PipelineManifold] notifies its listeners (managed with [addListener] |
1500 | /// and [removeListener]) when this property changes its value. |
1501 | /// |
1502 | /// See also: |
1503 | /// |
1504 | /// * [SemanticsBinding.semanticsEnabled], which [PipelineManifold] |
1505 | /// implementations typically use to back this property. |
1506 | bool get semanticsEnabled; |
1507 | |
1508 | /// Called by a [PipelineOwner] connected to this [PipelineManifold] when a |
1509 | /// [RenderObject] associated with that pipeline owner wishes to update its |
1510 | /// visual appearance. |
1511 | /// |
1512 | /// Typical implementations of this function will schedule a task to flush the |
1513 | /// various stages of the pipeline. This function might be called multiple |
1514 | /// times in quick succession. Implementations should take care to discard |
1515 | /// duplicate calls quickly. |
1516 | /// |
1517 | /// A [PipelineOwner] connected to this [PipelineManifold] will call |
1518 | /// [PipelineOwner.onNeedVisualUpdate] instead of this method if it has been |
1519 | /// configured with a non-null [PipelineOwner.onNeedVisualUpdate] callback. |
1520 | /// |
1521 | /// See also: |
1522 | /// |
1523 | /// * [SchedulerBinding.ensureVisualUpdate], which [PipelineManifold] |
1524 | /// implementations typically call to implement this method. |
1525 | void requestVisualUpdate(); |
1526 | } |
1527 | |
1528 | const String _flutterRenderingLibrary = 'package:flutter/rendering.dart' ; |
1529 | |
1530 | /// An object in the render tree. |
1531 | /// |
1532 | /// The [RenderObject] class hierarchy is the core of the rendering |
1533 | /// library's reason for being. |
1534 | /// |
1535 | /// {@youtube 560 315 https://www.youtube.com/watch?v=zmbmrw07qBc} |
1536 | /// |
1537 | /// [RenderObject]s have a [parent], and have a slot called [parentData] in |
1538 | /// which the parent [RenderObject] can store child-specific data, for example, |
1539 | /// the child position. The [RenderObject] class also implements the basic |
1540 | /// layout and paint protocols. |
1541 | /// |
1542 | /// The [RenderObject] class, however, does not define a child model (e.g. |
1543 | /// whether a node has zero, one, or more children). It also doesn't define a |
1544 | /// coordinate system (e.g. whether children are positioned in Cartesian |
1545 | /// coordinates, in polar coordinates, etc) or a specific layout protocol (e.g. |
1546 | /// whether the layout is width-in-height-out, or constraint-in-size-out, or |
1547 | /// whether the parent sets the size and position of the child before or after |
1548 | /// the child lays out, etc; or indeed whether the children are allowed to read |
1549 | /// their parent's [parentData] slot). |
1550 | /// |
1551 | /// The [RenderBox] subclass introduces the opinion that the layout |
1552 | /// system uses Cartesian coordinates. |
1553 | /// |
1554 | /// ## Lifecycle |
1555 | /// |
1556 | /// A [RenderObject] must [dispose] when it is no longer needed. The creator |
1557 | /// of the object is responsible for disposing of it. Typically, the creator is |
1558 | /// a [RenderObjectElement], and that element will dispose the object it creates |
1559 | /// when it is unmounted. |
1560 | /// |
1561 | /// [RenderObject]s are responsible for cleaning up any expensive resources |
1562 | /// they hold when [dispose] is called, such as [Picture] or [Image] objects. |
1563 | /// This includes any [Layer]s that the render object has directly created. The |
1564 | /// base implementation of dispose will nullify the [layer] property. Subclasses |
1565 | /// must also nullify any other layer(s) it directly creates. |
1566 | /// |
1567 | /// ## Writing a RenderObject subclass |
1568 | /// |
1569 | /// In most cases, subclassing [RenderObject] itself is overkill, and |
1570 | /// [RenderBox] would be a better starting point. However, if a render object |
1571 | /// doesn't want to use a Cartesian coordinate system, then it should indeed |
1572 | /// inherit from [RenderObject] directly. This allows it to define its own |
1573 | /// layout protocol by using a new subclass of [Constraints] rather than using |
1574 | /// [BoxConstraints], and by potentially using an entirely new set of objects |
1575 | /// and values to represent the result of the output rather than just a [Size]. |
1576 | /// This increased flexibility comes at the cost of not being able to rely on |
1577 | /// the features of [RenderBox]. For example, [RenderBox] implements an |
1578 | /// intrinsic sizing protocol that allows you to measure a child without fully |
1579 | /// laying it out, in such a way that if that child changes size, the parent |
1580 | /// will be laid out again (to take into account the new dimensions of the |
1581 | /// child). This is a subtle and bug-prone feature to get right. |
1582 | /// |
1583 | /// Most aspects of writing a [RenderBox] apply to writing a [RenderObject] as |
1584 | /// well, and therefore the discussion at [RenderBox] is recommended background |
1585 | /// reading. The main differences are around layout and hit testing, since those |
1586 | /// are the aspects that [RenderBox] primarily specializes. |
1587 | /// |
1588 | /// ### Layout |
1589 | /// |
1590 | /// A layout protocol begins with a subclass of [Constraints]. See the |
1591 | /// discussion at [Constraints] for more information on how to write a |
1592 | /// [Constraints] subclass. |
1593 | /// |
1594 | /// The [performLayout] method should take the [constraints], and apply them. |
1595 | /// The output of the layout algorithm is fields set on the object that describe |
1596 | /// the geometry of the object for the purposes of the parent's layout. For |
1597 | /// example, with [RenderBox] the output is the [RenderBox.size] field. This |
1598 | /// output should only be read by the parent if the parent specified |
1599 | /// `parentUsesSize` as true when calling [layout] on the child. |
1600 | /// |
1601 | /// Anytime anything changes on a render object that would affect the layout of |
1602 | /// that object, it should call [markNeedsLayout]. |
1603 | /// |
1604 | /// ### Hit Testing |
1605 | /// |
1606 | /// Hit testing is even more open-ended than layout. There is no method to |
1607 | /// override, you are expected to provide one. |
1608 | /// |
1609 | /// The general behavior of your hit-testing method should be similar to the |
1610 | /// behavior described for [RenderBox]. The main difference is that the input |
1611 | /// need not be an [Offset]. You are also allowed to use a different subclass of |
1612 | /// [HitTestEntry] when adding entries to the [HitTestResult]. When the |
1613 | /// [handleEvent] method is called, the same object that was added to the |
1614 | /// [HitTestResult] will be passed in, so it can be used to track information |
1615 | /// like the precise coordinate of the hit, in whatever coordinate system is |
1616 | /// used by the new layout protocol. |
1617 | /// |
1618 | /// ### Adapting from one protocol to another |
1619 | /// |
1620 | /// In general, the root of a Flutter render object tree is a [RenderView]. This |
1621 | /// object has a single child, which must be a [RenderBox]. Thus, if you want to |
1622 | /// have a custom [RenderObject] subclass in the render tree, you have two |
1623 | /// choices: you either need to replace the [RenderView] itself, or you need to |
1624 | /// have a [RenderBox] that has your class as its child. (The latter is the much |
1625 | /// more common case.) |
1626 | /// |
1627 | /// This [RenderBox] subclass converts from the box protocol to the protocol of |
1628 | /// your class. |
1629 | /// |
1630 | /// In particular, this means that for hit testing it overrides |
1631 | /// [RenderBox.hitTest], and calls whatever method you have in your class for |
1632 | /// hit testing. |
1633 | /// |
1634 | /// Similarly, it overrides [performLayout] to create a [Constraints] object |
1635 | /// appropriate for your class and passes that to the child's [layout] method. |
1636 | /// |
1637 | /// ### Layout interactions between render objects |
1638 | /// |
1639 | /// In general, the layout of a render object should only depend on the output of |
1640 | /// its child's layout, and then only if `parentUsesSize` is set to true in the |
1641 | /// [layout] call. Furthermore, if it is set to true, the parent must call the |
1642 | /// child's [layout] if the child is to be rendered, because otherwise the |
1643 | /// parent will not be notified when the child changes its layout outputs. |
1644 | /// |
1645 | /// It is possible to set up render object protocols that transfer additional |
1646 | /// information. For example, in the [RenderBox] protocol you can query your |
1647 | /// children's intrinsic dimensions and baseline geometry. However, if this is |
1648 | /// done then it is imperative that the child call [markNeedsLayout] on the |
1649 | /// parent any time that additional information changes, if the parent used it |
1650 | /// in the last layout phase. For an example of how to implement this, see the |
1651 | /// [RenderBox.markNeedsLayout] method. It overrides |
1652 | /// [RenderObject.markNeedsLayout] so that if a parent has queried the intrinsic |
1653 | /// or baseline information, it gets marked dirty whenever the child's geometry |
1654 | /// changes. |
1655 | abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarget { |
1656 | /// Initializes internal fields for subclasses. |
1657 | RenderObject() { |
1658 | if (kFlutterMemoryAllocationsEnabled) { |
1659 | FlutterMemoryAllocations.instance.dispatchObjectCreated( |
1660 | library: _flutterRenderingLibrary, |
1661 | className: ' $RenderObject' , |
1662 | object: this, |
1663 | ); |
1664 | } |
1665 | _needsCompositing = isRepaintBoundary || alwaysNeedsCompositing; |
1666 | _wasRepaintBoundary = isRepaintBoundary; |
1667 | } |
1668 | |
1669 | /// Cause the entire subtree rooted at the given [RenderObject] to be marked |
1670 | /// dirty for layout, paint, etc, so that the effects of a hot reload can be |
1671 | /// seen, or so that the effect of changing a global debug flag (such as |
1672 | /// [debugPaintSizeEnabled]) can be applied. |
1673 | /// |
1674 | /// This is called by the [RendererBinding] in response to the |
1675 | /// `ext.flutter.reassemble` hook, which is used by development tools when the |
1676 | /// application code has changed, to cause the widget tree to pick up any |
1677 | /// changed implementations. |
1678 | /// |
1679 | /// This is expensive and should not be called except during development. |
1680 | /// |
1681 | /// See also: |
1682 | /// |
1683 | /// * [BindingBase.reassembleApplication] |
1684 | void reassemble() { |
1685 | markNeedsLayout(); |
1686 | markNeedsCompositingBitsUpdate(); |
1687 | markNeedsPaint(); |
1688 | markNeedsSemanticsUpdate(); |
1689 | visitChildren((RenderObject child) { |
1690 | child.reassemble(); |
1691 | }); |
1692 | } |
1693 | |
1694 | /// Whether this has been disposed. |
1695 | /// |
1696 | /// If asserts are disabled, this property is always null. |
1697 | bool? get debugDisposed { |
1698 | bool? disposed; |
1699 | assert(() { |
1700 | disposed = _debugDisposed; |
1701 | return true; |
1702 | }()); |
1703 | return disposed; |
1704 | } |
1705 | |
1706 | bool _debugDisposed = false; |
1707 | |
1708 | /// Release any resources held by this render object. |
1709 | /// |
1710 | /// The object that creates a RenderObject is in charge of disposing it. |
1711 | /// If this render object has created any children directly, it must dispose |
1712 | /// of those children in this method as well. It must not dispose of any |
1713 | /// children that were created by some other object, such as |
1714 | /// a [RenderObjectElement]. Those children will be disposed when that |
1715 | /// element unmounts, which may be delayed if the element is moved to another |
1716 | /// part of the tree. |
1717 | /// |
1718 | /// Implementations of this method must end with a call to the inherited |
1719 | /// method, as in `super.dispose()`. |
1720 | /// |
1721 | /// The object is no longer usable after calling dispose. |
1722 | @mustCallSuper |
1723 | void dispose() { |
1724 | assert(!_debugDisposed); |
1725 | if (kFlutterMemoryAllocationsEnabled) { |
1726 | FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); |
1727 | } |
1728 | _layerHandle.layer = null; |
1729 | assert(() { |
1730 | // TODO(dnfield): Enable this assert once clients have had a chance to |
1731 | // migrate. |
1732 | // visitChildren((RenderObject child) { |
1733 | // assert( |
1734 | // child.debugDisposed!, |
1735 | // '${child.runtimeType} (child of $runtimeType) must be disposed before calling super.dispose().', |
1736 | // ); |
1737 | // }); |
1738 | _debugDisposed = true; |
1739 | return true; |
1740 | }()); |
1741 | } |
1742 | |
1743 | // LAYOUT |
1744 | |
1745 | /// Data for use by the parent render object. |
1746 | /// |
1747 | /// The parent data is used by the render object that lays out this object |
1748 | /// (typically this object's parent in the render tree) to store information |
1749 | /// relevant to itself and to any other nodes who happen to know exactly what |
1750 | /// the data means. The parent data is opaque to the child. |
1751 | /// |
1752 | /// * The parent data field must not be directly set, except by calling |
1753 | /// [setupParentData] on the parent node. |
1754 | /// * The parent data can be set before the child is added to the parent, by |
1755 | /// calling [setupParentData] on the future parent node. |
1756 | /// * The conventions for using the parent data depend on the layout protocol |
1757 | /// used between the parent and child. For example, in box layout, the |
1758 | /// parent data is completely opaque but in sector layout the child is |
1759 | /// permitted to read some fields of the parent data. |
1760 | ParentData? parentData; |
1761 | |
1762 | /// Override to setup parent data correctly for your children. |
1763 | /// |
1764 | /// You can call this function to set up the parent data for child before the |
1765 | /// child is added to the parent's child list. |
1766 | void setupParentData(covariant RenderObject child) { |
1767 | assert(_debugCanPerformMutations); |
1768 | if (child.parentData is! ParentData) { |
1769 | child.parentData = ParentData(); |
1770 | } |
1771 | } |
1772 | |
1773 | /// The depth of this render object in the render tree. |
1774 | /// |
1775 | /// The depth of nodes in a tree monotonically increases as you traverse down |
1776 | /// the tree: a node always has a [depth] greater than its ancestors. |
1777 | /// There's no guarantee regarding depth between siblings. |
1778 | /// |
1779 | /// The [depth] of a child can be more than one greater than the [depth] of |
1780 | /// the parent, because the [depth] values are never decreased: all that |
1781 | /// matters is that it's greater than the parent. Consider a tree with a root |
1782 | /// node A, a child B, and a grandchild C. Initially, A will have [depth] 0, |
1783 | /// B [depth] 1, and C [depth] 2. If C is moved to be a child of A, |
1784 | /// sibling of B, then the numbers won't change. C's [depth] will still be 2. |
1785 | /// |
1786 | /// The depth of a node is used to ensure that nodes are processed in |
1787 | /// depth order. The [depth] is automatically maintained by the [adoptChild] |
1788 | /// and [dropChild] methods. |
1789 | int get depth => _depth; |
1790 | int _depth = 0; |
1791 | |
1792 | /// Adjust the [depth] of the given [child] to be greater than this node's own |
1793 | /// [depth]. |
1794 | /// |
1795 | /// Only call this method from overrides of [redepthChildren]. |
1796 | @protected |
1797 | void redepthChild(RenderObject child) { |
1798 | assert(child.owner == owner); |
1799 | if (child._depth <= _depth) { |
1800 | child._depth = _depth + 1; |
1801 | child.redepthChildren(); |
1802 | } |
1803 | } |
1804 | |
1805 | /// Adjust the [depth] of this node's children, if any. |
1806 | /// |
1807 | /// Override this method in subclasses with child nodes to call [redepthChild] |
1808 | /// for each child. Do not call this method directly. |
1809 | @protected |
1810 | void redepthChildren() { } |
1811 | |
1812 | /// The parent of this render object in the render tree. |
1813 | /// |
1814 | /// The [parent] of the root node in the render tree is null. |
1815 | RenderObject? get parent => _parent; |
1816 | RenderObject? _parent; |
1817 | |
1818 | /// Called by subclasses when they decide a render object is a child. |
1819 | /// |
1820 | /// Only for use by subclasses when changing their child lists. Calling this |
1821 | /// in other cases will lead to an inconsistent tree and probably cause crashes. |
1822 | @mustCallSuper |
1823 | @protected |
1824 | void adoptChild(RenderObject child) { |
1825 | assert(child._parent == null); |
1826 | assert(() { |
1827 | RenderObject node = this; |
1828 | while (node.parent != null) { |
1829 | node = node.parent!; |
1830 | } |
1831 | assert(node != child); // indicates we are about to create a cycle |
1832 | return true; |
1833 | }()); |
1834 | |
1835 | setupParentData(child); |
1836 | markNeedsLayout(); |
1837 | markNeedsCompositingBitsUpdate(); |
1838 | markNeedsSemanticsUpdate(); |
1839 | child._parent = this; |
1840 | if (attached) { |
1841 | child.attach(_owner!); |
1842 | } |
1843 | redepthChild(child); |
1844 | } |
1845 | |
1846 | /// Called by subclasses when they decide a render object is no longer a child. |
1847 | /// |
1848 | /// Only for use by subclasses when changing their child lists. Calling this |
1849 | /// in other cases will lead to an inconsistent tree and probably cause crashes. |
1850 | @mustCallSuper |
1851 | @protected |
1852 | void dropChild(RenderObject child) { |
1853 | assert(child._parent == this); |
1854 | assert(child.attached == attached); |
1855 | assert(child.parentData != null); |
1856 | child._cleanRelayoutBoundary(); |
1857 | child.parentData!.detach(); |
1858 | child.parentData = null; |
1859 | child._parent = null; |
1860 | if (attached) { |
1861 | child.detach(); |
1862 | } |
1863 | markNeedsLayout(); |
1864 | markNeedsCompositingBitsUpdate(); |
1865 | markNeedsSemanticsUpdate(); |
1866 | } |
1867 | |
1868 | /// Calls visitor for each immediate child of this render object. |
1869 | /// |
1870 | /// Override in subclasses with children and call the visitor for each child. |
1871 | void visitChildren(RenderObjectVisitor visitor) { } |
1872 | |
1873 | /// The object responsible for creating this render object. |
1874 | /// |
1875 | /// Used in debug messages. |
1876 | /// |
1877 | /// See also: |
1878 | /// |
1879 | /// * [DebugCreator], which the [widgets] library uses as values for this field. |
1880 | Object? debugCreator; |
1881 | |
1882 | void _reportException(String method, Object exception, StackTrace stack) { |
1883 | FlutterError.reportError(FlutterErrorDetails( |
1884 | exception: exception, |
1885 | stack: stack, |
1886 | library: 'rendering library' , |
1887 | context: ErrorDescription('during $method()' ), |
1888 | informationCollector: () => <DiagnosticsNode>[ |
1889 | // debugCreator should always be null outside of debugMode, but we want |
1890 | // the tree shaker to notice this. |
1891 | if (kDebugMode && debugCreator != null) |
1892 | DiagnosticsDebugCreator(debugCreator!), |
1893 | describeForError('The following RenderObject was being processed when the exception was fired' ), |
1894 | // TODO(jacobr): this error message has a code smell. Consider whether |
1895 | // displaying the truncated children is really useful for command line |
1896 | // users. Inspector users can see the full tree by clicking on the |
1897 | // render object so this may not be that useful. |
1898 | describeForError('RenderObject' , style: DiagnosticsTreeStyle.truncateChildren), |
1899 | ], |
1900 | )); |
1901 | } |
1902 | |
1903 | /// Whether [performResize] for this render object is currently running. |
1904 | /// |
1905 | /// Only valid when asserts are enabled. In release builds, always returns |
1906 | /// false. |
1907 | bool get debugDoingThisResize => _debugDoingThisResize; |
1908 | bool _debugDoingThisResize = false; |
1909 | |
1910 | /// Whether [performLayout] for this render object is currently running. |
1911 | /// |
1912 | /// Only valid when asserts are enabled. In release builds, always returns |
1913 | /// false. |
1914 | bool get debugDoingThisLayout => _debugDoingThisLayout; |
1915 | bool _debugDoingThisLayout = false; |
1916 | |
1917 | /// The render object that is actively computing layout. |
1918 | /// |
1919 | /// Only valid when asserts are enabled. In release builds, always returns |
1920 | /// null. |
1921 | static RenderObject? get debugActiveLayout => _debugActiveLayout; |
1922 | static RenderObject? _debugActiveLayout; |
1923 | |
1924 | /// Set [debugActiveLayout] to null when [inner] callback is called. |
1925 | /// This is useful when you have to temporarily clear that variable to |
1926 | /// disable some false-positive checks, such as when computing toStringDeep |
1927 | /// or using custom trees. |
1928 | @pragma('vm:prefer-inline' ) |
1929 | static T _withDebugActiveLayoutCleared<T>(T Function() inner) { |
1930 | RenderObject? debugPreviousActiveLayout; |
1931 | assert(() { |
1932 | debugPreviousActiveLayout = _debugActiveLayout; |
1933 | _debugActiveLayout = null; |
1934 | return true; |
1935 | }()); |
1936 | final T result = inner(); |
1937 | assert(() { |
1938 | _debugActiveLayout = debugPreviousActiveLayout; |
1939 | return true; |
1940 | }()); |
1941 | return result; |
1942 | } |
1943 | |
1944 | /// Whether the parent render object is permitted to use this render object's |
1945 | /// size. |
1946 | /// |
1947 | /// Determined by the `parentUsesSize` parameter to [layout]. |
1948 | /// |
1949 | /// Only valid when asserts are enabled. In release builds, throws. |
1950 | bool get debugCanParentUseSize => _debugCanParentUseSize!; |
1951 | bool? _debugCanParentUseSize; |
1952 | |
1953 | bool _debugMutationsLocked = false; |
1954 | |
1955 | /// Whether tree mutations are currently permitted. |
1956 | /// |
1957 | /// This is only useful during layout. One should also not mutate the tree at |
1958 | /// other times (e.g. during paint or while assembling the semantic tree) but |
1959 | /// this function does not currently enforce those conventions. |
1960 | /// |
1961 | /// Only valid when asserts are enabled. This will throw in release builds. |
1962 | bool get _debugCanPerformMutations { |
1963 | late bool result; |
1964 | assert(() { |
1965 | if (_debugDisposed) { |
1966 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
1967 | ErrorSummary('A disposed RenderObject was mutated.' ), |
1968 | DiagnosticsProperty<RenderObject>( |
1969 | 'The disposed RenderObject was' , |
1970 | this, |
1971 | style: DiagnosticsTreeStyle.errorProperty, |
1972 | ), |
1973 | ]); |
1974 | } |
1975 | |
1976 | final PipelineOwner? owner = this.owner; |
1977 | // Detached nodes are allowed to mutate and the "can perform mutations" |
1978 | // check will be performed when they re-attach. This assert is only useful |
1979 | // during layout. |
1980 | if (owner == null || !owner.debugDoingLayout) { |
1981 | result = true; |
1982 | return true; |
1983 | } |
1984 | |
1985 | RenderObject? activeLayoutRoot = this; |
1986 | while (activeLayoutRoot != null) { |
1987 | final bool mutationsToDirtySubtreesAllowed = activeLayoutRoot.owner?._debugAllowMutationsToDirtySubtrees ?? false; |
1988 | final bool doingLayoutWithCallback = activeLayoutRoot._doingThisLayoutWithCallback; |
1989 | // Mutations on this subtree is allowed when: |
1990 | // - the "activeLayoutRoot" subtree is being mutated in a layout callback. |
1991 | // - a different part of the render tree is doing a layout callback, |
1992 | // and this subtree is being reparented to that subtree, as a result |
1993 | // of global key reparenting. |
1994 | if (doingLayoutWithCallback || mutationsToDirtySubtreesAllowed && activeLayoutRoot._needsLayout) { |
1995 | result = true; |
1996 | return true; |
1997 | } |
1998 | |
1999 | if (!activeLayoutRoot._debugMutationsLocked) { |
2000 | final RenderObject? p = activeLayoutRoot.debugLayoutParent; |
2001 | activeLayoutRoot = p is RenderObject ? p : null; |
2002 | } else { |
2003 | // activeLayoutRoot found. |
2004 | break; |
2005 | } |
2006 | } |
2007 | |
2008 | final RenderObject debugActiveLayout = RenderObject.debugActiveLayout!; |
2009 | final String culpritMethodName = debugActiveLayout.debugDoingThisLayout ? 'performLayout' : 'performResize' ; |
2010 | final String culpritFullMethodName = ' ${debugActiveLayout.runtimeType}. $culpritMethodName' ; |
2011 | result = false; |
2012 | |
2013 | if (activeLayoutRoot == null) { |
2014 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2015 | ErrorSummary('A $runtimeType was mutated in $culpritFullMethodName.' ), |
2016 | ErrorDescription( |
2017 | 'The RenderObject was mutated when none of its ancestors is actively performing layout.' , |
2018 | ), |
2019 | DiagnosticsProperty<RenderObject>( |
2020 | 'The RenderObject being mutated was' , |
2021 | this, |
2022 | style: DiagnosticsTreeStyle.errorProperty, |
2023 | ), |
2024 | DiagnosticsProperty<RenderObject>( |
2025 | 'The RenderObject that was mutating the said $runtimeType was' , |
2026 | debugActiveLayout, |
2027 | style: DiagnosticsTreeStyle.errorProperty, |
2028 | ), |
2029 | ]); |
2030 | } |
2031 | |
2032 | if (activeLayoutRoot == this) { |
2033 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2034 | ErrorSummary('A $runtimeType was mutated in its own $culpritMethodName implementation.' ), |
2035 | ErrorDescription('A RenderObject must not re-dirty itself while still being laid out.' ), |
2036 | DiagnosticsProperty<RenderObject>( |
2037 | 'The RenderObject being mutated was' , |
2038 | this, |
2039 | style: DiagnosticsTreeStyle.errorProperty, |
2040 | ), |
2041 | ErrorHint('Consider using the LayoutBuilder widget to dynamically change a subtree during layout.' ), |
2042 | ]); |
2043 | } |
2044 | |
2045 | final ErrorSummary summary = ErrorSummary('A $runtimeType was mutated in $culpritFullMethodName.' ); |
2046 | final bool isMutatedByAncestor = activeLayoutRoot == debugActiveLayout; |
2047 | final String description = isMutatedByAncestor |
2048 | ? 'A RenderObject must not mutate its descendants in its $culpritMethodName method.' |
2049 | : 'A RenderObject must not mutate another RenderObject from a different render subtree ' |
2050 | 'in its $culpritMethodName method.' ; |
2051 | |
2052 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2053 | summary, |
2054 | ErrorDescription(description), |
2055 | DiagnosticsProperty<RenderObject>( |
2056 | 'The RenderObject being mutated was' , |
2057 | this, |
2058 | style: DiagnosticsTreeStyle.errorProperty, |
2059 | ), |
2060 | DiagnosticsProperty<RenderObject>( |
2061 | 'The ${isMutatedByAncestor ? 'ancestor ' : '' }RenderObject that was mutating the said $runtimeType was' , |
2062 | debugActiveLayout, |
2063 | style: DiagnosticsTreeStyle.errorProperty, |
2064 | ), |
2065 | if (!isMutatedByAncestor) DiagnosticsProperty<RenderObject>( |
2066 | 'Their common ancestor was' , |
2067 | activeLayoutRoot, |
2068 | style: DiagnosticsTreeStyle.errorProperty, |
2069 | ), |
2070 | ErrorHint( |
2071 | 'Mutating the layout of another RenderObject may cause some RenderObjects in its subtree to be laid out more than once. ' |
2072 | 'Consider using the LayoutBuilder widget to dynamically mutate a subtree during layout.' |
2073 | ), |
2074 | ]); |
2075 | }()); |
2076 | return result; |
2077 | } |
2078 | |
2079 | /// The [RenderObject] that's expected to call [layout] on this [RenderObject] |
2080 | /// in its [performLayout] implementation. |
2081 | /// |
2082 | /// This method is used to implement an assert that ensures the render subtree |
2083 | /// actively performing layout can not get accidentally mutated. It's only |
2084 | /// implemented in debug mode and always returns null in release mode. |
2085 | /// |
2086 | /// The default implementation returns [parent] and overriding is rarely |
2087 | /// needed. A [RenderObject] subclass that expects its |
2088 | /// [RenderObject.performLayout] to be called from a different [RenderObject] |
2089 | /// that's not its [parent] should override this property to return the actual |
2090 | /// layout parent. |
2091 | @protected |
2092 | RenderObject? get debugLayoutParent { |
2093 | RenderObject? layoutParent; |
2094 | assert(() { |
2095 | layoutParent = parent; |
2096 | return true; |
2097 | }()); |
2098 | return layoutParent; |
2099 | } |
2100 | |
2101 | /// The owner for this render object (null if unattached). |
2102 | /// |
2103 | /// The entire render tree that this render object belongs to |
2104 | /// will have the same owner. |
2105 | PipelineOwner? get owner => _owner; |
2106 | PipelineOwner? _owner; |
2107 | |
2108 | /// Whether the render tree this render object belongs to is attached to a [PipelineOwner]. |
2109 | /// |
2110 | /// This becomes true during the call to [attach]. |
2111 | /// |
2112 | /// This becomes false during the call to [detach]. |
2113 | bool get attached => _owner != null; |
2114 | |
2115 | /// Mark this render object as attached to the given owner. |
2116 | /// |
2117 | /// Typically called only from the [parent]'s [attach] method, and by the |
2118 | /// [owner] to mark the root of a tree as attached. |
2119 | /// |
2120 | /// Subclasses with children should override this method to |
2121 | /// [attach] all their children to the same [owner] |
2122 | /// after calling the inherited method, as in `super.attach(owner)`. |
2123 | @mustCallSuper |
2124 | void attach(PipelineOwner owner) { |
2125 | assert(!_debugDisposed); |
2126 | assert(_owner == null); |
2127 | _owner = owner; |
2128 | // If the node was dirtied in some way while unattached, make sure to add |
2129 | // it to the appropriate dirty list now that an owner is available |
2130 | if (_needsLayout && _relayoutBoundary != null) { |
2131 | // Don't enter this block if we've never laid out at all; |
2132 | // scheduleInitialLayout() will handle it |
2133 | _needsLayout = false; |
2134 | markNeedsLayout(); |
2135 | } |
2136 | if (_needsCompositingBitsUpdate) { |
2137 | _needsCompositingBitsUpdate = false; |
2138 | markNeedsCompositingBitsUpdate(); |
2139 | } |
2140 | if (_needsPaint && _layerHandle.layer != null) { |
2141 | // Don't enter this block if we've never painted at all; |
2142 | // scheduleInitialPaint() will handle it |
2143 | _needsPaint = false; |
2144 | markNeedsPaint(); |
2145 | } |
2146 | if (_needsSemanticsUpdate && _semanticsConfiguration.isSemanticBoundary) { |
2147 | // Don't enter this block if we've never updated semantics at all; |
2148 | // scheduleInitialSemantics() will handle it |
2149 | _needsSemanticsUpdate = false; |
2150 | markNeedsSemanticsUpdate(); |
2151 | } |
2152 | } |
2153 | |
2154 | /// Mark this render object as detached from its [PipelineOwner]. |
2155 | /// |
2156 | /// Typically called only from the [parent]'s [detach], and by the [owner] to |
2157 | /// mark the root of a tree as detached. |
2158 | /// |
2159 | /// Subclasses with children should override this method to |
2160 | /// [detach] all their children after calling the inherited method, |
2161 | /// as in `super.detach()`. |
2162 | @mustCallSuper |
2163 | void detach() { |
2164 | assert(_owner != null); |
2165 | _owner = null; |
2166 | assert(parent == null || attached == parent!.attached); |
2167 | } |
2168 | |
2169 | /// Whether this render object's layout information is dirty. |
2170 | /// |
2171 | /// This is only set in debug mode. In general, render objects should not need |
2172 | /// to condition their runtime behavior on whether they are dirty or not, |
2173 | /// since they should only be marked dirty immediately prior to being laid |
2174 | /// out and painted. In release builds, this throws. |
2175 | /// |
2176 | /// It is intended to be used by tests and asserts. |
2177 | bool get debugNeedsLayout { |
2178 | late bool result; |
2179 | assert(() { |
2180 | result = _needsLayout; |
2181 | return true; |
2182 | }()); |
2183 | return result; |
2184 | } |
2185 | bool _needsLayout = true; |
2186 | |
2187 | RenderObject? _relayoutBoundary; |
2188 | |
2189 | /// Whether [invokeLayoutCallback] for this render object is currently running. |
2190 | bool get debugDoingThisLayoutWithCallback => _doingThisLayoutWithCallback; |
2191 | bool _doingThisLayoutWithCallback = false; |
2192 | |
2193 | /// The layout constraints most recently supplied by the parent. |
2194 | /// |
2195 | /// If layout has not yet happened, accessing this getter will |
2196 | /// throw a [StateError] exception. |
2197 | @protected |
2198 | Constraints get constraints { |
2199 | if (_constraints == null) { |
2200 | throw StateError('A RenderObject does not have any constraints before it has been laid out.' ); |
2201 | } |
2202 | return _constraints!; |
2203 | } |
2204 | Constraints? _constraints; |
2205 | |
2206 | /// Verify that the object's constraints are being met. Override |
2207 | /// this function in a subclass to verify that your state matches |
2208 | /// the constraints object. This function is only called in checked |
2209 | /// mode and only when needsLayout is false. If the constraints are |
2210 | /// not met, it should assert or throw an exception. |
2211 | @protected |
2212 | void debugAssertDoesMeetConstraints(); |
2213 | |
2214 | /// When true, debugAssertDoesMeetConstraints() is currently |
2215 | /// executing asserts for verifying the consistent behavior of |
2216 | /// intrinsic dimensions methods. |
2217 | /// |
2218 | /// This should only be set by debugAssertDoesMeetConstraints() |
2219 | /// implementations. It is used by tests to selectively ignore |
2220 | /// custom layout callbacks. It should not be set outside of |
2221 | /// debugAssertDoesMeetConstraints(), and should not be checked in |
2222 | /// release mode (where it will always be false). |
2223 | static bool debugCheckingIntrinsics = false; |
2224 | bool _debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout() { |
2225 | if (_relayoutBoundary == null) { |
2226 | // We don't know where our relayout boundary is yet. |
2227 | return true; |
2228 | } |
2229 | RenderObject node = this; |
2230 | while (node != _relayoutBoundary) { |
2231 | assert(node._relayoutBoundary == _relayoutBoundary); |
2232 | assert(node.parent != null); |
2233 | node = node.parent!; |
2234 | if ((!node._needsLayout) && (!node._debugDoingThisLayout)) { |
2235 | return false; |
2236 | } |
2237 | } |
2238 | assert(node._relayoutBoundary == node); |
2239 | return true; |
2240 | } |
2241 | |
2242 | /// Mark this render object's layout information as dirty, and either register |
2243 | /// this object with its [PipelineOwner], or defer to the parent, depending on |
2244 | /// whether this object is a relayout boundary or not respectively. |
2245 | /// |
2246 | /// ## Background |
2247 | /// |
2248 | /// Rather than eagerly updating layout information in response to writes into |
2249 | /// a render object, we instead mark the layout information as dirty, which |
2250 | /// schedules a visual update. As part of the visual update, the rendering |
2251 | /// pipeline updates the render object's layout information. |
2252 | /// |
2253 | /// This mechanism batches the layout work so that multiple sequential writes |
2254 | /// are coalesced, removing redundant computation. |
2255 | /// |
2256 | /// If a render object's parent indicates that it uses the size of one of its |
2257 | /// render object children when computing its layout information, this |
2258 | /// function, when called for the child, will also mark the parent as needing |
2259 | /// layout. In that case, since both the parent and the child need to have |
2260 | /// their layout recomputed, the pipeline owner is only notified about the |
2261 | /// parent; when the parent is laid out, it will call the child's [layout] |
2262 | /// method and thus the child will be laid out as well. |
2263 | /// |
2264 | /// Once [markNeedsLayout] has been called on a render object, |
2265 | /// [debugNeedsLayout] returns true for that render object until just after |
2266 | /// the pipeline owner has called [layout] on the render object. |
2267 | /// |
2268 | /// ## Special cases |
2269 | /// |
2270 | /// Some subclasses of [RenderObject], notably [RenderBox], have other |
2271 | /// situations in which the parent needs to be notified if the child is |
2272 | /// dirtied (e.g., if the child's intrinsic dimensions or baseline changes). |
2273 | /// Such subclasses override markNeedsLayout and either call |
2274 | /// `super.markNeedsLayout()`, in the normal case, or call |
2275 | /// [markParentNeedsLayout], in the case where the parent needs to be laid out |
2276 | /// as well as the child. |
2277 | /// |
2278 | /// If [sizedByParent] has changed, calls |
2279 | /// [markNeedsLayoutForSizedByParentChange] instead of [markNeedsLayout]. |
2280 | void markNeedsLayout() { |
2281 | assert(_debugCanPerformMutations); |
2282 | if (_needsLayout) { |
2283 | assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout()); |
2284 | return; |
2285 | } |
2286 | if (_relayoutBoundary == null) { |
2287 | _needsLayout = true; |
2288 | if (parent != null) { |
2289 | // _relayoutBoundary is cleaned by an ancestor in RenderObject.layout. |
2290 | // Conservatively mark everything dirty until it reaches the closest |
2291 | // known relayout boundary. |
2292 | markParentNeedsLayout(); |
2293 | } |
2294 | return; |
2295 | } |
2296 | if (_relayoutBoundary != this) { |
2297 | markParentNeedsLayout(); |
2298 | } else { |
2299 | _needsLayout = true; |
2300 | if (owner != null) { |
2301 | assert(() { |
2302 | if (debugPrintMarkNeedsLayoutStacks) { |
2303 | debugPrintStack(label: 'markNeedsLayout() called for $this' ); |
2304 | } |
2305 | return true; |
2306 | }()); |
2307 | owner!._nodesNeedingLayout.add(this); |
2308 | owner!.requestVisualUpdate(); |
2309 | } |
2310 | } |
2311 | } |
2312 | |
2313 | /// Mark this render object's layout information as dirty, and then defer to |
2314 | /// the parent. |
2315 | /// |
2316 | /// This function should only be called from [markNeedsLayout] or |
2317 | /// [markNeedsLayoutForSizedByParentChange] implementations of subclasses that |
2318 | /// introduce more reasons for deferring the handling of dirty layout to the |
2319 | /// parent. See [markNeedsLayout] for details. |
2320 | /// |
2321 | /// Only call this if [parent] is not null. |
2322 | @protected |
2323 | void markParentNeedsLayout() { |
2324 | assert(_debugCanPerformMutations); |
2325 | _needsLayout = true; |
2326 | assert(this.parent != null); |
2327 | final RenderObject parent = this.parent!; |
2328 | if (!_doingThisLayoutWithCallback) { |
2329 | parent.markNeedsLayout(); |
2330 | } else { |
2331 | assert(parent._debugDoingThisLayout); |
2332 | } |
2333 | assert(parent == this.parent); |
2334 | } |
2335 | |
2336 | /// Mark this render object's layout information as dirty (like |
2337 | /// [markNeedsLayout]), and additionally also handle any necessary work to |
2338 | /// handle the case where [sizedByParent] has changed value. |
2339 | /// |
2340 | /// This should be called whenever [sizedByParent] might have changed. |
2341 | /// |
2342 | /// Only call this if [parent] is not null. |
2343 | void markNeedsLayoutForSizedByParentChange() { |
2344 | markNeedsLayout(); |
2345 | markParentNeedsLayout(); |
2346 | } |
2347 | |
2348 | void _cleanRelayoutBoundary() { |
2349 | if (_relayoutBoundary != this) { |
2350 | _relayoutBoundary = null; |
2351 | visitChildren(_cleanChildRelayoutBoundary); |
2352 | } |
2353 | } |
2354 | |
2355 | void _propagateRelayoutBoundary() { |
2356 | if (_relayoutBoundary == this) { |
2357 | return; |
2358 | } |
2359 | final RenderObject? parentRelayoutBoundary = parent?._relayoutBoundary; |
2360 | assert(parentRelayoutBoundary != null); |
2361 | if (parentRelayoutBoundary != _relayoutBoundary) { |
2362 | _relayoutBoundary = parentRelayoutBoundary; |
2363 | visitChildren(_propagateRelayoutBoundaryToChild); |
2364 | } |
2365 | } |
2366 | |
2367 | // Reduces closure allocation for visitChildren use cases. |
2368 | static void _cleanChildRelayoutBoundary(RenderObject child) { |
2369 | child._cleanRelayoutBoundary(); |
2370 | } |
2371 | |
2372 | static void _propagateRelayoutBoundaryToChild(RenderObject child) { |
2373 | child._propagateRelayoutBoundary(); |
2374 | } |
2375 | |
2376 | /// Bootstrap the rendering pipeline by scheduling the very first layout. |
2377 | /// |
2378 | /// Requires this render object to be attached and that this render object |
2379 | /// is the root of the render tree. |
2380 | /// |
2381 | /// See [RenderView] for an example of how this function is used. |
2382 | void scheduleInitialLayout() { |
2383 | assert(!_debugDisposed); |
2384 | assert(attached); |
2385 | assert(parent is! RenderObject); |
2386 | assert(!owner!._debugDoingLayout); |
2387 | assert(_relayoutBoundary == null); |
2388 | _relayoutBoundary = this; |
2389 | assert(() { |
2390 | _debugCanParentUseSize = false; |
2391 | return true; |
2392 | }()); |
2393 | owner!._nodesNeedingLayout.add(this); |
2394 | } |
2395 | |
2396 | @pragma('vm:notify-debugger-on-exception' ) |
2397 | void _layoutWithoutResize() { |
2398 | assert(_relayoutBoundary == this); |
2399 | RenderObject? debugPreviousActiveLayout; |
2400 | assert(!_debugMutationsLocked); |
2401 | assert(!_doingThisLayoutWithCallback); |
2402 | assert(_debugCanParentUseSize != null); |
2403 | assert(() { |
2404 | _debugMutationsLocked = true; |
2405 | _debugDoingThisLayout = true; |
2406 | debugPreviousActiveLayout = _debugActiveLayout; |
2407 | _debugActiveLayout = this; |
2408 | if (debugPrintLayouts) { |
2409 | debugPrint('Laying out (without resize) $this' ); |
2410 | } |
2411 | return true; |
2412 | }()); |
2413 | try { |
2414 | performLayout(); |
2415 | markNeedsSemanticsUpdate(); |
2416 | } catch (e, stack) { |
2417 | _reportException('performLayout' , e, stack); |
2418 | } |
2419 | assert(() { |
2420 | _debugActiveLayout = debugPreviousActiveLayout; |
2421 | _debugDoingThisLayout = false; |
2422 | _debugMutationsLocked = false; |
2423 | return true; |
2424 | }()); |
2425 | _needsLayout = false; |
2426 | markNeedsPaint(); |
2427 | } |
2428 | |
2429 | /// Compute the layout for this render object. |
2430 | /// |
2431 | /// This method is the main entry point for parents to ask their children to |
2432 | /// update their layout information. The parent passes a constraints object, |
2433 | /// which informs the child as to which layouts are permissible. The child is |
2434 | /// required to obey the given constraints. |
2435 | /// |
2436 | /// If the parent reads information computed during the child's layout, the |
2437 | /// parent must pass true for `parentUsesSize`. In that case, the parent will |
2438 | /// be marked as needing layout whenever the child is marked as needing layout |
2439 | /// because the parent's layout information depends on the child's layout |
2440 | /// information. If the parent uses the default value (false) for |
2441 | /// `parentUsesSize`, the child can change its layout information (subject to |
2442 | /// the given constraints) without informing the parent. |
2443 | /// |
2444 | /// Subclasses should not override [layout] directly. Instead, they should |
2445 | /// override [performResize] and/or [performLayout]. The [layout] method |
2446 | /// delegates the actual work to [performResize] and [performLayout]. |
2447 | /// |
2448 | /// The parent's [performLayout] method should call the [layout] of all its |
2449 | /// children unconditionally. It is the [layout] method's responsibility (as |
2450 | /// implemented here) to return early if the child does not need to do any |
2451 | /// work to update its layout information. |
2452 | @pragma('vm:notify-debugger-on-exception' ) |
2453 | void layout(Constraints constraints, { bool parentUsesSize = false }) { |
2454 | assert(!_debugDisposed); |
2455 | if (!kReleaseMode && debugProfileLayoutsEnabled) { |
2456 | Map<String, String>? debugTimelineArguments; |
2457 | assert(() { |
2458 | if (debugEnhanceLayoutTimelineArguments) { |
2459 | debugTimelineArguments = toDiagnosticsNode().toTimelineArguments(); |
2460 | } |
2461 | return true; |
2462 | }()); |
2463 | FlutterTimeline.startSync( |
2464 | ' $runtimeType' , |
2465 | arguments: debugTimelineArguments, |
2466 | ); |
2467 | } |
2468 | assert(constraints.debugAssertIsValid( |
2469 | isAppliedConstraint: true, |
2470 | informationCollector: () { |
2471 | final List<String> stack = StackTrace.current.toString().split('\n' ); |
2472 | int? targetFrame; |
2473 | final Pattern layoutFramePattern = RegExp(r'^#[0-9]+ +Render(?:Object|Box).layout \(' ); |
2474 | for (int i = 0; i < stack.length; i += 1) { |
2475 | if (layoutFramePattern.matchAsPrefix(stack[i]) != null) { |
2476 | targetFrame = i + 1; |
2477 | } else if (targetFrame != null) { |
2478 | break; |
2479 | } |
2480 | } |
2481 | if (targetFrame != null && targetFrame < stack.length) { |
2482 | final Pattern targetFramePattern = RegExp(r'^#[0-9]+ +(.+)$' ); |
2483 | final Match? targetFrameMatch = targetFramePattern.matchAsPrefix(stack[targetFrame]); |
2484 | final String? problemFunction = (targetFrameMatch != null && targetFrameMatch.groupCount > 0) ? targetFrameMatch.group(1) : stack[targetFrame].trim(); |
2485 | return <DiagnosticsNode>[ |
2486 | ErrorDescription( |
2487 | "These invalid constraints were provided to $runtimeType's layout() " |
2488 | 'function by the following function, which probably computed the ' |
2489 | 'invalid constraints in question:\n' |
2490 | ' $problemFunction' , |
2491 | ), |
2492 | ]; |
2493 | } |
2494 | return <DiagnosticsNode>[]; |
2495 | }, |
2496 | )); |
2497 | assert(!_debugDoingThisResize); |
2498 | assert(!_debugDoingThisLayout); |
2499 | final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject; |
2500 | final RenderObject relayoutBoundary = isRelayoutBoundary ? this : parent!._relayoutBoundary!; |
2501 | assert(() { |
2502 | _debugCanParentUseSize = parentUsesSize; |
2503 | return true; |
2504 | }()); |
2505 | |
2506 | if (!_needsLayout && constraints == _constraints) { |
2507 | assert(() { |
2508 | // in case parentUsesSize changed since the last invocation, set size |
2509 | // to itself, so it has the right internal debug values. |
2510 | _debugDoingThisResize = sizedByParent; |
2511 | _debugDoingThisLayout = !sizedByParent; |
2512 | final RenderObject? debugPreviousActiveLayout = _debugActiveLayout; |
2513 | _debugActiveLayout = this; |
2514 | debugResetSize(); |
2515 | _debugActiveLayout = debugPreviousActiveLayout; |
2516 | _debugDoingThisLayout = false; |
2517 | _debugDoingThisResize = false; |
2518 | return true; |
2519 | }()); |
2520 | |
2521 | if (relayoutBoundary != _relayoutBoundary) { |
2522 | _relayoutBoundary = relayoutBoundary; |
2523 | visitChildren(_propagateRelayoutBoundaryToChild); |
2524 | } |
2525 | |
2526 | if (!kReleaseMode && debugProfileLayoutsEnabled) { |
2527 | FlutterTimeline.finishSync(); |
2528 | } |
2529 | return; |
2530 | } |
2531 | _constraints = constraints; |
2532 | if (_relayoutBoundary != null && relayoutBoundary != _relayoutBoundary) { |
2533 | // The local relayout boundary has changed, must notify children in case |
2534 | // they also need updating. Otherwise, they will be confused about what |
2535 | // their actual relayout boundary is later. |
2536 | visitChildren(_cleanChildRelayoutBoundary); |
2537 | } |
2538 | _relayoutBoundary = relayoutBoundary; |
2539 | assert(!_debugMutationsLocked); |
2540 | assert(!_doingThisLayoutWithCallback); |
2541 | assert(() { |
2542 | _debugMutationsLocked = true; |
2543 | if (debugPrintLayouts) { |
2544 | debugPrint('Laying out ( ${sizedByParent ? "with separate resize" : "with resize allowed" }) $this' ); |
2545 | } |
2546 | return true; |
2547 | }()); |
2548 | if (sizedByParent) { |
2549 | assert(() { |
2550 | _debugDoingThisResize = true; |
2551 | return true; |
2552 | }()); |
2553 | try { |
2554 | performResize(); |
2555 | assert(() { |
2556 | debugAssertDoesMeetConstraints(); |
2557 | return true; |
2558 | }()); |
2559 | } catch (e, stack) { |
2560 | _reportException('performResize' , e, stack); |
2561 | } |
2562 | assert(() { |
2563 | _debugDoingThisResize = false; |
2564 | return true; |
2565 | }()); |
2566 | } |
2567 | RenderObject? debugPreviousActiveLayout; |
2568 | assert(() { |
2569 | _debugDoingThisLayout = true; |
2570 | debugPreviousActiveLayout = _debugActiveLayout; |
2571 | _debugActiveLayout = this; |
2572 | return true; |
2573 | }()); |
2574 | try { |
2575 | performLayout(); |
2576 | markNeedsSemanticsUpdate(); |
2577 | assert(() { |
2578 | debugAssertDoesMeetConstraints(); |
2579 | return true; |
2580 | }()); |
2581 | } catch (e, stack) { |
2582 | _reportException('performLayout' , e, stack); |
2583 | } |
2584 | assert(() { |
2585 | _debugActiveLayout = debugPreviousActiveLayout; |
2586 | _debugDoingThisLayout = false; |
2587 | _debugMutationsLocked = false; |
2588 | return true; |
2589 | }()); |
2590 | _needsLayout = false; |
2591 | markNeedsPaint(); |
2592 | |
2593 | if (!kReleaseMode && debugProfileLayoutsEnabled) { |
2594 | FlutterTimeline.finishSync(); |
2595 | } |
2596 | } |
2597 | |
2598 | /// If a subclass has a "size" (the state controlled by `parentUsesSize`, |
2599 | /// whatever it is in the subclass, e.g. the actual `size` property of |
2600 | /// [RenderBox]), and the subclass verifies that in debug mode this "size" |
2601 | /// property isn't used when [debugCanParentUseSize] isn't set, then that |
2602 | /// subclass should override [debugResetSize] to reapply the current values of |
2603 | /// [debugCanParentUseSize] to that state. |
2604 | @protected |
2605 | void debugResetSize() { } |
2606 | |
2607 | /// Whether the constraints are the only input to the sizing algorithm (in |
2608 | /// particular, child nodes have no impact). |
2609 | /// |
2610 | /// Returning false is always correct, but returning true can be more |
2611 | /// efficient when computing the size of this render object because we don't |
2612 | /// need to recompute the size if the constraints don't change. |
2613 | /// |
2614 | /// Typically, subclasses will always return the same value. If the value can |
2615 | /// change, then, when it does change, the subclass should make sure to call |
2616 | /// [markNeedsLayoutForSizedByParentChange]. |
2617 | /// |
2618 | /// Subclasses that return true must not change the dimensions of this render |
2619 | /// object in [performLayout]. Instead, that work should be done by |
2620 | /// [performResize] or - for subclasses of [RenderBox] - in |
2621 | /// [RenderBox.computeDryLayout]. |
2622 | @protected |
2623 | bool get sizedByParent => false; |
2624 | |
2625 | /// {@template flutter.rendering.RenderObject.performResize} |
2626 | /// Updates the render objects size using only the constraints. |
2627 | /// |
2628 | /// Do not call this function directly: call [layout] instead. This function |
2629 | /// is called by [layout] when there is actually work to be done by this |
2630 | /// render object during layout. The layout constraints provided by your |
2631 | /// parent are available via the [constraints] getter. |
2632 | /// |
2633 | /// This function is called only if [sizedByParent] is true. |
2634 | /// {@endtemplate} |
2635 | /// |
2636 | /// Subclasses that set [sizedByParent] to true should override this method to |
2637 | /// compute their size. Subclasses of [RenderBox] should consider overriding |
2638 | /// [RenderBox.computeDryLayout] instead. |
2639 | @protected |
2640 | void performResize(); |
2641 | |
2642 | /// Do the work of computing the layout for this render object. |
2643 | /// |
2644 | /// Do not call this function directly: call [layout] instead. This function |
2645 | /// is called by [layout] when there is actually work to be done by this |
2646 | /// render object during layout. The layout constraints provided by your |
2647 | /// parent are available via the [constraints] getter. |
2648 | /// |
2649 | /// If [sizedByParent] is true, then this function should not actually change |
2650 | /// the dimensions of this render object. Instead, that work should be done by |
2651 | /// [performResize]. If [sizedByParent] is false, then this function should |
2652 | /// both change the dimensions of this render object and instruct its children |
2653 | /// to layout. |
2654 | /// |
2655 | /// In implementing this function, you must call [layout] on each of your |
2656 | /// children, passing true for parentUsesSize if your layout information is |
2657 | /// dependent on your child's layout information. Passing true for |
2658 | /// parentUsesSize ensures that this render object will undergo layout if the |
2659 | /// child undergoes layout. Otherwise, the child can change its layout |
2660 | /// information without informing this render object. |
2661 | @protected |
2662 | void performLayout(); |
2663 | |
2664 | /// Allows mutations to be made to this object's child list (and any |
2665 | /// descendants) as well as to any other dirty nodes in the render tree owned |
2666 | /// by the same [PipelineOwner] as this object. The `callback` argument is |
2667 | /// invoked synchronously, and the mutations are allowed only during that |
2668 | /// callback's execution. |
2669 | /// |
2670 | /// This exists to allow child lists to be built on-demand during layout (e.g. |
2671 | /// based on the object's size), and to enable nodes to be moved around the |
2672 | /// tree as this happens (e.g. to handle [GlobalKey] reparenting), while still |
2673 | /// ensuring that any particular node is only laid out once per frame. |
2674 | /// |
2675 | /// Calling this function disables a number of assertions that are intended to |
2676 | /// catch likely bugs. As such, using this function is generally discouraged. |
2677 | /// |
2678 | /// This function can only be called during layout. |
2679 | @protected |
2680 | void invokeLayoutCallback<T extends Constraints>(LayoutCallback<T> callback) { |
2681 | assert(_debugMutationsLocked); |
2682 | assert(_debugDoingThisLayout); |
2683 | assert(!_doingThisLayoutWithCallback); |
2684 | _doingThisLayoutWithCallback = true; |
2685 | try { |
2686 | owner!._enableMutationsToDirtySubtrees(() { callback(constraints as T); }); |
2687 | } finally { |
2688 | _doingThisLayoutWithCallback = false; |
2689 | } |
2690 | } |
2691 | |
2692 | // PAINTING |
2693 | |
2694 | /// Whether [paint] for this render object is currently running. |
2695 | /// |
2696 | /// Only valid when asserts are enabled. In release builds, always returns |
2697 | /// false. |
2698 | bool get debugDoingThisPaint => _debugDoingThisPaint; |
2699 | bool _debugDoingThisPaint = false; |
2700 | |
2701 | /// The render object that is actively painting. |
2702 | /// |
2703 | /// Only valid when asserts are enabled. In release builds, always returns |
2704 | /// null. |
2705 | static RenderObject? get debugActivePaint => _debugActivePaint; |
2706 | static RenderObject? _debugActivePaint; |
2707 | |
2708 | /// Whether this render object repaints separately from its parent. |
2709 | /// |
2710 | /// Override this in subclasses to indicate that instances of your class ought |
2711 | /// to repaint independently. For example, render objects that repaint |
2712 | /// frequently might want to repaint themselves without requiring their parent |
2713 | /// to repaint. |
2714 | /// |
2715 | /// If this getter returns true, the [paintBounds] are applied to this object |
2716 | /// and all descendants. The framework invokes [RenderObject.updateCompositedLayer] |
2717 | /// to create an [OffsetLayer] and assigns it to the [layer] field. |
2718 | /// Render objects that declare themselves as repaint boundaries must not replace |
2719 | /// the layer created by the framework. |
2720 | /// |
2721 | /// If the value of this getter changes, [markNeedsCompositingBitsUpdate] must |
2722 | /// be called. |
2723 | /// |
2724 | /// See [RepaintBoundary] for more information about how repaint boundaries function. |
2725 | bool get isRepaintBoundary => false; |
2726 | |
2727 | /// Called, in debug mode, if [isRepaintBoundary] is true, when either the |
2728 | /// this render object or its parent attempt to paint. |
2729 | /// |
2730 | /// This can be used to record metrics about whether the node should actually |
2731 | /// be a repaint boundary. |
2732 | void debugRegisterRepaintBoundaryPaint({ bool includedParent = true, bool includedChild = false }) { } |
2733 | |
2734 | /// Whether this render object always needs compositing. |
2735 | /// |
2736 | /// Override this in subclasses to indicate that your paint function always |
2737 | /// creates at least one composited layer. For example, videos should return |
2738 | /// true if they use hardware decoders. |
2739 | /// |
2740 | /// You must call [markNeedsCompositingBitsUpdate] if the value of this getter |
2741 | /// changes. (This is implied when [adoptChild] or [dropChild] are called.) |
2742 | @protected |
2743 | bool get alwaysNeedsCompositing => false; |
2744 | |
2745 | late bool _wasRepaintBoundary; |
2746 | |
2747 | /// Update the composited layer owned by this render object. |
2748 | /// |
2749 | /// This method is called by the framework when [isRepaintBoundary] is true. |
2750 | /// |
2751 | /// If [oldLayer] is `null`, this method must return a new [OffsetLayer] |
2752 | /// (or subtype thereof). If [oldLayer] is not `null`, then this method must |
2753 | /// reuse the layer instance that is provided - it is an error to create a new |
2754 | /// layer in this instance. The layer will be disposed by the framework when |
2755 | /// either the render object is disposed or if it is no longer a repaint |
2756 | /// boundary. |
2757 | /// |
2758 | /// The [OffsetLayer.offset] property will be managed by the framework and |
2759 | /// must not be updated by this method. |
2760 | /// |
2761 | /// If a property of the composited layer needs to be updated, the render object |
2762 | /// must call [markNeedsCompositedLayerUpdate] which will schedule this method |
2763 | /// to be called without repainting children. If this widget was marked as |
2764 | /// needing to paint and needing a composited layer update, this method is only |
2765 | /// called once. |
2766 | // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/102102 revisit the |
2767 | // constraint that the instance/type of layer cannot be changed at runtime. |
2768 | OffsetLayer updateCompositedLayer({required covariant OffsetLayer? oldLayer}) { |
2769 | assert(isRepaintBoundary); |
2770 | return oldLayer ?? OffsetLayer(); |
2771 | } |
2772 | |
2773 | /// The compositing layer that this render object uses to repaint. |
2774 | /// |
2775 | /// If this render object is not a repaint boundary, it is the responsibility |
2776 | /// of the [paint] method to populate this field. If [needsCompositing] is |
2777 | /// true, this field may be populated with the root-most layer used by the |
2778 | /// render object implementation. When repainting, instead of creating a new |
2779 | /// layer the render object may update the layer stored in this field for better |
2780 | /// performance. It is also OK to leave this field as null and create a new |
2781 | /// layer on every repaint, but without the performance benefit. If |
2782 | /// [needsCompositing] is false, this field must be set to null either by |
2783 | /// never populating this field, or by setting it to null when the value of |
2784 | /// [needsCompositing] changes from true to false. |
2785 | /// |
2786 | /// If a new layer is created and stored in some other field on the render |
2787 | /// object, the render object must use a [LayerHandle] to store it. A layer |
2788 | /// handle will prevent the layer from being disposed before the render |
2789 | /// object is finished with it, and it will also make sure that the layer |
2790 | /// gets appropriately disposed when the render object creates a replacement |
2791 | /// or nulls it out. The render object must null out the [LayerHandle.layer] |
2792 | /// in its [dispose] method. |
2793 | /// |
2794 | /// If this render object is a repaint boundary, the framework automatically |
2795 | /// creates an [OffsetLayer] and populates this field prior to calling the |
2796 | /// [paint] method. The [paint] method must not replace the value of this |
2797 | /// field. |
2798 | @protected |
2799 | ContainerLayer? get layer { |
2800 | assert(!isRepaintBoundary || _layerHandle.layer == null || _layerHandle.layer is OffsetLayer); |
2801 | return _layerHandle.layer; |
2802 | } |
2803 | |
2804 | @protected |
2805 | set layer(ContainerLayer? newLayer) { |
2806 | assert( |
2807 | !isRepaintBoundary, |
2808 | 'Attempted to set a layer to a repaint boundary render object.\n' |
2809 | 'The framework creates and assigns an OffsetLayer to a repaint ' |
2810 | 'boundary automatically.' , |
2811 | ); |
2812 | _layerHandle.layer = newLayer; |
2813 | } |
2814 | |
2815 | final LayerHandle<ContainerLayer> _layerHandle = LayerHandle<ContainerLayer>(); |
2816 | |
2817 | /// In debug mode, the compositing layer that this render object uses to repaint. |
2818 | /// |
2819 | /// This getter is intended for debugging purposes only. In release builds, it |
2820 | /// always returns null. In debug builds, it returns the layer even if the layer |
2821 | /// is dirty. |
2822 | /// |
2823 | /// For production code, consider [layer]. |
2824 | ContainerLayer? get debugLayer { |
2825 | ContainerLayer? result; |
2826 | assert(() { |
2827 | result = _layerHandle.layer; |
2828 | return true; |
2829 | }()); |
2830 | return result; |
2831 | } |
2832 | |
2833 | bool _needsCompositingBitsUpdate = false; // set to true when a child is added |
2834 | /// Mark the compositing state for this render object as dirty. |
2835 | /// |
2836 | /// This is called to indicate that the value for [needsCompositing] needs to |
2837 | /// be recomputed during the next [PipelineOwner.flushCompositingBits] engine |
2838 | /// phase. |
2839 | /// |
2840 | /// When the subtree is mutated, we need to recompute our |
2841 | /// [needsCompositing] bit, and some of our ancestors need to do the |
2842 | /// same (in case ours changed in a way that will change theirs). To |
2843 | /// this end, [adoptChild] and [dropChild] call this method, and, as |
2844 | /// necessary, this method calls the parent's, etc, walking up the |
2845 | /// tree to mark all the nodes that need updating. |
2846 | /// |
2847 | /// This method does not schedule a rendering frame, because since |
2848 | /// it cannot be the case that _only_ the compositing bits changed, |
2849 | /// something else will have scheduled a frame for us. |
2850 | void markNeedsCompositingBitsUpdate() { |
2851 | assert(!_debugDisposed); |
2852 | if (_needsCompositingBitsUpdate) { |
2853 | return; |
2854 | } |
2855 | _needsCompositingBitsUpdate = true; |
2856 | if (parent is RenderObject) { |
2857 | final RenderObject parent = this.parent!; |
2858 | if (parent._needsCompositingBitsUpdate) { |
2859 | return; |
2860 | } |
2861 | |
2862 | if ((!_wasRepaintBoundary || !isRepaintBoundary) && !parent.isRepaintBoundary) { |
2863 | parent.markNeedsCompositingBitsUpdate(); |
2864 | return; |
2865 | } |
2866 | } |
2867 | // parent is fine (or there isn't one), but we are dirty |
2868 | if (owner != null) { |
2869 | owner!._nodesNeedingCompositingBitsUpdate.add(this); |
2870 | } |
2871 | } |
2872 | |
2873 | late bool _needsCompositing; // initialized in the constructor |
2874 | /// Whether we or one of our descendants has a compositing layer. |
2875 | /// |
2876 | /// If this node needs compositing as indicated by this bit, then all ancestor |
2877 | /// nodes will also need compositing. |
2878 | /// |
2879 | /// Only legal to call after [PipelineOwner.flushLayout] and |
2880 | /// [PipelineOwner.flushCompositingBits] have been called. |
2881 | bool get needsCompositing { |
2882 | assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty |
2883 | return _needsCompositing; |
2884 | } |
2885 | |
2886 | void _updateCompositingBits() { |
2887 | if (!_needsCompositingBitsUpdate) { |
2888 | return; |
2889 | } |
2890 | final bool oldNeedsCompositing = _needsCompositing; |
2891 | _needsCompositing = false; |
2892 | visitChildren((RenderObject child) { |
2893 | child._updateCompositingBits(); |
2894 | if (child.needsCompositing) { |
2895 | _needsCompositing = true; |
2896 | } |
2897 | }); |
2898 | if (isRepaintBoundary || alwaysNeedsCompositing) { |
2899 | _needsCompositing = true; |
2900 | } |
2901 | // If a node was previously a repaint boundary, but no longer is one, then |
2902 | // regardless of its compositing state we need to find a new parent to |
2903 | // paint from. To do this, we mark it clean again so that the traversal |
2904 | // in markNeedsPaint is not short-circuited. It is removed from _nodesNeedingPaint |
2905 | // so that we do not attempt to paint from it after locating a parent. |
2906 | if (!isRepaintBoundary && _wasRepaintBoundary) { |
2907 | _needsPaint = false; |
2908 | _needsCompositedLayerUpdate = false; |
2909 | owner?._nodesNeedingPaint.remove(this); |
2910 | _needsCompositingBitsUpdate = false; |
2911 | markNeedsPaint(); |
2912 | } else if (oldNeedsCompositing != _needsCompositing) { |
2913 | _needsCompositingBitsUpdate = false; |
2914 | markNeedsPaint(); |
2915 | } else { |
2916 | _needsCompositingBitsUpdate = false; |
2917 | } |
2918 | } |
2919 | |
2920 | /// Whether this render object's paint information is dirty. |
2921 | /// |
2922 | /// This is only set in debug mode. In general, render objects should not need |
2923 | /// to condition their runtime behavior on whether they are dirty or not, |
2924 | /// since they should only be marked dirty immediately prior to being laid |
2925 | /// out and painted. (In release builds, this throws.) |
2926 | /// |
2927 | /// It is intended to be used by tests and asserts. |
2928 | /// |
2929 | /// It is possible (and indeed, quite common) for [debugNeedsPaint] to be |
2930 | /// false and [debugNeedsLayout] to be true. The render object will still be |
2931 | /// repainted in the next frame when this is the case, because the |
2932 | /// [markNeedsPaint] method is implicitly called by the framework after a |
2933 | /// render object is laid out, prior to the paint phase. |
2934 | bool get debugNeedsPaint { |
2935 | late bool result; |
2936 | assert(() { |
2937 | result = _needsPaint; |
2938 | return true; |
2939 | }()); |
2940 | return result; |
2941 | } |
2942 | bool _needsPaint = true; |
2943 | |
2944 | /// Whether this render object's layer information is dirty. |
2945 | /// |
2946 | /// This is only set in debug mode. In general, render objects should not need |
2947 | /// to condition their runtime behavior on whether they are dirty or not, |
2948 | /// since they should only be marked dirty immediately prior to being laid |
2949 | /// out and painted. (In release builds, this throws.) |
2950 | /// |
2951 | /// It is intended to be used by tests and asserts. |
2952 | bool get debugNeedsCompositedLayerUpdate { |
2953 | late bool result; |
2954 | assert(() { |
2955 | result = _needsCompositedLayerUpdate; |
2956 | return true; |
2957 | }()); |
2958 | return result; |
2959 | } |
2960 | bool _needsCompositedLayerUpdate = false; |
2961 | |
2962 | /// Mark this render object as having changed its visual appearance. |
2963 | /// |
2964 | /// Rather than eagerly updating this render object's display list |
2965 | /// in response to writes, we instead mark the render object as needing to |
2966 | /// paint, which schedules a visual update. As part of the visual update, the |
2967 | /// rendering pipeline will give this render object an opportunity to update |
2968 | /// its display list. |
2969 | /// |
2970 | /// This mechanism batches the painting work so that multiple sequential |
2971 | /// writes are coalesced, removing redundant computation. |
2972 | /// |
2973 | /// Once [markNeedsPaint] has been called on a render object, |
2974 | /// [debugNeedsPaint] returns true for that render object until just after |
2975 | /// the pipeline owner has called [paint] on the render object. |
2976 | /// |
2977 | /// See also: |
2978 | /// |
2979 | /// * [RepaintBoundary], to scope a subtree of render objects to their own |
2980 | /// layer, thus limiting the number of nodes that [markNeedsPaint] must mark |
2981 | /// dirty. |
2982 | void markNeedsPaint() { |
2983 | assert(!_debugDisposed); |
2984 | assert(owner == null || !owner!.debugDoingPaint); |
2985 | if (_needsPaint) { |
2986 | return; |
2987 | } |
2988 | _needsPaint = true; |
2989 | // If this was not previously a repaint boundary it will not have |
2990 | // a layer we can paint from. |
2991 | if (isRepaintBoundary && _wasRepaintBoundary) { |
2992 | assert(() { |
2993 | if (debugPrintMarkNeedsPaintStacks) { |
2994 | debugPrintStack(label: 'markNeedsPaint() called for $this' ); |
2995 | } |
2996 | return true; |
2997 | }()); |
2998 | // If we always have our own layer, then we can just repaint |
2999 | // ourselves without involving any other nodes. |
3000 | assert(_layerHandle.layer is OffsetLayer); |
3001 | if (owner != null) { |
3002 | owner!._nodesNeedingPaint.add(this); |
3003 | owner!.requestVisualUpdate(); |
3004 | } |
3005 | } else if (parent is RenderObject) { |
3006 | parent!.markNeedsPaint(); |
3007 | } else { |
3008 | assert(() { |
3009 | if (debugPrintMarkNeedsPaintStacks) { |
3010 | debugPrintStack(label: 'markNeedsPaint() called for $this (root of render tree)' ); |
3011 | } |
3012 | return true; |
3013 | }()); |
3014 | // If we are the root of the render tree and not a repaint boundary |
3015 | // then we have to paint ourselves, since nobody else can paint us. |
3016 | // We don't add ourselves to _nodesNeedingPaint in this case, |
3017 | // because the root is always told to paint regardless. |
3018 | // |
3019 | // Trees rooted at a RenderView do not go through this |
3020 | // code path because RenderViews are repaint boundaries. |
3021 | if (owner != null) { |
3022 | owner!.requestVisualUpdate(); |
3023 | } |
3024 | } |
3025 | } |
3026 | |
3027 | /// Mark this render object as having changed a property on its composited |
3028 | /// layer. |
3029 | /// |
3030 | /// Render objects that have a composited layer have [isRepaintBoundary] equal |
3031 | /// to true may update the properties of that composited layer without repainting |
3032 | /// their children. If this render object is a repaint boundary but does |
3033 | /// not yet have a composited layer created for it, this method will instead |
3034 | /// mark the nearest repaint boundary parent as needing to be painted. |
3035 | /// |
3036 | /// If this method is called on a render object that is not a repaint boundary |
3037 | /// or is a repaint boundary but hasn't been composited yet, it is equivalent |
3038 | /// to calling [markNeedsPaint]. |
3039 | /// |
3040 | /// See also: |
3041 | /// |
3042 | /// * [RenderOpacity], which uses this method when its opacity is updated to |
3043 | /// update the layer opacity without repainting children. |
3044 | void markNeedsCompositedLayerUpdate() { |
3045 | assert(!_debugDisposed); |
3046 | assert(owner == null || !owner!.debugDoingPaint); |
3047 | if (_needsCompositedLayerUpdate || _needsPaint) { |
3048 | return; |
3049 | } |
3050 | _needsCompositedLayerUpdate = true; |
3051 | // If this was not previously a repaint boundary it will not have |
3052 | // a layer we can paint from. |
3053 | if (isRepaintBoundary && _wasRepaintBoundary) { |
3054 | // If we always have our own layer, then we can just repaint |
3055 | // ourselves without involving any other nodes. |
3056 | assert(_layerHandle.layer != null); |
3057 | if (owner != null) { |
3058 | owner!._nodesNeedingPaint.add(this); |
3059 | owner!.requestVisualUpdate(); |
3060 | } |
3061 | } else { |
3062 | markNeedsPaint(); |
3063 | } |
3064 | } |
3065 | |
3066 | // Called when flushPaint() tries to make us paint but our layer is detached. |
3067 | // To make sure that our subtree is repainted when it's finally reattached, |
3068 | // even in the case where some ancestor layer is itself never marked dirty, we |
3069 | // have to mark our entire detached subtree as dirty and needing to be |
3070 | // repainted. That way, we'll eventually be repainted. |
3071 | void _skippedPaintingOnLayer() { |
3072 | assert(attached); |
3073 | assert(isRepaintBoundary); |
3074 | assert(_needsPaint || _needsCompositedLayerUpdate); |
3075 | assert(_layerHandle.layer != null); |
3076 | assert(!_layerHandle.layer!.attached); |
3077 | RenderObject? node = parent; |
3078 | while (node is RenderObject) { |
3079 | if (node.isRepaintBoundary) { |
3080 | if (node._layerHandle.layer == null) { |
3081 | // Looks like the subtree here has never been painted. Let it handle itself. |
3082 | break; |
3083 | } |
3084 | if (node._layerHandle.layer!.attached) { |
3085 | // It's the one that detached us, so it's the one that will decide to repaint us. |
3086 | break; |
3087 | } |
3088 | node._needsPaint = true; |
3089 | } |
3090 | node = node.parent; |
3091 | } |
3092 | } |
3093 | |
3094 | /// Bootstrap the rendering pipeline by scheduling the very first paint. |
3095 | /// |
3096 | /// Requires that this render object is attached, is the root of the render |
3097 | /// tree, and has a composited layer. |
3098 | /// |
3099 | /// See [RenderView] for an example of how this function is used. |
3100 | void scheduleInitialPaint(ContainerLayer rootLayer) { |
3101 | assert(rootLayer.attached); |
3102 | assert(attached); |
3103 | assert(parent is! RenderObject); |
3104 | assert(!owner!._debugDoingPaint); |
3105 | assert(isRepaintBoundary); |
3106 | assert(_layerHandle.layer == null); |
3107 | _layerHandle.layer = rootLayer; |
3108 | assert(_needsPaint); |
3109 | owner!._nodesNeedingPaint.add(this); |
3110 | } |
3111 | |
3112 | /// Replace the layer. This is only valid for the root of a render |
3113 | /// object subtree (whatever object [scheduleInitialPaint] was |
3114 | /// called on). |
3115 | /// |
3116 | /// This might be called if, e.g., the device pixel ratio changed. |
3117 | void replaceRootLayer(OffsetLayer rootLayer) { |
3118 | assert(!_debugDisposed); |
3119 | assert(rootLayer.attached); |
3120 | assert(attached); |
3121 | assert(parent is! RenderObject); |
3122 | assert(!owner!._debugDoingPaint); |
3123 | assert(isRepaintBoundary); |
3124 | assert(_layerHandle.layer != null); // use scheduleInitialPaint the first time |
3125 | _layerHandle.layer!.detach(); |
3126 | _layerHandle.layer = rootLayer; |
3127 | markNeedsPaint(); |
3128 | } |
3129 | |
3130 | void _paintWithContext(PaintingContext context, Offset offset) { |
3131 | assert(!_debugDisposed); |
3132 | assert(() { |
3133 | if (_debugDoingThisPaint) { |
3134 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
3135 | ErrorSummary('Tried to paint a RenderObject reentrantly.' ), |
3136 | describeForError( |
3137 | 'The following RenderObject was already being painted when it was ' |
3138 | 'painted again' , |
3139 | ), |
3140 | ErrorDescription( |
3141 | 'Since this typically indicates an infinite recursion, it is ' |
3142 | 'disallowed.' , |
3143 | ), |
3144 | ]); |
3145 | } |
3146 | return true; |
3147 | }()); |
3148 | // If we still need layout, then that means that we were skipped in the |
3149 | // layout phase and therefore don't need painting. We might not know that |
3150 | // yet (that is, our layer might not have been detached yet), because the |
3151 | // same node that skipped us in layout is above us in the tree (obviously) |
3152 | // and therefore may not have had a chance to paint yet (since the tree |
3153 | // paints in reverse order). In particular this will happen if they have |
3154 | // a different layer, because there's a repaint boundary between us. |
3155 | if (_needsLayout) { |
3156 | return; |
3157 | } |
3158 | if (!kReleaseMode && debugProfilePaintsEnabled) { |
3159 | Map<String, String>? debugTimelineArguments; |
3160 | assert(() { |
3161 | if (debugEnhancePaintTimelineArguments) { |
3162 | debugTimelineArguments = toDiagnosticsNode().toTimelineArguments(); |
3163 | } |
3164 | return true; |
3165 | }()); |
3166 | FlutterTimeline.startSync( |
3167 | ' $runtimeType' , |
3168 | arguments: debugTimelineArguments, |
3169 | ); |
3170 | } |
3171 | assert(() { |
3172 | if (_needsCompositingBitsUpdate) { |
3173 | if (parent is RenderObject) { |
3174 | final RenderObject parent = this.parent!; |
3175 | bool visitedByParent = false; |
3176 | parent.visitChildren((RenderObject child) { |
3177 | if (child == this) { |
3178 | visitedByParent = true; |
3179 | } |
3180 | }); |
3181 | if (!visitedByParent) { |
3182 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
3183 | ErrorSummary( |
3184 | "A RenderObject was not visited by the parent's visitChildren " |
3185 | 'during paint.' , |
3186 | ), |
3187 | parent.describeForError( |
3188 | 'The parent was' , |
3189 | ), |
3190 | describeForError( |
3191 | 'The child that was not visited was' , |
3192 | ), |
3193 | ErrorDescription( |
3194 | 'A RenderObject with children must implement visitChildren and ' |
3195 | 'call the visitor exactly once for each child; it also should not ' |
3196 | 'paint children that were removed with dropChild.' , |
3197 | ), |
3198 | ErrorHint( |
3199 | 'This usually indicates an error in the Flutter framework itself.' , |
3200 | ), |
3201 | ]); |
3202 | } |
3203 | } |
3204 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
3205 | ErrorSummary( |
3206 | 'Tried to paint a RenderObject before its compositing bits were ' |
3207 | 'updated.' , |
3208 | ), |
3209 | describeForError( |
3210 | 'The following RenderObject was marked as having dirty compositing ' |
3211 | 'bits at the time that it was painted' , |
3212 | ), |
3213 | ErrorDescription( |
3214 | 'A RenderObject that still has dirty compositing bits cannot be ' |
3215 | 'painted because this indicates that the tree has not yet been ' |
3216 | 'properly configured for creating the layer tree.' , |
3217 | ), |
3218 | ErrorHint( |
3219 | 'This usually indicates an error in the Flutter framework itself.' , |
3220 | ), |
3221 | ]); |
3222 | } |
3223 | return true; |
3224 | }()); |
3225 | RenderObject? debugLastActivePaint; |
3226 | assert(() { |
3227 | _debugDoingThisPaint = true; |
3228 | debugLastActivePaint = _debugActivePaint; |
3229 | _debugActivePaint = this; |
3230 | assert(!isRepaintBoundary || _layerHandle.layer != null); |
3231 | return true; |
3232 | }()); |
3233 | _needsPaint = false; |
3234 | _needsCompositedLayerUpdate = false; |
3235 | _wasRepaintBoundary = isRepaintBoundary; |
3236 | try { |
3237 | paint(context, offset); |
3238 | assert(!_needsLayout); // check that the paint() method didn't mark us dirty again |
3239 | assert(!_needsPaint); // check that the paint() method didn't mark us dirty again |
3240 | } catch (e, stack) { |
3241 | _reportException('paint' , e, stack); |
3242 | } |
3243 | assert(() { |
3244 | debugPaint(context, offset); |
3245 | _debugActivePaint = debugLastActivePaint; |
3246 | _debugDoingThisPaint = false; |
3247 | return true; |
3248 | }()); |
3249 | if (!kReleaseMode && debugProfilePaintsEnabled) { |
3250 | FlutterTimeline.finishSync(); |
3251 | } |
3252 | } |
3253 | |
3254 | /// An estimate of the bounds within which this render object will paint. |
3255 | /// Useful for debugging flags such as [debugPaintLayerBordersEnabled]. |
3256 | /// |
3257 | /// These are also the bounds used by [showOnScreen] to make a [RenderObject] |
3258 | /// visible on screen. |
3259 | Rect get paintBounds; |
3260 | |
3261 | /// Override this method to paint debugging information. |
3262 | void debugPaint(PaintingContext context, Offset offset) { } |
3263 | |
3264 | /// Paint this render object into the given context at the given offset. |
3265 | /// |
3266 | /// Subclasses should override this method to provide a visual appearance |
3267 | /// for themselves. The render object's local coordinate system is |
3268 | /// axis-aligned with the coordinate system of the context's canvas and the |
3269 | /// render object's local origin (i.e, x=0 and y=0) is placed at the given |
3270 | /// offset in the context's canvas. |
3271 | /// |
3272 | /// Do not call this function directly. If you wish to paint yourself, call |
3273 | /// [markNeedsPaint] instead to schedule a call to this function. If you wish |
3274 | /// to paint one of your children, call [PaintingContext.paintChild] on the |
3275 | /// given `context`. |
3276 | /// |
3277 | /// When painting one of your children (via a paint child function on the |
3278 | /// given context), the current canvas held by the context might change |
3279 | /// because draw operations before and after painting children might need to |
3280 | /// be recorded on separate compositing layers. |
3281 | void paint(PaintingContext context, Offset offset) { } |
3282 | |
3283 | /// Applies the transform that would be applied when painting the given child |
3284 | /// to the given matrix. |
3285 | /// |
3286 | /// Used by coordinate conversion functions to translate coordinates local to |
3287 | /// one render object into coordinates local to another render object. |
3288 | /// |
3289 | /// Some RenderObjects will provide a zeroed out matrix in this method, |
3290 | /// indicating that the child should not paint anything or respond to hit |
3291 | /// tests currently. A parent may supply a non-zero matrix even though it |
3292 | /// does not paint its child currently, for example if the parent is a |
3293 | /// [RenderOffstage] with `offstage` set to true. In both of these cases, |
3294 | /// the parent must return `false` from [paintsChild]. |
3295 | void applyPaintTransform(covariant RenderObject child, Matrix4 transform) { |
3296 | assert(child.parent == this); |
3297 | } |
3298 | |
3299 | /// Whether the given child would be painted if [paint] were called. |
3300 | /// |
3301 | /// Some RenderObjects skip painting their children if they are configured to |
3302 | /// not produce any visible effects. For example, a [RenderOffstage] with |
3303 | /// its `offstage` property set to true, or a [RenderOpacity] with its opacity |
3304 | /// value set to zero. |
3305 | /// |
3306 | /// In these cases, the parent may still supply a non-zero matrix in |
3307 | /// [applyPaintTransform] to inform callers about where it would paint the |
3308 | /// child if the child were painted at all. Alternatively, the parent may |
3309 | /// supply a zeroed out matrix if it would not otherwise be able to determine |
3310 | /// a valid matrix for the child and thus cannot meaningfully determine where |
3311 | /// the child would paint. |
3312 | bool paintsChild(covariant RenderObject child) { |
3313 | assert(child.parent == this); |
3314 | return true; |
3315 | } |
3316 | |
3317 | /// {@template flutter.rendering.RenderObject.getTransformTo} |
3318 | /// Applies the paint transform up the tree to `ancestor`. |
3319 | /// |
3320 | /// Returns a matrix that maps the local paint coordinate system to the |
3321 | /// coordinate system of `ancestor`. |
3322 | /// |
3323 | /// If `ancestor` is null, this method returns a matrix that maps from the |
3324 | /// local paint coordinate system to the coordinate system of the |
3325 | /// [PipelineOwner.rootNode]. |
3326 | /// {@endtemplate} |
3327 | /// |
3328 | /// For the render tree owned by the [RendererBinding] (i.e. for the main |
3329 | /// render tree displayed on the device) this means that this method maps to |
3330 | /// the global coordinate system in logical pixels. To get physical pixels, |
3331 | /// use [applyPaintTransform] from the [RenderView] to further transform the |
3332 | /// coordinate. |
3333 | Matrix4 getTransformTo(RenderObject? ancestor) { |
3334 | final bool ancestorSpecified = ancestor != null; |
3335 | assert(attached); |
3336 | if (ancestor == null) { |
3337 | final RenderObject? rootNode = owner!.rootNode; |
3338 | if (rootNode is RenderObject) { |
3339 | ancestor = rootNode; |
3340 | } |
3341 | } |
3342 | final List<RenderObject> renderers = <RenderObject>[]; |
3343 | for (RenderObject renderer = this; renderer != ancestor; renderer = renderer.parent!) { |
3344 | renderers.add(renderer); |
3345 | assert(renderer.parent != null); // Failed to find ancestor in parent chain. |
3346 | } |
3347 | if (ancestorSpecified) { |
3348 | renderers.add(ancestor!); |
3349 | } |
3350 | final Matrix4 transform = Matrix4.identity(); |
3351 | for (int index = renderers.length - 1; index > 0; index -= 1) { |
3352 | renderers[index].applyPaintTransform(renderers[index - 1], transform); |
3353 | } |
3354 | return transform; |
3355 | } |
3356 | |
3357 | |
3358 | /// Returns a rect in this object's coordinate system that describes |
3359 | /// the approximate bounding box of the clip rect that would be |
3360 | /// applied to the given child during the paint phase, if any. |
3361 | /// |
3362 | /// Returns null if the child would not be clipped. |
3363 | /// |
3364 | /// This is used in the semantics phase to avoid including children |
3365 | /// that are not physically visible. |
3366 | /// |
3367 | /// RenderObjects that respect a [Clip] behavior when painting _must_ respect |
3368 | /// that same behavior when describing this value. For example, if passing |
3369 | /// [Clip.none] to [PaintingContext.pushClipRect] as the `clipBehavior`, then |
3370 | /// the implementation of this method must return null. |
3371 | Rect? describeApproximatePaintClip(covariant RenderObject child) => null; |
3372 | |
3373 | /// Returns a rect in this object's coordinate system that describes |
3374 | /// which [SemanticsNode]s produced by the `child` should be included in the |
3375 | /// semantics tree. [SemanticsNode]s from the `child` that are positioned |
3376 | /// outside of this rect will be dropped. Child [SemanticsNode]s that are |
3377 | /// positioned inside this rect, but outside of [describeApproximatePaintClip] |
3378 | /// will be included in the tree marked as hidden. Child [SemanticsNode]s |
3379 | /// that are inside of both rect will be included in the tree as regular |
3380 | /// nodes. |
3381 | /// |
3382 | /// This method only returns a non-null value if the semantics clip rect |
3383 | /// is different from the rect returned by [describeApproximatePaintClip]. |
3384 | /// If the semantics clip rect and the paint clip rect are the same, this |
3385 | /// method returns null. |
3386 | /// |
3387 | /// A viewport would typically implement this method to include semantic nodes |
3388 | /// in the semantics tree that are currently hidden just before the leading |
3389 | /// or just after the trailing edge. These nodes have to be included in the |
3390 | /// semantics tree to implement implicit accessibility scrolling on iOS where |
3391 | /// the viewport scrolls implicitly when moving the accessibility focus from |
3392 | /// the last visible node in the viewport to the first hidden one. |
3393 | /// |
3394 | /// See also: |
3395 | /// |
3396 | /// * [RenderViewportBase.cacheExtent], used by viewports to extend their |
3397 | /// semantics clip beyond their approximate paint clip. |
3398 | Rect? describeSemanticsClip(covariant RenderObject? child) => null; |
3399 | |
3400 | // SEMANTICS |
3401 | |
3402 | /// Bootstrap the semantics reporting mechanism by marking this node |
3403 | /// as needing a semantics update. |
3404 | /// |
3405 | /// Requires that this render object is attached, and is the root of |
3406 | /// the render tree. |
3407 | /// |
3408 | /// See [RendererBinding] for an example of how this function is used. |
3409 | void scheduleInitialSemantics() { |
3410 | assert(!_debugDisposed); |
3411 | assert(attached); |
3412 | assert(parent is! RenderObject); |
3413 | assert(!owner!._debugDoingSemantics); |
3414 | assert(_semantics == null); |
3415 | assert(_needsSemanticsUpdate); |
3416 | assert(owner!._semanticsOwner != null); |
3417 | owner!._nodesNeedingSemantics.add(this); |
3418 | owner!.requestVisualUpdate(); |
3419 | } |
3420 | |
3421 | /// Report the semantics of this node, for example for accessibility purposes. |
3422 | /// |
3423 | /// This method should be overridden by subclasses that have interesting |
3424 | /// semantic information. |
3425 | /// |
3426 | /// The given [SemanticsConfiguration] object is mutable and should be |
3427 | /// annotated in a manner that describes the current state. No reference |
3428 | /// should be kept to that object; mutating it outside of the context of the |
3429 | /// [describeSemanticsConfiguration] call (for example as a result of |
3430 | /// asynchronous computation) will at best have no useful effect and at worse |
3431 | /// will cause crashes as the data will be in an inconsistent state. |
3432 | /// |
3433 | /// {@tool snippet} |
3434 | /// |
3435 | /// The following snippet will describe the node as a button that responds to |
3436 | /// tap actions. |
3437 | /// |
3438 | /// ```dart |
3439 | /// abstract class SemanticButtonRenderObject extends RenderObject { |
3440 | /// @override |
3441 | /// void describeSemanticsConfiguration(SemanticsConfiguration config) { |
3442 | /// super.describeSemanticsConfiguration(config); |
3443 | /// config |
3444 | /// ..onTap = _handleTap |
3445 | /// ..label = 'I am a button' |
3446 | /// ..isButton = true; |
3447 | /// } |
3448 | /// |
3449 | /// void _handleTap() { |
3450 | /// // Do something. |
3451 | /// } |
3452 | /// } |
3453 | /// ``` |
3454 | /// {@end-tool} |
3455 | @protected |
3456 | void describeSemanticsConfiguration(SemanticsConfiguration config) { |
3457 | // Nothing to do by default. |
3458 | } |
3459 | |
3460 | /// Sends a [SemanticsEvent] associated with this render object's [SemanticsNode]. |
3461 | /// |
3462 | /// If this render object has no semantics information, the first parent |
3463 | /// render object with a non-null semantic node is used. |
3464 | /// |
3465 | /// If semantics are disabled, no events are dispatched. |
3466 | /// |
3467 | /// See [SemanticsNode.sendEvent] for a full description of the behavior. |
3468 | void sendSemanticsEvent(SemanticsEvent semanticsEvent) { |
3469 | if (owner!.semanticsOwner == null) { |
3470 | return; |
3471 | } |
3472 | if (_semantics != null && !_semantics!.isMergedIntoParent) { |
3473 | _semantics!.sendEvent(semanticsEvent); |
3474 | } else if (parent != null) { |
3475 | parent!.sendSemanticsEvent(semanticsEvent); |
3476 | } |
3477 | } |
3478 | |
3479 | // Use [_semanticsConfiguration] to access. |
3480 | SemanticsConfiguration? _cachedSemanticsConfiguration; |
3481 | |
3482 | SemanticsConfiguration get _semanticsConfiguration { |
3483 | if (_cachedSemanticsConfiguration == null) { |
3484 | _cachedSemanticsConfiguration = SemanticsConfiguration(); |
3485 | describeSemanticsConfiguration(_cachedSemanticsConfiguration!); |
3486 | assert( |
3487 | !_cachedSemanticsConfiguration!.explicitChildNodes || _cachedSemanticsConfiguration!.childConfigurationsDelegate == null, |
3488 | 'A SemanticsConfiguration with explicitChildNode set to true cannot have a non-null childConfigsDelegate.' , |
3489 | ); |
3490 | } |
3491 | return _cachedSemanticsConfiguration!; |
3492 | } |
3493 | |
3494 | /// The bounding box, in the local coordinate system, of this |
3495 | /// object, for accessibility purposes. |
3496 | Rect get semanticBounds; |
3497 | |
3498 | bool _needsSemanticsUpdate = true; |
3499 | SemanticsNode? _semantics; |
3500 | |
3501 | /// The semantics of this render object. |
3502 | /// |
3503 | /// Exposed only for testing and debugging. To learn about the semantics of |
3504 | /// render objects in production, obtain a [SemanticsHandle] from |
3505 | /// [PipelineOwner.ensureSemantics]. |
3506 | /// |
3507 | /// Only valid in debug and profile mode. In release builds, always returns |
3508 | /// null. |
3509 | SemanticsNode? get debugSemantics { |
3510 | if (!kReleaseMode) { |
3511 | return _semantics; |
3512 | } |
3513 | return null; |
3514 | } |
3515 | |
3516 | /// Removes all semantics from this render object and its descendants. |
3517 | /// |
3518 | /// Should only be called on objects whose [parent] is not a [RenderObject]. |
3519 | /// |
3520 | /// Override this method if you instantiate new [SemanticsNode]s in an |
3521 | /// overridden [assembleSemanticsNode] method, to dispose of those nodes. |
3522 | @mustCallSuper |
3523 | void clearSemantics() { |
3524 | _needsSemanticsUpdate = true; |
3525 | _semantics = null; |
3526 | visitChildren((RenderObject child) { |
3527 | child.clearSemantics(); |
3528 | }); |
3529 | } |
3530 | |
3531 | /// Mark this node as needing an update to its semantics description. |
3532 | /// |
3533 | /// This must be called whenever the semantics configuration of this |
3534 | /// [RenderObject] as annotated by [describeSemanticsConfiguration] changes in |
3535 | /// any way to update the semantics tree. |
3536 | void markNeedsSemanticsUpdate() { |
3537 | assert(!_debugDisposed); |
3538 | assert(!attached || !owner!._debugDoingSemantics); |
3539 | if (!attached || owner!._semanticsOwner == null) { |
3540 | _cachedSemanticsConfiguration = null; |
3541 | return; |
3542 | } |
3543 | |
3544 | // Dirty the semantics tree starting at `this` until we have reached a |
3545 | // RenderObject that is a semantics boundary. All semantics past this |
3546 | // RenderObject are still up-to date. Therefore, we will later only rebuild |
3547 | // the semantics subtree starting at the identified semantics boundary. |
3548 | |
3549 | final bool wasSemanticsBoundary = _semantics != null && (_cachedSemanticsConfiguration?.isSemanticBoundary ?? false); |
3550 | |
3551 | bool mayProduceSiblingNodes = |
3552 | _cachedSemanticsConfiguration?.childConfigurationsDelegate != null || |
3553 | _semanticsConfiguration.childConfigurationsDelegate != null; |
3554 | _cachedSemanticsConfiguration = null; |
3555 | |
3556 | bool isEffectiveSemanticsBoundary = _semanticsConfiguration.isSemanticBoundary && wasSemanticsBoundary; |
3557 | RenderObject node = this; |
3558 | |
3559 | // The sibling nodes will be attached to the parent of immediate semantics |
3560 | // node, thus marking this semantics boundary dirty is not enough, it needs |
3561 | // to find the first parent semantics boundary that does not have any |
3562 | // possible sibling node. |
3563 | while (node.parent != null && (mayProduceSiblingNodes || !isEffectiveSemanticsBoundary)) { |
3564 | if (node != this && node._needsSemanticsUpdate) { |
3565 | break; |
3566 | } |
3567 | node._needsSemanticsUpdate = true; |
3568 | // Since this node is a semantics boundary, the produced sibling nodes will |
3569 | // be attached to the parent semantics boundary. Thus, these sibling nodes |
3570 | // will not be carried to the next loop. |
3571 | if (isEffectiveSemanticsBoundary) { |
3572 | mayProduceSiblingNodes = false; |
3573 | } |
3574 | |
3575 | node = node.parent!; |
3576 | isEffectiveSemanticsBoundary = node._semanticsConfiguration.isSemanticBoundary; |
3577 | if (isEffectiveSemanticsBoundary && node._semantics == null) { |
3578 | // We have reached a semantics boundary that doesn't own a semantics node. |
3579 | // That means the semantics of this branch are currently blocked and will |
3580 | // not appear in the semantics tree. We can abort the walk here. |
3581 | return; |
3582 | } |
3583 | } |
3584 | if (node != this && _semantics != null && _needsSemanticsUpdate) { |
3585 | // If `this` node has already been added to [owner._nodesNeedingSemantics] |
3586 | // remove it as it is no longer guaranteed that its semantics |
3587 | // node will continue to be in the tree. If it still is in the tree, the |
3588 | // ancestor `node` added to [owner._nodesNeedingSemantics] at the end of |
3589 | // this block will ensure that the semantics of `this` node actually gets |
3590 | // updated. |
3591 | // (See semantics_10_test.dart for an example why this is required). |
3592 | owner!._nodesNeedingSemantics.remove(this); |
3593 | } |
3594 | if (!node._needsSemanticsUpdate) { |
3595 | node._needsSemanticsUpdate = true; |
3596 | if (owner != null) { |
3597 | assert(node._semanticsConfiguration.isSemanticBoundary || node.parent == null); |
3598 | owner!._nodesNeedingSemantics.add(node); |
3599 | owner!.requestVisualUpdate(); |
3600 | } |
3601 | } |
3602 | } |
3603 | |
3604 | /// Updates the semantic information of the render object. |
3605 | void _updateSemantics() { |
3606 | assert(_semanticsConfiguration.isSemanticBoundary || parent == null); |
3607 | if (_needsLayout) { |
3608 | // There's not enough information in this subtree to compute semantics. |
3609 | // The subtree is probably being kept alive by a viewport but not laid out. |
3610 | return; |
3611 | } |
3612 | if (!kReleaseMode) { |
3613 | FlutterTimeline.startSync('Semantics.GetFragment' ); |
3614 | } |
3615 | final _SemanticsFragment fragment = _getSemanticsForParent( |
3616 | mergeIntoParent: _semantics?.parent?.isPartOfNodeMerging ?? false, |
3617 | blockUserActions: _semantics?.areUserActionsBlocked ?? false, |
3618 | ); |
3619 | if (!kReleaseMode) { |
3620 | FlutterTimeline.finishSync(); |
3621 | } |
3622 | assert(fragment is _InterestingSemanticsFragment); |
3623 | final _InterestingSemanticsFragment interestingFragment = fragment as _InterestingSemanticsFragment; |
3624 | final List<SemanticsNode> result = <SemanticsNode>[]; |
3625 | final List<SemanticsNode> siblingNodes = <SemanticsNode>[]; |
3626 | |
3627 | if (!kReleaseMode) { |
3628 | FlutterTimeline.startSync('Semantics.compileChildren' ); |
3629 | } |
3630 | interestingFragment.compileChildren( |
3631 | parentSemanticsClipRect: _semantics?.parentSemanticsClipRect, |
3632 | parentPaintClipRect: _semantics?.parentPaintClipRect, |
3633 | elevationAdjustment: _semantics?.elevationAdjustment ?? 0.0, |
3634 | result: result, |
3635 | siblingNodes: siblingNodes, |
3636 | ); |
3637 | if (!kReleaseMode) { |
3638 | FlutterTimeline.finishSync(); |
3639 | } |
3640 | // Result may contain sibling nodes that are irrelevant for this update. |
3641 | assert(interestingFragment.config == null && result.any((SemanticsNode node) => node == _semantics)); |
3642 | } |
3643 | |
3644 | /// Returns the semantics that this node would like to add to its parent. |
3645 | _SemanticsFragment _getSemanticsForParent({ |
3646 | required bool mergeIntoParent, |
3647 | required bool blockUserActions, |
3648 | }) { |
3649 | assert(!_needsLayout, 'Updated layout information required for $this to calculate semantics.' ); |
3650 | |
3651 | final SemanticsConfiguration config = _semanticsConfiguration; |
3652 | bool dropSemanticsOfPreviousSiblings = config.isBlockingSemanticsOfPreviouslyPaintedNodes; |
3653 | bool producesForkingFragment = !config.hasBeenAnnotated && !config.isSemanticBoundary; |
3654 | final bool blockChildInteractions = blockUserActions || config.isBlockingUserActions; |
3655 | final bool childrenMergeIntoParent = mergeIntoParent || config.isMergingSemanticsOfDescendants; |
3656 | final List<SemanticsConfiguration> childConfigurations = <SemanticsConfiguration>[]; |
3657 | final bool explicitChildNode = config.explicitChildNodes || parent == null; |
3658 | final ChildSemanticsConfigurationsDelegate? childConfigurationsDelegate = config.childConfigurationsDelegate; |
3659 | final Map<SemanticsConfiguration, _InterestingSemanticsFragment> configToFragment = <SemanticsConfiguration, _InterestingSemanticsFragment>{}; |
3660 | final List<_InterestingSemanticsFragment> mergeUpFragments = <_InterestingSemanticsFragment>[]; |
3661 | final List<List<_InterestingSemanticsFragment>> siblingMergeFragmentGroups = <List<_InterestingSemanticsFragment>>[]; |
3662 | final bool hasTags = config.tagsForChildren?.isNotEmpty ?? false; |
3663 | visitChildrenForSemantics((RenderObject renderChild) { |
3664 | assert(!_needsLayout); |
3665 | final _SemanticsFragment parentFragment = renderChild._getSemanticsForParent( |
3666 | mergeIntoParent: childrenMergeIntoParent, |
3667 | blockUserActions: blockChildInteractions, |
3668 | ); |
3669 | if (parentFragment.dropsSemanticsOfPreviousSiblings) { |
3670 | childConfigurations.clear(); |
3671 | mergeUpFragments.clear(); |
3672 | siblingMergeFragmentGroups.clear(); |
3673 | if (!config.isSemanticBoundary) { |
3674 | dropSemanticsOfPreviousSiblings = true; |
3675 | } |
3676 | } |
3677 | for (final _InterestingSemanticsFragment fragment in parentFragment.mergeUpFragments) { |
3678 | fragment.addAncestor(this); |
3679 | if (hasTags) { |
3680 | fragment.addTags(config.tagsForChildren!); |
3681 | } |
3682 | if (childConfigurationsDelegate != null && fragment.config != null) { |
3683 | // This fragment need to go through delegate to determine whether it |
3684 | // merge up or not. |
3685 | childConfigurations.add(fragment.config!); |
3686 | configToFragment[fragment.config!] = fragment; |
3687 | } else { |
3688 | mergeUpFragments.add(fragment); |
3689 | } |
3690 | } |
3691 | if (parentFragment is _ContainerSemanticsFragment) { |
3692 | // Container fragments needs to propagate sibling merge group to be |
3693 | // compiled by _SwitchableSemanticsFragment. |
3694 | for (final List<_InterestingSemanticsFragment> siblingMergeGroup in parentFragment.siblingMergeGroups) { |
3695 | for (final _InterestingSemanticsFragment siblingMergingFragment in siblingMergeGroup) { |
3696 | siblingMergingFragment.addAncestor(this); |
3697 | if (hasTags) { |
3698 | siblingMergingFragment.addTags(config.tagsForChildren!); |
3699 | } |
3700 | } |
3701 | siblingMergeFragmentGroups.add(siblingMergeGroup); |
3702 | } |
3703 | } |
3704 | }); |
3705 | |
3706 | assert(childConfigurationsDelegate != null || configToFragment.isEmpty); |
3707 | |
3708 | if (explicitChildNode) { |
3709 | for (final _InterestingSemanticsFragment fragment in mergeUpFragments) { |
3710 | fragment.markAsExplicit(); |
3711 | } |
3712 | } else if (childConfigurationsDelegate != null) { |
3713 | final ChildSemanticsConfigurationsResult result = childConfigurationsDelegate(childConfigurations); |
3714 | mergeUpFragments.addAll( |
3715 | result.mergeUp.map<_InterestingSemanticsFragment>((SemanticsConfiguration config) { |
3716 | final _InterestingSemanticsFragment? fragment = configToFragment[config]; |
3717 | if (fragment == null) { |
3718 | // Parent fragment of Incomplete fragments can't be a forking |
3719 | // fragment since they need to be merged. |
3720 | producesForkingFragment = false; |
3721 | return _IncompleteSemanticsFragment(config: config, owner: this); |
3722 | } |
3723 | return fragment; |
3724 | }), |
3725 | ); |
3726 | for (final Iterable<SemanticsConfiguration> group in result.siblingMergeGroups) { |
3727 | siblingMergeFragmentGroups.add( |
3728 | group.map<_InterestingSemanticsFragment>((SemanticsConfiguration config) { |
3729 | return configToFragment[config] ?? _IncompleteSemanticsFragment(config: config, owner: this); |
3730 | }).toList(), |
3731 | ); |
3732 | } |
3733 | } |
3734 | |
3735 | _needsSemanticsUpdate = false; |
3736 | |
3737 | final _SemanticsFragment result; |
3738 | if (parent == null) { |
3739 | assert(!config.hasBeenAnnotated); |
3740 | assert(!mergeIntoParent); |
3741 | assert(siblingMergeFragmentGroups.isEmpty); |
3742 | _marksExplicitInMergeGroup(mergeUpFragments, isMergeUp: true); |
3743 | siblingMergeFragmentGroups.forEach(_marksExplicitInMergeGroup); |
3744 | result = _RootSemanticsFragment( |
3745 | owner: this, |
3746 | dropsSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings, |
3747 | ); |
3748 | } else if (producesForkingFragment) { |
3749 | result = _ContainerSemanticsFragment( |
3750 | siblingMergeGroups: siblingMergeFragmentGroups, |
3751 | dropsSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings, |
3752 | ); |
3753 | } else { |
3754 | _marksExplicitInMergeGroup(mergeUpFragments, isMergeUp: true); |
3755 | siblingMergeFragmentGroups.forEach(_marksExplicitInMergeGroup); |
3756 | result = _SwitchableSemanticsFragment( |
3757 | config: config, |
3758 | blockUserActions: blockUserActions, |
3759 | mergeIntoParent: mergeIntoParent, |
3760 | siblingMergeGroups: siblingMergeFragmentGroups, |
3761 | owner: this, |
3762 | dropsSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings, |
3763 | ); |
3764 | if (config.isSemanticBoundary) { |
3765 | final _SwitchableSemanticsFragment fragment = result as _SwitchableSemanticsFragment; |
3766 | fragment.markAsExplicit(); |
3767 | } |
3768 | } |
3769 | result.addAll(mergeUpFragments); |
3770 | return result; |
3771 | } |
3772 | |
3773 | void _marksExplicitInMergeGroup(List<_InterestingSemanticsFragment> mergeGroup, {bool isMergeUp = false}) { |
3774 | final Set<_InterestingSemanticsFragment> toBeExplicit = <_InterestingSemanticsFragment>{}; |
3775 | for (int i = 0; i < mergeGroup.length; i += 1) { |
3776 | final _InterestingSemanticsFragment fragment = mergeGroup[i]; |
3777 | if (!fragment.hasConfigForParent) { |
3778 | continue; |
3779 | } |
3780 | if (isMergeUp && !_semanticsConfiguration.isCompatibleWith(fragment.config)) { |
3781 | toBeExplicit.add(fragment); |
3782 | } |
3783 | final int siblingLength = i; |
3784 | for (int j = 0; j < siblingLength; j += 1) { |
3785 | final _InterestingSemanticsFragment siblingFragment = mergeGroup[j]; |
3786 | if (!fragment.config!.isCompatibleWith(siblingFragment.config)) { |
3787 | toBeExplicit.add(fragment); |
3788 | toBeExplicit.add(siblingFragment); |
3789 | } |
3790 | } |
3791 | } |
3792 | for (final _InterestingSemanticsFragment fragment in toBeExplicit) { |
3793 | fragment.markAsExplicit(); |
3794 | } |
3795 | } |
3796 | |
3797 | /// Called when collecting the semantics of this node. |
3798 | /// |
3799 | /// The implementation has to return the children in paint order skipping all |
3800 | /// children that are not semantically relevant (e.g. because they are |
3801 | /// invisible). |
3802 | /// |
3803 | /// The default implementation mirrors the behavior of |
3804 | /// [visitChildren] (which is supposed to walk all the children). |
3805 | void visitChildrenForSemantics(RenderObjectVisitor visitor) { |
3806 | visitChildren(visitor); |
3807 | } |
3808 | |
3809 | /// Assemble the [SemanticsNode] for this [RenderObject]. |
3810 | /// |
3811 | /// If [describeSemanticsConfiguration] sets |
3812 | /// [SemanticsConfiguration.isSemanticBoundary] to true, this method is called |
3813 | /// with the `node` created for this [RenderObject], the `config` to be |
3814 | /// applied to that node and the `children` [SemanticsNode]s that descendants |
3815 | /// of this RenderObject have generated. |
3816 | /// |
3817 | /// By default, the method will annotate `node` with `config` and add the |
3818 | /// `children` to it. |
3819 | /// |
3820 | /// Subclasses can override this method to add additional [SemanticsNode]s |
3821 | /// to the tree. If new [SemanticsNode]s are instantiated in this method |
3822 | /// they must be disposed in [clearSemantics]. |
3823 | void assembleSemanticsNode( |
3824 | SemanticsNode node, |
3825 | SemanticsConfiguration config, |
3826 | Iterable<SemanticsNode> children, |
3827 | ) { |
3828 | assert(node == _semantics); |
3829 | // TODO(a14n): remove the following cast by updating type of parameter in either updateWith or assembleSemanticsNode |
3830 | node.updateWith(config: config, childrenInInversePaintOrder: children as List<SemanticsNode>); |
3831 | } |
3832 | |
3833 | // EVENTS |
3834 | |
3835 | /// Override this method to handle pointer events that hit this render object. |
3836 | @override |
3837 | void handleEvent(PointerEvent event, covariant HitTestEntry entry) { } |
3838 | |
3839 | |
3840 | // HIT TESTING |
3841 | |
3842 | // RenderObject subclasses are expected to have a method like the following |
3843 | // (with the signature being whatever passes for coordinates for this |
3844 | // particular class): |
3845 | // |
3846 | // bool hitTest(HitTestResult result, { Offset position }) { |
3847 | // // If the given position is not inside this node, then return false. |
3848 | // // Otherwise: |
3849 | // // For each child that intersects the position, in z-order starting from |
3850 | // // the top, call hitTest() for that child, passing it /result/, and the |
3851 | // // coordinates converted to the child's coordinate origin, and stop at |
3852 | // // the first child that returns true. |
3853 | // // Then, add yourself to /result/, and return true. |
3854 | // } |
3855 | // |
3856 | // If you add yourself to /result/ and still return false, then that means you |
3857 | // will see events but so will objects below you. |
3858 | |
3859 | |
3860 | /// Returns a human understandable name. |
3861 | @override |
3862 | String toStringShort() { |
3863 | String header = describeIdentity(this); |
3864 | if (!kReleaseMode) { |
3865 | if (_debugDisposed) { |
3866 | header += ' DISPOSED' ; |
3867 | return header; |
3868 | } |
3869 | if (_relayoutBoundary != null && _relayoutBoundary != this) { |
3870 | int count = 1; |
3871 | RenderObject? target = parent; |
3872 | while (target != null && target != _relayoutBoundary) { |
3873 | target = target.parent; |
3874 | count += 1; |
3875 | } |
3876 | header += ' relayoutBoundary=up $count' ; |
3877 | } |
3878 | if (_needsLayout) { |
3879 | header += ' NEEDS-LAYOUT' ; |
3880 | } |
3881 | if (_needsPaint) { |
3882 | header += ' NEEDS-PAINT' ; |
3883 | } |
3884 | if (_needsCompositingBitsUpdate) { |
3885 | header += ' NEEDS-COMPOSITING-BITS-UPDATE' ; |
3886 | } |
3887 | if (!attached) { |
3888 | header += ' DETACHED' ; |
3889 | } |
3890 | } |
3891 | return header; |
3892 | } |
3893 | |
3894 | @override |
3895 | String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) => toStringShort(); |
3896 | |
3897 | /// Returns a description of the tree rooted at this node. |
3898 | /// If the prefix argument is provided, then every line in the output |
3899 | /// will be prefixed by that string. |
3900 | @override |
3901 | String toStringDeep({ |
3902 | String prefixLineOne = '' , |
3903 | String? prefixOtherLines = '' , |
3904 | DiagnosticLevel minLevel = DiagnosticLevel.debug, |
3905 | }) { |
3906 | return _withDebugActiveLayoutCleared(() => super.toStringDeep( |
3907 | prefixLineOne: prefixLineOne, |
3908 | prefixOtherLines: prefixOtherLines, |
3909 | minLevel: minLevel, |
3910 | )); |
3911 | } |
3912 | |
3913 | /// Returns a one-line detailed description of the render object. |
3914 | /// This description is often somewhat long. |
3915 | /// |
3916 | /// This includes the same information for this RenderObject as given by |
3917 | /// [toStringDeep], but does not recurse to any children. |
3918 | @override |
3919 | String toStringShallow({ |
3920 | String joiner = ', ' , |
3921 | DiagnosticLevel minLevel = DiagnosticLevel.debug, |
3922 | }) { |
3923 | return _withDebugActiveLayoutCleared(() => super.toStringShallow(joiner: joiner, minLevel: minLevel)); |
3924 | } |
3925 | |
3926 | @protected |
3927 | @override |
3928 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3929 | super.debugFillProperties(properties); |
3930 | properties.add(FlagProperty('needsCompositing' , value: _needsCompositing, ifTrue: 'needs compositing' )); |
3931 | properties.add(DiagnosticsProperty<Object?>('creator' , debugCreator, defaultValue: null, level: DiagnosticLevel.debug)); |
3932 | properties.add(DiagnosticsProperty<ParentData>('parentData' , parentData, tooltip: (_debugCanParentUseSize ?? false) ? 'can use size' : null, missingIfNull: true)); |
3933 | properties.add(DiagnosticsProperty<Constraints>('constraints' , _constraints, missingIfNull: true)); |
3934 | // don't access it via the "layer" getter since that's only valid when we don't need paint |
3935 | properties.add(DiagnosticsProperty<ContainerLayer>('layer' , _layerHandle.layer, defaultValue: null)); |
3936 | properties.add(DiagnosticsProperty<SemanticsNode>('semantics node' , _semantics, defaultValue: null)); |
3937 | properties.add(FlagProperty( |
3938 | 'isBlockingSemanticsOfPreviouslyPaintedNodes' , |
3939 | value: _semanticsConfiguration.isBlockingSemanticsOfPreviouslyPaintedNodes, |
3940 | ifTrue: 'blocks semantics of earlier render objects below the common boundary' , |
3941 | )); |
3942 | properties.add(FlagProperty('isSemanticBoundary' , value: _semanticsConfiguration.isSemanticBoundary, ifTrue: 'semantic boundary' )); |
3943 | } |
3944 | |
3945 | @override |
3946 | List<DiagnosticsNode> debugDescribeChildren() => <DiagnosticsNode>[]; |
3947 | |
3948 | /// Attempt to make (a portion of) this or a descendant [RenderObject] visible |
3949 | /// on screen. |
3950 | /// |
3951 | /// If `descendant` is provided, that [RenderObject] is made visible. If |
3952 | /// `descendant` is omitted, this [RenderObject] is made visible. |
3953 | /// |
3954 | /// The optional `rect` parameter describes which area of that [RenderObject] |
3955 | /// should be shown on screen. If `rect` is null, the entire |
3956 | /// [RenderObject] (as defined by its [paintBounds]) will be revealed. The |
3957 | /// `rect` parameter is interpreted relative to the coordinate system of |
3958 | /// `descendant` if that argument is provided and relative to this |
3959 | /// [RenderObject] otherwise. |
3960 | /// |
3961 | /// The `duration` parameter can be set to a non-zero value to bring the |
3962 | /// target object on screen in an animation defined by `curve`. |
3963 | /// |
3964 | /// See also: |
3965 | /// |
3966 | /// * [RenderViewportBase.showInViewport], which [RenderViewportBase] and |
3967 | /// [SingleChildScrollView] delegate this method to. |
3968 | void showOnScreen({ |
3969 | RenderObject? descendant, |
3970 | Rect? rect, |
3971 | Duration duration = Duration.zero, |
3972 | Curve curve = Curves.ease, |
3973 | }) { |
3974 | if (parent is RenderObject) { |
3975 | parent!.showOnScreen( |
3976 | descendant: descendant ?? this, |
3977 | rect: rect, |
3978 | duration: duration, |
3979 | curve: curve, |
3980 | ); |
3981 | } |
3982 | } |
3983 | |
3984 | /// Adds a debug representation of a [RenderObject] optimized for including in |
3985 | /// error messages. |
3986 | /// |
3987 | /// The default [style] of [DiagnosticsTreeStyle.shallow] ensures that all of |
3988 | /// the properties of the render object are included in the error output but |
3989 | /// none of the children of the object are. |
3990 | /// |
3991 | /// You should always include a RenderObject in an error message if it is the |
3992 | /// [RenderObject] causing the failure or contract violation of the error. |
3993 | DiagnosticsNode describeForError(String name, { DiagnosticsTreeStyle style = DiagnosticsTreeStyle.shallow }) { |
3994 | return toDiagnosticsNode(name: name, style: style); |
3995 | } |
3996 | } |
3997 | |
3998 | /// Generic mixin for render objects with one child. |
3999 | /// |
4000 | /// Provides a child model for a render object subclass that has |
4001 | /// a unique child, which is accessible via the [child] getter. |
4002 | /// |
4003 | /// This mixin is typically used to implement render objects created |
4004 | /// in a [SingleChildRenderObjectWidget]. |
4005 | mixin RenderObjectWithChildMixin<ChildType extends RenderObject> on RenderObject { |
4006 | /// Checks whether the given render object has the correct [runtimeType] to be |
4007 | /// a child of this render object. |
4008 | /// |
4009 | /// Does nothing if assertions are disabled. |
4010 | /// |
4011 | /// Always returns true. |
4012 | bool debugValidateChild(RenderObject child) { |
4013 | assert(() { |
4014 | if (child is! ChildType) { |
4015 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4016 | ErrorSummary( |
4017 | 'A $runtimeType expected a child of type $ChildType but received a ' |
4018 | 'child of type ${child.runtimeType}.' , |
4019 | ), |
4020 | ErrorDescription( |
4021 | 'RenderObjects expect specific types of children because they ' |
4022 | 'coordinate with their children during layout and paint. For ' |
4023 | 'example, a RenderSliver cannot be the child of a RenderBox because ' |
4024 | 'a RenderSliver does not understand the RenderBox layout protocol.' , |
4025 | ), |
4026 | ErrorSpacer(), |
4027 | DiagnosticsProperty<Object?>( |
4028 | 'The $runtimeType that expected a $ChildType child was created by' , |
4029 | debugCreator, |
4030 | style: DiagnosticsTreeStyle.errorProperty, |
4031 | ), |
4032 | ErrorSpacer(), |
4033 | DiagnosticsProperty<Object?>( |
4034 | 'The ${child.runtimeType} that did not match the expected child type ' |
4035 | 'was created by' , |
4036 | child.debugCreator, |
4037 | style: DiagnosticsTreeStyle.errorProperty, |
4038 | ), |
4039 | ]); |
4040 | } |
4041 | return true; |
4042 | }()); |
4043 | return true; |
4044 | } |
4045 | |
4046 | ChildType? _child; |
4047 | /// The render object's unique child. |
4048 | ChildType? get child => _child; |
4049 | set child(ChildType? value) { |
4050 | if (_child != null) { |
4051 | dropChild(_child!); |
4052 | } |
4053 | _child = value; |
4054 | if (_child != null) { |
4055 | adoptChild(_child!); |
4056 | } |
4057 | } |
4058 | |
4059 | @override |
4060 | void attach(PipelineOwner owner) { |
4061 | super.attach(owner); |
4062 | _child?.attach(owner); |
4063 | } |
4064 | |
4065 | @override |
4066 | void detach() { |
4067 | super.detach(); |
4068 | _child?.detach(); |
4069 | } |
4070 | |
4071 | @override |
4072 | void redepthChildren() { |
4073 | if (_child != null) { |
4074 | redepthChild(_child!); |
4075 | } |
4076 | } |
4077 | |
4078 | @override |
4079 | void visitChildren(RenderObjectVisitor visitor) { |
4080 | if (_child != null) { |
4081 | visitor(_child!); |
4082 | } |
4083 | } |
4084 | |
4085 | @override |
4086 | List<DiagnosticsNode> debugDescribeChildren() { |
4087 | return child != null ? <DiagnosticsNode>[child!.toDiagnosticsNode(name: 'child' )] : <DiagnosticsNode>[]; |
4088 | } |
4089 | } |
4090 | |
4091 | /// Parent data to support a doubly-linked list of children. |
4092 | /// |
4093 | /// The children can be traversed using [nextSibling] or [previousSibling], |
4094 | /// which can be called on the parent data of the render objects |
4095 | /// obtained via [ContainerRenderObjectMixin.firstChild] or |
4096 | /// [ContainerRenderObjectMixin.lastChild]. |
4097 | mixin ContainerParentDataMixin<ChildType extends RenderObject> on ParentData { |
4098 | /// The previous sibling in the parent's child list. |
4099 | ChildType? previousSibling; |
4100 | /// The next sibling in the parent's child list. |
4101 | ChildType? nextSibling; |
4102 | |
4103 | /// Clear the sibling pointers. |
4104 | @override |
4105 | void detach() { |
4106 | assert(previousSibling == null, 'Pointers to siblings must be nulled before detaching ParentData.' ); |
4107 | assert(nextSibling == null, 'Pointers to siblings must be nulled before detaching ParentData.' ); |
4108 | super.detach(); |
4109 | } |
4110 | } |
4111 | |
4112 | /// Generic mixin for render objects with a list of children. |
4113 | /// |
4114 | /// Provides a child model for a render object subclass that has a doubly-linked |
4115 | /// list of children. |
4116 | /// |
4117 | /// The [ChildType] specifies the type of the children (extending [RenderObject]), |
4118 | /// e.g. [RenderBox]. |
4119 | /// |
4120 | /// [ParentDataType] stores parent container data on its child render objects. |
4121 | /// It must extend [ContainerParentDataMixin], which provides the interface |
4122 | /// for visiting children. This data is populated by |
4123 | /// [RenderObject.setupParentData] implemented by the class using this mixin. |
4124 | /// |
4125 | /// When using [RenderBox] as the child type, you will usually want to make use of |
4126 | /// [RenderBoxContainerDefaultsMixin] and extend [ContainerBoxParentData] for the |
4127 | /// parent data. |
4128 | /// |
4129 | /// Moreover, this is a required mixin for render objects returned to [MultiChildRenderObjectWidget]. |
4130 | /// |
4131 | /// See also: |
4132 | /// |
4133 | /// * [SlottedContainerRenderObjectMixin], which organizes its children |
4134 | /// in different named slots. |
4135 | mixin ContainerRenderObjectMixin<ChildType extends RenderObject, ParentDataType extends ContainerParentDataMixin<ChildType>> on RenderObject { |
4136 | bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType? equals }) { |
4137 | ParentDataType childParentData = child.parentData! as ParentDataType; |
4138 | while (childParentData.previousSibling != null) { |
4139 | assert(childParentData.previousSibling != child); |
4140 | child = childParentData.previousSibling!; |
4141 | childParentData = child.parentData! as ParentDataType; |
4142 | } |
4143 | return child == equals; |
4144 | } |
4145 | bool _debugUltimateNextSiblingOf(ChildType child, { ChildType? equals }) { |
4146 | ParentDataType childParentData = child.parentData! as ParentDataType; |
4147 | while (childParentData.nextSibling != null) { |
4148 | assert(childParentData.nextSibling != child); |
4149 | child = childParentData.nextSibling!; |
4150 | childParentData = child.parentData! as ParentDataType; |
4151 | } |
4152 | return child == equals; |
4153 | } |
4154 | |
4155 | int _childCount = 0; |
4156 | /// The number of children. |
4157 | int get childCount => _childCount; |
4158 | |
4159 | /// Checks whether the given render object has the correct [runtimeType] to be |
4160 | /// a child of this render object. |
4161 | /// |
4162 | /// Does nothing if assertions are disabled. |
4163 | /// |
4164 | /// Always returns true. |
4165 | bool debugValidateChild(RenderObject child) { |
4166 | assert(() { |
4167 | if (child is! ChildType) { |
4168 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4169 | ErrorSummary( |
4170 | 'A $runtimeType expected a child of type $ChildType but received a ' |
4171 | 'child of type ${child.runtimeType}.' , |
4172 | ), |
4173 | ErrorDescription( |
4174 | 'RenderObjects expect specific types of children because they ' |
4175 | 'coordinate with their children during layout and paint. For ' |
4176 | 'example, a RenderSliver cannot be the child of a RenderBox because ' |
4177 | 'a RenderSliver does not understand the RenderBox layout protocol.' , |
4178 | ), |
4179 | ErrorSpacer(), |
4180 | DiagnosticsProperty<Object?>( |
4181 | 'The $runtimeType that expected a $ChildType child was created by' , |
4182 | debugCreator, |
4183 | style: DiagnosticsTreeStyle.errorProperty, |
4184 | ), |
4185 | ErrorSpacer(), |
4186 | DiagnosticsProperty<Object?>( |
4187 | 'The ${child.runtimeType} that did not match the expected child type ' |
4188 | 'was created by' , |
4189 | child.debugCreator, |
4190 | style: DiagnosticsTreeStyle.errorProperty, |
4191 | ), |
4192 | ]); |
4193 | } |
4194 | return true; |
4195 | }()); |
4196 | return true; |
4197 | } |
4198 | |
4199 | ChildType? _firstChild; |
4200 | ChildType? _lastChild; |
4201 | void _insertIntoChildList(ChildType child, { ChildType? after }) { |
4202 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4203 | assert(childParentData.nextSibling == null); |
4204 | assert(childParentData.previousSibling == null); |
4205 | _childCount += 1; |
4206 | assert(_childCount > 0); |
4207 | if (after == null) { |
4208 | // insert at the start (_firstChild) |
4209 | childParentData.nextSibling = _firstChild; |
4210 | if (_firstChild != null) { |
4211 | final ParentDataType firstChildParentData = _firstChild!.parentData! as ParentDataType; |
4212 | firstChildParentData.previousSibling = child; |
4213 | } |
4214 | _firstChild = child; |
4215 | _lastChild ??= child; |
4216 | } else { |
4217 | assert(_firstChild != null); |
4218 | assert(_lastChild != null); |
4219 | assert(_debugUltimatePreviousSiblingOf(after, equals: _firstChild)); |
4220 | assert(_debugUltimateNextSiblingOf(after, equals: _lastChild)); |
4221 | final ParentDataType afterParentData = after.parentData! as ParentDataType; |
4222 | if (afterParentData.nextSibling == null) { |
4223 | // insert at the end (_lastChild); we'll end up with two or more children |
4224 | assert(after == _lastChild); |
4225 | childParentData.previousSibling = after; |
4226 | afterParentData.nextSibling = child; |
4227 | _lastChild = child; |
4228 | } else { |
4229 | // insert in the middle; we'll end up with three or more children |
4230 | // set up links from child to siblings |
4231 | childParentData.nextSibling = afterParentData.nextSibling; |
4232 | childParentData.previousSibling = after; |
4233 | // set up links from siblings to child |
4234 | final ParentDataType childPreviousSiblingParentData = childParentData.previousSibling!.parentData! as ParentDataType; |
4235 | final ParentDataType childNextSiblingParentData = childParentData.nextSibling!.parentData! as ParentDataType; |
4236 | childPreviousSiblingParentData.nextSibling = child; |
4237 | childNextSiblingParentData.previousSibling = child; |
4238 | assert(afterParentData.nextSibling == child); |
4239 | } |
4240 | } |
4241 | } |
4242 | |
4243 | /// Insert child into this render object's child list after the given child. |
4244 | /// |
4245 | /// If `after` is null, then this inserts the child at the start of the list, |
4246 | /// and the child becomes the new [firstChild]. |
4247 | void insert(ChildType child, { ChildType? after }) { |
4248 | assert(child != this, 'A RenderObject cannot be inserted into itself.' ); |
4249 | assert(after != this, 'A RenderObject cannot simultaneously be both the parent and the sibling of another RenderObject.' ); |
4250 | assert(child != after, 'A RenderObject cannot be inserted after itself.' ); |
4251 | assert(child != _firstChild); |
4252 | assert(child != _lastChild); |
4253 | adoptChild(child); |
4254 | _insertIntoChildList(child, after: after); |
4255 | } |
4256 | |
4257 | /// Append child to the end of this render object's child list. |
4258 | void add(ChildType child) { |
4259 | insert(child, after: _lastChild); |
4260 | } |
4261 | |
4262 | /// Add all the children to the end of this render object's child list. |
4263 | void addAll(List<ChildType>? children) { |
4264 | children?.forEach(add); |
4265 | } |
4266 | |
4267 | void _removeFromChildList(ChildType child) { |
4268 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4269 | assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild)); |
4270 | assert(_debugUltimateNextSiblingOf(child, equals: _lastChild)); |
4271 | assert(_childCount >= 0); |
4272 | if (childParentData.previousSibling == null) { |
4273 | assert(_firstChild == child); |
4274 | _firstChild = childParentData.nextSibling; |
4275 | } else { |
4276 | final ParentDataType childPreviousSiblingParentData = childParentData.previousSibling!.parentData! as ParentDataType; |
4277 | childPreviousSiblingParentData.nextSibling = childParentData.nextSibling; |
4278 | } |
4279 | if (childParentData.nextSibling == null) { |
4280 | assert(_lastChild == child); |
4281 | _lastChild = childParentData.previousSibling; |
4282 | } else { |
4283 | final ParentDataType childNextSiblingParentData = childParentData.nextSibling!.parentData! as ParentDataType; |
4284 | childNextSiblingParentData.previousSibling = childParentData.previousSibling; |
4285 | } |
4286 | childParentData.previousSibling = null; |
4287 | childParentData.nextSibling = null; |
4288 | _childCount -= 1; |
4289 | } |
4290 | |
4291 | /// Remove this child from the child list. |
4292 | /// |
4293 | /// Requires the child to be present in the child list. |
4294 | void remove(ChildType child) { |
4295 | _removeFromChildList(child); |
4296 | dropChild(child); |
4297 | } |
4298 | |
4299 | /// Remove all their children from this render object's child list. |
4300 | /// |
4301 | /// More efficient than removing them individually. |
4302 | void removeAll() { |
4303 | ChildType? child = _firstChild; |
4304 | while (child != null) { |
4305 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4306 | final ChildType? next = childParentData.nextSibling; |
4307 | childParentData.previousSibling = null; |
4308 | childParentData.nextSibling = null; |
4309 | dropChild(child); |
4310 | child = next; |
4311 | } |
4312 | _firstChild = null; |
4313 | _lastChild = null; |
4314 | _childCount = 0; |
4315 | } |
4316 | |
4317 | /// Move the given `child` in the child list to be after another child. |
4318 | /// |
4319 | /// More efficient than removing and re-adding the child. Requires the child |
4320 | /// to already be in the child list at some position. Pass null for `after` to |
4321 | /// move the child to the start of the child list. |
4322 | void move(ChildType child, { ChildType? after }) { |
4323 | assert(child != this); |
4324 | assert(after != this); |
4325 | assert(child != after); |
4326 | assert(child.parent == this); |
4327 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4328 | if (childParentData.previousSibling == after) { |
4329 | return; |
4330 | } |
4331 | _removeFromChildList(child); |
4332 | _insertIntoChildList(child, after: after); |
4333 | markNeedsLayout(); |
4334 | } |
4335 | |
4336 | @override |
4337 | void attach(PipelineOwner owner) { |
4338 | super.attach(owner); |
4339 | ChildType? child = _firstChild; |
4340 | while (child != null) { |
4341 | child.attach(owner); |
4342 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4343 | child = childParentData.nextSibling; |
4344 | } |
4345 | } |
4346 | |
4347 | @override |
4348 | void detach() { |
4349 | super.detach(); |
4350 | ChildType? child = _firstChild; |
4351 | while (child != null) { |
4352 | child.detach(); |
4353 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4354 | child = childParentData.nextSibling; |
4355 | } |
4356 | } |
4357 | |
4358 | @override |
4359 | void redepthChildren() { |
4360 | ChildType? child = _firstChild; |
4361 | while (child != null) { |
4362 | redepthChild(child); |
4363 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4364 | child = childParentData.nextSibling; |
4365 | } |
4366 | } |
4367 | |
4368 | @override |
4369 | void visitChildren(RenderObjectVisitor visitor) { |
4370 | ChildType? child = _firstChild; |
4371 | while (child != null) { |
4372 | visitor(child); |
4373 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4374 | child = childParentData.nextSibling; |
4375 | } |
4376 | } |
4377 | |
4378 | /// The first child in the child list. |
4379 | ChildType? get firstChild => _firstChild; |
4380 | |
4381 | /// The last child in the child list. |
4382 | ChildType? get lastChild => _lastChild; |
4383 | |
4384 | /// The previous child before the given child in the child list. |
4385 | ChildType? childBefore(ChildType child) { |
4386 | assert(child.parent == this); |
4387 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4388 | return childParentData.previousSibling; |
4389 | } |
4390 | |
4391 | /// The next child after the given child in the child list. |
4392 | ChildType? childAfter(ChildType child) { |
4393 | assert(child.parent == this); |
4394 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4395 | return childParentData.nextSibling; |
4396 | } |
4397 | |
4398 | @override |
4399 | List<DiagnosticsNode> debugDescribeChildren() { |
4400 | final List<DiagnosticsNode> children = <DiagnosticsNode>[]; |
4401 | if (firstChild != null) { |
4402 | ChildType child = firstChild!; |
4403 | int count = 1; |
4404 | while (true) { |
4405 | children.add(child.toDiagnosticsNode(name: 'child $count' )); |
4406 | if (child == lastChild) { |
4407 | break; |
4408 | } |
4409 | count += 1; |
4410 | final ParentDataType childParentData = child.parentData! as ParentDataType; |
4411 | child = childParentData.nextSibling!; |
4412 | } |
4413 | } |
4414 | return children; |
4415 | } |
4416 | } |
4417 | |
4418 | /// Mixin for [RenderObject] that will call [systemFontsDidChange] whenever the |
4419 | /// system fonts change. |
4420 | /// |
4421 | /// System fonts can change when the OS installs or removes a font. Use this |
4422 | /// mixin if the [RenderObject] uses [TextPainter] or [Paragraph] to correctly |
4423 | /// update the text when it happens. |
4424 | mixin RelayoutWhenSystemFontsChangeMixin on RenderObject { |
4425 | |
4426 | /// A callback that is called when system fonts have changed. |
4427 | /// |
4428 | /// The framework defers the invocation of the callback to the |
4429 | /// [SchedulerPhase.transientCallbacks] phase to ensure that the |
4430 | /// [RenderObject]'s text layout is still valid when user interactions are in |
4431 | /// progress (which usually take place during the [SchedulerPhase.idle] phase). |
4432 | /// |
4433 | /// By default, [markNeedsLayout] is called on the [RenderObject] |
4434 | /// implementing this mixin. |
4435 | /// |
4436 | /// Subclass should override this method to clear any extra cache that depend |
4437 | /// on font-related metrics. |
4438 | @protected |
4439 | @mustCallSuper |
4440 | void systemFontsDidChange() { |
4441 | markNeedsLayout(); |
4442 | } |
4443 | |
4444 | bool _hasPendingSystemFontsDidChangeCallBack = false; |
4445 | void _scheduleSystemFontsUpdate() { |
4446 | assert( |
4447 | SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle, |
4448 | ' ${objectRuntimeType(this, "RelayoutWhenSystemFontsChangeMixin" )}._scheduleSystemFontsUpdate() ' |
4449 | 'called during ${SchedulerBinding.instance.schedulerPhase}.' , |
4450 | ); |
4451 | if (_hasPendingSystemFontsDidChangeCallBack) { |
4452 | return; |
4453 | } |
4454 | _hasPendingSystemFontsDidChangeCallBack = true; |
4455 | SchedulerBinding.instance.scheduleFrameCallback((Duration timeStamp) { |
4456 | assert(_hasPendingSystemFontsDidChangeCallBack); |
4457 | _hasPendingSystemFontsDidChangeCallBack = false; |
4458 | assert( |
4459 | attached || (debugDisposed ?? true), |
4460 | ' $this is detached during ${SchedulerBinding.instance.schedulerPhase} but is not disposed.' , |
4461 | ); |
4462 | if (attached) { |
4463 | systemFontsDidChange(); |
4464 | } |
4465 | }); |
4466 | } |
4467 | |
4468 | @override |
4469 | void attach(PipelineOwner owner) { |
4470 | super.attach(owner); |
4471 | // If there's a pending callback that would imply this node was detached |
4472 | // between the idle phase and the next transientCallbacks phase. The tree |
4473 | // can not be mutated between those two phases so that should never happen. |
4474 | assert(!_hasPendingSystemFontsDidChangeCallBack); |
4475 | PaintingBinding.instance.systemFonts.addListener(_scheduleSystemFontsUpdate); |
4476 | } |
4477 | |
4478 | @override |
4479 | void detach() { |
4480 | assert(!_hasPendingSystemFontsDidChangeCallBack); |
4481 | PaintingBinding.instance.systemFonts.removeListener(_scheduleSystemFontsUpdate); |
4482 | super.detach(); |
4483 | } |
4484 | } |
4485 | |
4486 | /// Describes the semantics information a [RenderObject] wants to add to its |
4487 | /// parent. |
4488 | /// |
4489 | /// It has two notable subclasses: |
4490 | /// * [_InterestingSemanticsFragment] describing actual semantic information to |
4491 | /// be added to the parent. |
4492 | /// * [_ContainerSemanticsFragment]: a container class to transport the semantic |
4493 | /// information of multiple [_InterestingSemanticsFragment] to a parent. |
4494 | abstract class _SemanticsFragment { |
4495 | _SemanticsFragment({ |
4496 | required this.dropsSemanticsOfPreviousSiblings, |
4497 | }); |
4498 | |
4499 | /// Incorporate the fragments of children into this fragment. |
4500 | void addAll(Iterable<_InterestingSemanticsFragment> fragments); |
4501 | |
4502 | /// Whether this fragment wants to make the semantics information of |
4503 | /// previously painted [RenderObject]s unreachable for accessibility purposes. |
4504 | /// |
4505 | /// See also: |
4506 | /// |
4507 | /// * [SemanticsConfiguration.isBlockingSemanticsOfPreviouslyPaintedNodes] |
4508 | /// describes what semantics are dropped in more detail. |
4509 | final bool dropsSemanticsOfPreviousSiblings; |
4510 | |
4511 | /// Returns [_InterestingSemanticsFragment] describing the actual semantic |
4512 | /// information that this fragment wants to add to the parent. |
4513 | List<_InterestingSemanticsFragment> get mergeUpFragments; |
4514 | } |
4515 | |
4516 | /// A container used when a [RenderObject] wants to add multiple independent |
4517 | /// [_InterestingSemanticsFragment] to its parent. |
4518 | /// |
4519 | /// The [_InterestingSemanticsFragment] to be added to the parent can be |
4520 | /// obtained via [mergeUpFragments]. |
4521 | class _ContainerSemanticsFragment extends _SemanticsFragment { |
4522 | _ContainerSemanticsFragment({ |
4523 | required super.dropsSemanticsOfPreviousSiblings, |
4524 | required this.siblingMergeGroups, |
4525 | }); |
4526 | |
4527 | final List<List<_InterestingSemanticsFragment>> siblingMergeGroups; |
4528 | |
4529 | @override |
4530 | void addAll(Iterable<_InterestingSemanticsFragment> fragments) { |
4531 | mergeUpFragments.addAll(fragments); |
4532 | } |
4533 | |
4534 | @override |
4535 | final List<_InterestingSemanticsFragment> mergeUpFragments = <_InterestingSemanticsFragment>[]; |
4536 | } |
4537 | |
4538 | /// A [_SemanticsFragment] that describes which concrete semantic information |
4539 | /// a [RenderObject] wants to add to the [SemanticsNode] of its parent. |
4540 | /// |
4541 | /// Specifically, it describes which children (as returned by [compileChildren]) |
4542 | /// should be added to the parent's [SemanticsNode] and which [config] should be |
4543 | /// merged into the parent's [SemanticsNode]. |
4544 | abstract class _InterestingSemanticsFragment extends _SemanticsFragment { |
4545 | _InterestingSemanticsFragment({ |
4546 | required RenderObject owner, |
4547 | required super.dropsSemanticsOfPreviousSiblings, |
4548 | }) : _ancestorChain = <RenderObject>[owner]; |
4549 | |
4550 | /// The [RenderObject] that owns this fragment (and any new [SemanticsNode] |
4551 | /// introduced by it). |
4552 | RenderObject get owner => _ancestorChain.first; |
4553 | |
4554 | final List<RenderObject> _ancestorChain; |
4555 | |
4556 | /// The children to be added to the parent. |
4557 | /// |
4558 | /// See also: |
4559 | /// |
4560 | /// * [SemanticsNode.parentSemanticsClipRect] for the source and definition |
4561 | /// of the `parentSemanticsClipRect` argument. |
4562 | /// * [SemanticsNode.parentPaintClipRect] for the source and definition |
4563 | /// of the `parentPaintClipRect` argument. |
4564 | /// * [SemanticsNode.elevationAdjustment] for the source and definition |
4565 | /// of the `elevationAdjustment` argument. |
4566 | void compileChildren({ |
4567 | required Rect? parentSemanticsClipRect, |
4568 | required Rect? parentPaintClipRect, |
4569 | required double elevationAdjustment, |
4570 | required List<SemanticsNode> result, |
4571 | required List<SemanticsNode> siblingNodes, |
4572 | }); |
4573 | |
4574 | /// The [SemanticsConfiguration] the child wants to merge into the parent's |
4575 | /// [SemanticsNode] or null if it doesn't want to merge anything. |
4576 | SemanticsConfiguration? get config; |
4577 | |
4578 | /// Disallows this fragment to merge any configuration into its parent's |
4579 | /// [SemanticsNode]. |
4580 | /// |
4581 | /// After calling this, the fragment will only produce children to be added |
4582 | /// to the parent and it will return null for [config]. |
4583 | void markAsExplicit(); |
4584 | |
4585 | /// Consume the fragments of children. |
4586 | /// |
4587 | /// For each provided fragment it will add that fragment's children to |
4588 | /// this fragment's children (as returned by [compileChildren]) and merge that |
4589 | /// fragment's [config] into this fragment's [config]. |
4590 | /// |
4591 | /// If a provided fragment should not merge anything into [config] call |
4592 | /// [markAsExplicit] before passing the fragment to this method. |
4593 | @override |
4594 | void addAll(Iterable<_InterestingSemanticsFragment> fragments); |
4595 | |
4596 | /// Whether this fragment wants to add any semantic information to the parent |
4597 | /// [SemanticsNode]. |
4598 | bool get hasConfigForParent => config != null; |
4599 | |
4600 | @override |
4601 | List<_InterestingSemanticsFragment> get mergeUpFragments => <_InterestingSemanticsFragment>[this]; |
4602 | |
4603 | Set<SemanticsTag>? _tagsForChildren; |
4604 | |
4605 | /// Tag all children produced by [compileChildren] with `tags`. |
4606 | /// |
4607 | /// `tags` must not be empty. |
4608 | void addTags(Iterable<SemanticsTag> tags) { |
4609 | assert(tags.isNotEmpty); |
4610 | _tagsForChildren ??= <SemanticsTag>{}; |
4611 | _tagsForChildren!.addAll(tags); |
4612 | } |
4613 | |
4614 | /// Adds the geometric information of `ancestor` to this object. |
4615 | /// |
4616 | /// Those information are required to properly compute the value for |
4617 | /// [SemanticsNode.transform], [SemanticsNode.clipRect], and |
4618 | /// [SemanticsNode.rect]. |
4619 | /// |
4620 | /// Ancestors have to be added in order from [owner] up until the next |
4621 | /// [RenderObject] that owns a [SemanticsNode] is reached. |
4622 | void addAncestor(RenderObject ancestor) { |
4623 | _ancestorChain.add(ancestor); |
4624 | } |
4625 | } |
4626 | |
4627 | /// An [_InterestingSemanticsFragment] that produces the root [SemanticsNode] of |
4628 | /// the semantics tree. |
4629 | /// |
4630 | /// The root node is available as the only element in the Iterable returned by |
4631 | /// [children]. |
4632 | class _RootSemanticsFragment extends _InterestingSemanticsFragment { |
4633 | _RootSemanticsFragment({ |
4634 | required super.owner, |
4635 | required super.dropsSemanticsOfPreviousSiblings, |
4636 | }); |
4637 | |
4638 | @override |
4639 | void compileChildren({ |
4640 | Rect? parentSemanticsClipRect, |
4641 | Rect? parentPaintClipRect, |
4642 | required double elevationAdjustment, |
4643 | required List<SemanticsNode> result, |
4644 | required List<SemanticsNode> siblingNodes, |
4645 | }) { |
4646 | assert(_tagsForChildren == null || _tagsForChildren!.isEmpty); |
4647 | assert(parentSemanticsClipRect == null); |
4648 | assert(parentPaintClipRect == null); |
4649 | assert(_ancestorChain.length == 1); |
4650 | assert(elevationAdjustment == 0.0); |
4651 | |
4652 | owner._semantics ??= SemanticsNode.root( |
4653 | showOnScreen: owner.showOnScreen, |
4654 | owner: owner.owner!.semanticsOwner!, |
4655 | ); |
4656 | final SemanticsNode node = owner._semantics!; |
4657 | assert(MatrixUtils.matrixEquals(node.transform, Matrix4.identity())); |
4658 | assert(node.parentSemanticsClipRect == null); |
4659 | assert(node.parentPaintClipRect == null); |
4660 | |
4661 | node.rect = owner.semanticBounds; |
4662 | |
4663 | final List<SemanticsNode> children = <SemanticsNode>[]; |
4664 | for (final _InterestingSemanticsFragment fragment in _children) { |
4665 | assert(fragment.config == null); |
4666 | fragment.compileChildren( |
4667 | parentSemanticsClipRect: parentSemanticsClipRect, |
4668 | parentPaintClipRect: parentPaintClipRect, |
4669 | elevationAdjustment: 0.0, |
4670 | result: children, |
4671 | siblingNodes: siblingNodes, |
4672 | ); |
4673 | } |
4674 | // Root node does not have a parent and thus can't attach sibling nodes. |
4675 | assert(siblingNodes.isEmpty); |
4676 | node.updateWith(config: null, childrenInInversePaintOrder: children); |
4677 | |
4678 | // The root node is the only semantics node allowed to be invisible. This |
4679 | // can happen when the canvas the app is drawn on has a size of 0 by 0 |
4680 | // pixel. If this happens, the root node must not have any children (because |
4681 | // these would be invisible as well and are therefore excluded from the |
4682 | // tree). |
4683 | assert(!node.isInvisible || children.isEmpty); |
4684 | result.add(node); |
4685 | } |
4686 | |
4687 | @override |
4688 | SemanticsConfiguration? get config => null; |
4689 | |
4690 | final List<_InterestingSemanticsFragment> _children = <_InterestingSemanticsFragment>[]; |
4691 | |
4692 | @override |
4693 | void markAsExplicit() { |
4694 | // nothing to do, we are always explicit. |
4695 | } |
4696 | |
4697 | @override |
4698 | void addAll(Iterable<_InterestingSemanticsFragment> fragments) { |
4699 | _children.addAll(fragments); |
4700 | } |
4701 | } |
4702 | |
4703 | /// A fragment with partial information that must not form an explicit |
4704 | /// semantics node without merging into another _SwitchableSemanticsFragment. |
4705 | /// |
4706 | /// This fragment is generated from synthetic SemanticsConfiguration returned from |
4707 | /// [SemanticsConfiguration.childConfigurationsDelegate]. |
4708 | class _IncompleteSemanticsFragment extends _InterestingSemanticsFragment { |
4709 | _IncompleteSemanticsFragment({ |
4710 | required this.config, |
4711 | required super.owner, |
4712 | }) : super(dropsSemanticsOfPreviousSiblings: false); |
4713 | |
4714 | @override |
4715 | void addAll(Iterable<_InterestingSemanticsFragment> fragments) { |
4716 | assert(false, 'This fragment must be a leaf node' ); |
4717 | } |
4718 | |
4719 | @override |
4720 | void compileChildren({ |
4721 | required Rect? parentSemanticsClipRect, |
4722 | required Rect? parentPaintClipRect, |
4723 | required double elevationAdjustment, |
4724 | required List<SemanticsNode> result, |
4725 | required List<SemanticsNode> siblingNodes, |
4726 | }) { |
4727 | // There is nothing to do because this fragment must be a leaf node and |
4728 | // must not be explicit. |
4729 | } |
4730 | |
4731 | @override |
4732 | final SemanticsConfiguration config; |
4733 | |
4734 | @override |
4735 | void markAsExplicit() { |
4736 | assert( |
4737 | false, |
4738 | 'SemanticsConfiguration created in ' |
4739 | 'SemanticsConfiguration.childConfigurationsDelegate must not produce ' |
4740 | 'its own semantics node' |
4741 | ); |
4742 | } |
4743 | } |
4744 | |
4745 | /// An [_InterestingSemanticsFragment] that can be told to only add explicit |
4746 | /// [SemanticsNode]s to the parent. |
4747 | /// |
4748 | /// If [markAsExplicit] was not called before this fragment is added to |
4749 | /// another fragment it will merge [config] into the parent's [SemanticsNode] |
4750 | /// and add its [children] to it. |
4751 | /// |
4752 | /// If [markAsExplicit] was called before adding this fragment to another |
4753 | /// fragment it will create a new [SemanticsNode]. The newly created node will |
4754 | /// be annotated with the [SemanticsConfiguration] that - without the call to |
4755 | /// [markAsExplicit] - would have been merged into the parent's [SemanticsNode]. |
4756 | /// Similarly, the new node will also take over the children that otherwise |
4757 | /// would have been added to the parent's [SemanticsNode]. |
4758 | /// |
4759 | /// After a call to [markAsExplicit] the only element returned by [children] |
4760 | /// is the newly created node and [config] will return null as the fragment |
4761 | /// no longer wants to merge any semantic information into the parent's |
4762 | /// [SemanticsNode]. |
4763 | class _SwitchableSemanticsFragment extends _InterestingSemanticsFragment { |
4764 | _SwitchableSemanticsFragment({ |
4765 | required bool mergeIntoParent, |
4766 | required bool blockUserActions, |
4767 | required SemanticsConfiguration config, |
4768 | required List<List<_InterestingSemanticsFragment>> siblingMergeGroups, |
4769 | required super.owner, |
4770 | required super.dropsSemanticsOfPreviousSiblings, |
4771 | }) : _siblingMergeGroups = siblingMergeGroups, |
4772 | _mergeIntoParent = mergeIntoParent, |
4773 | _config = config { |
4774 | if (blockUserActions && !_config.isBlockingUserActions) { |
4775 | _ensureConfigIsWritable(); |
4776 | _config.isBlockingUserActions = true; |
4777 | } |
4778 | } |
4779 | |
4780 | final bool _mergeIntoParent; |
4781 | SemanticsConfiguration _config; |
4782 | bool _isConfigWritable = false; |
4783 | bool _mergesToSibling = false; |
4784 | |
4785 | final List<List<_InterestingSemanticsFragment>> _siblingMergeGroups; |
4786 | |
4787 | void _mergeSiblingGroup(Rect? parentSemanticsClipRect, Rect? parentPaintClipRect, List<SemanticsNode> result, Set<int> usedSemanticsIds) { |
4788 | for (final List<_InterestingSemanticsFragment> group in _siblingMergeGroups) { |
4789 | Rect? rect; |
4790 | Rect? semanticsClipRect; |
4791 | Rect? paintClipRect; |
4792 | SemanticsConfiguration? configuration; |
4793 | // Use empty set because the _tagsForChildren may not contains all of the |
4794 | // tags if this fragment is not explicit. The _tagsForChildren are added |
4795 | // to sibling nodes at the end of compileChildren if this fragment is |
4796 | // explicit. |
4797 | final Set<SemanticsTag> tags = <SemanticsTag>{}; |
4798 | SemanticsNode? node; |
4799 | for (final _InterestingSemanticsFragment fragment in group) { |
4800 | if (fragment.config != null) { |
4801 | final _SwitchableSemanticsFragment switchableFragment = fragment as _SwitchableSemanticsFragment; |
4802 | switchableFragment._mergesToSibling = true; |
4803 | node ??= fragment.owner._semantics; |
4804 | configuration ??= SemanticsConfiguration(); |
4805 | configuration.absorb(switchableFragment.config!); |
4806 | // It is a child fragment of a _SwitchableFragment, it must have a |
4807 | // geometry. |
4808 | final _SemanticsGeometry geometry = switchableFragment._computeSemanticsGeometry( |
4809 | parentSemanticsClipRect: parentSemanticsClipRect, |
4810 | parentPaintClipRect: parentPaintClipRect, |
4811 | )!; |
4812 | final Rect fragmentRect = MatrixUtils.transformRect(geometry.transform, geometry.rect); |
4813 | rect = rect?.expandToInclude(fragmentRect) ?? fragmentRect; |
4814 | if (geometry.semanticsClipRect != null) { |
4815 | final Rect rect = MatrixUtils.transformRect(geometry.transform, geometry.semanticsClipRect!); |
4816 | semanticsClipRect = semanticsClipRect?.intersect(rect) ?? rect; |
4817 | } |
4818 | if (geometry.paintClipRect != null) { |
4819 | final Rect rect = MatrixUtils.transformRect(geometry.transform, geometry.paintClipRect!); |
4820 | paintClipRect = paintClipRect?.intersect(rect) ?? rect; |
4821 | } |
4822 | if (switchableFragment._tagsForChildren != null) { |
4823 | tags.addAll(switchableFragment._tagsForChildren!); |
4824 | } |
4825 | } |
4826 | } |
4827 | // Can be null if all fragments in group are marked as explicit. |
4828 | if (configuration != null && !rect!.isEmpty) { |
4829 | if (node == null || usedSemanticsIds.contains(node.id)) { |
4830 | node = SemanticsNode(showOnScreen: owner.showOnScreen); |
4831 | } |
4832 | usedSemanticsIds.add(node.id); |
4833 | node |
4834 | ..tags = tags |
4835 | ..rect = rect |
4836 | ..transform = null // Will be set when compiling immediate parent node. |
4837 | ..parentSemanticsClipRect = semanticsClipRect |
4838 | ..parentPaintClipRect = paintClipRect; |
4839 | for (final _InterestingSemanticsFragment fragment in group) { |
4840 | if (fragment.config != null) { |
4841 | fragment.owner._semantics = node; |
4842 | } |
4843 | } |
4844 | node.updateWith(config: configuration); |
4845 | result.add(node); |
4846 | } |
4847 | } |
4848 | } |
4849 | |
4850 | final List<_InterestingSemanticsFragment> _children = <_InterestingSemanticsFragment>[]; |
4851 | |
4852 | @override |
4853 | void compileChildren({ |
4854 | Rect? parentSemanticsClipRect, |
4855 | Rect? parentPaintClipRect, |
4856 | required double elevationAdjustment, |
4857 | required List<SemanticsNode> result, |
4858 | required List<SemanticsNode> siblingNodes, |
4859 | }) { |
4860 | final Set<int> usedSemanticsIds = <int>{}; |
4861 | Iterable<_InterestingSemanticsFragment> compilingFragments = _children; |
4862 | for (final List<_InterestingSemanticsFragment> siblingGroup in _siblingMergeGroups) { |
4863 | compilingFragments = compilingFragments.followedBy(siblingGroup); |
4864 | } |
4865 | if (!_isExplicit) { |
4866 | if (!_mergesToSibling) { |
4867 | owner._semantics = null; |
4868 | } |
4869 | _mergeSiblingGroup( |
4870 | parentSemanticsClipRect, |
4871 | parentPaintClipRect, |
4872 | siblingNodes, |
4873 | usedSemanticsIds, |
4874 | ); |
4875 | for (final _InterestingSemanticsFragment fragment in compilingFragments) { |
4876 | assert(_ancestorChain.first == fragment._ancestorChain.last); |
4877 | if (fragment is _SwitchableSemanticsFragment) { |
4878 | // Cached semantics node may be part of sibling merging group prior |
4879 | // to this update. In this case, the semantics node may continue to |
4880 | // be reused in that sibling merging group. |
4881 | if (fragment._isExplicit && |
4882 | fragment.owner._semantics != null && |
4883 | usedSemanticsIds.contains(fragment.owner._semantics!.id)) { |
4884 | fragment.owner._semantics = null; |
4885 | } |
4886 | } |
4887 | fragment._ancestorChain.addAll(_ancestorChain.skip(1)); |
4888 | fragment.compileChildren( |
4889 | parentSemanticsClipRect: parentSemanticsClipRect, |
4890 | parentPaintClipRect: parentPaintClipRect, |
4891 | // The fragment is not explicit, its elevation has been absorbed by |
4892 | // the parent config (as thickness). We still need to make sure that |
4893 | // its children are placed at the elevation dictated by this config. |
4894 | elevationAdjustment: elevationAdjustment + _config.elevation, |
4895 | result: result, |
4896 | siblingNodes: siblingNodes, |
4897 | ); |
4898 | } |
4899 | return; |
4900 | } |
4901 | |
4902 | final _SemanticsGeometry? geometry = _computeSemanticsGeometry( |
4903 | parentSemanticsClipRect: parentSemanticsClipRect, |
4904 | parentPaintClipRect: parentPaintClipRect, |
4905 | ); |
4906 | |
4907 | if (!_mergeIntoParent && (geometry?.dropFromTree ?? false)) { |
4908 | return; // Drop the node, it's not going to be visible. |
4909 | } |
4910 | |
4911 | final SemanticsNode node = (owner._semantics ??= SemanticsNode(showOnScreen: owner.showOnScreen)) |
4912 | ..tags = _tagsForChildren; |
4913 | |
4914 | node.elevationAdjustment = elevationAdjustment; |
4915 | if (elevationAdjustment != 0.0) { |
4916 | _ensureConfigIsWritable(); |
4917 | _config.elevation += elevationAdjustment; |
4918 | } |
4919 | |
4920 | if (geometry != null) { |
4921 | assert(_needsGeometryUpdate); |
4922 | node |
4923 | ..rect = geometry.rect |
4924 | ..transform = geometry.transform |
4925 | ..parentSemanticsClipRect = geometry.semanticsClipRect |
4926 | ..parentPaintClipRect = geometry.paintClipRect; |
4927 | if (!_mergeIntoParent && geometry.markAsHidden) { |
4928 | _ensureConfigIsWritable(); |
4929 | _config.isHidden = true; |
4930 | } |
4931 | } |
4932 | final List<SemanticsNode> children = <SemanticsNode>[]; |
4933 | _mergeSiblingGroup( |
4934 | node.parentSemanticsClipRect, |
4935 | node.parentPaintClipRect, |
4936 | siblingNodes, |
4937 | usedSemanticsIds, |
4938 | ); |
4939 | for (final _InterestingSemanticsFragment fragment in compilingFragments) { |
4940 | if (fragment is _SwitchableSemanticsFragment) { |
4941 | // Cached semantics node may be part of sibling merging group prior |
4942 | // to this update. In this case, the semantics node may continue to |
4943 | // be reused in that sibling merging group. |
4944 | if (fragment._isExplicit && |
4945 | fragment.owner._semantics != null && |
4946 | usedSemanticsIds.contains(fragment.owner._semantics!.id)) { |
4947 | fragment.owner._semantics = null; |
4948 | } |
4949 | } |
4950 | final List<SemanticsNode> childSiblingNodes = <SemanticsNode>[]; |
4951 | fragment.compileChildren( |
4952 | parentSemanticsClipRect: node.parentSemanticsClipRect, |
4953 | parentPaintClipRect: node.parentPaintClipRect, |
4954 | elevationAdjustment: 0.0, |
4955 | result: children, |
4956 | siblingNodes: childSiblingNodes, |
4957 | ); |
4958 | siblingNodes.addAll(childSiblingNodes); |
4959 | } |
4960 | |
4961 | if (_config.isSemanticBoundary) { |
4962 | owner.assembleSemanticsNode(node, _config, children); |
4963 | } else { |
4964 | node.updateWith(config: _config, childrenInInversePaintOrder: children); |
4965 | } |
4966 | result.add(node); |
4967 | // Sibling node needs to attach to the parent of an explicit node. |
4968 | for (final SemanticsNode siblingNode in siblingNodes) { |
4969 | // sibling nodes are in the same coordinate of the immediate explicit node. |
4970 | // They need to share the same transform if they are going to attach to the |
4971 | // parent of the immediate explicit node. |
4972 | assert(siblingNode.transform == null); |
4973 | siblingNode.transform = node.transform; |
4974 | if (_tagsForChildren != null) { |
4975 | siblingNode.tags ??= <SemanticsTag>{}; |
4976 | siblingNode.tags!.addAll(_tagsForChildren!); |
4977 | } |
4978 | } |
4979 | result.addAll(siblingNodes); |
4980 | siblingNodes.clear(); |
4981 | } |
4982 | |
4983 | _SemanticsGeometry? _computeSemanticsGeometry({ |
4984 | required Rect? parentSemanticsClipRect, |
4985 | required Rect? parentPaintClipRect, |
4986 | }) { |
4987 | return _needsGeometryUpdate |
4988 | ? _SemanticsGeometry(parentSemanticsClipRect: parentSemanticsClipRect, parentPaintClipRect: parentPaintClipRect, ancestors: _ancestorChain) |
4989 | : null; |
4990 | } |
4991 | |
4992 | @override |
4993 | SemanticsConfiguration? get config { |
4994 | return _isExplicit ? null : _config; |
4995 | } |
4996 | |
4997 | @override |
4998 | void addAll(Iterable<_InterestingSemanticsFragment> fragments) { |
4999 | for (final _InterestingSemanticsFragment fragment in fragments) { |
5000 | _children.add(fragment); |
5001 | if (fragment.config == null) { |
5002 | continue; |
5003 | } |
5004 | _ensureConfigIsWritable(); |
5005 | _config.absorb(fragment.config!); |
5006 | } |
5007 | } |
5008 | |
5009 | @override |
5010 | void addTags(Iterable<SemanticsTag> tags) { |
5011 | super.addTags(tags); |
5012 | // _ContainerSemanticsFragments add their tags to child fragments through |
5013 | // this method. This fragment must make sure its _config is in sync. |
5014 | if (tags.isNotEmpty) { |
5015 | _ensureConfigIsWritable(); |
5016 | tags.forEach(_config.addTagForChildren); |
5017 | } |
5018 | } |
5019 | |
5020 | void _ensureConfigIsWritable() { |
5021 | if (!_isConfigWritable) { |
5022 | _config = _config.copy(); |
5023 | _isConfigWritable = true; |
5024 | } |
5025 | } |
5026 | |
5027 | bool _isExplicit = false; |
5028 | |
5029 | @override |
5030 | void markAsExplicit() { |
5031 | _isExplicit = true; |
5032 | } |
5033 | |
5034 | bool get _needsGeometryUpdate => _ancestorChain.length > 1; |
5035 | } |
5036 | |
5037 | /// Helper class that keeps track of the geometry of a [SemanticsNode]. |
5038 | /// |
5039 | /// It is used to annotate a [SemanticsNode] with the current information for |
5040 | /// [SemanticsNode.rect] and [SemanticsNode.transform]. |
5041 | class _SemanticsGeometry { |
5042 | |
5043 | /// The `parentClippingRect` may be null if no clip is to be applied. |
5044 | /// |
5045 | /// The `ancestors` list has to include all [RenderObject] in order that are |
5046 | /// located between the [SemanticsNode] whose geometry is represented here |
5047 | /// (first [RenderObject] in the list) and its closest ancestor [RenderObject] |
5048 | /// that also owns its own [SemanticsNode] (last [RenderObject] in the list). |
5049 | _SemanticsGeometry({ |
5050 | required Rect? parentSemanticsClipRect, |
5051 | required Rect? parentPaintClipRect, |
5052 | required List<RenderObject> ancestors, |
5053 | }) { |
5054 | _computeValues(parentSemanticsClipRect, parentPaintClipRect, ancestors); |
5055 | } |
5056 | |
5057 | Rect? _paintClipRect; |
5058 | Rect? _semanticsClipRect; |
5059 | late Matrix4 _transform; |
5060 | late Rect _rect; |
5061 | |
5062 | /// Value for [SemanticsNode.transform]. |
5063 | Matrix4 get transform => _transform; |
5064 | |
5065 | /// Value for [SemanticsNode.parentSemanticsClipRect]. |
5066 | Rect? get semanticsClipRect => _semanticsClipRect; |
5067 | |
5068 | /// Value for [SemanticsNode.parentPaintClipRect]. |
5069 | Rect? get paintClipRect => _paintClipRect; |
5070 | |
5071 | /// Value for [SemanticsNode.rect]. |
5072 | Rect get rect => _rect; |
5073 | |
5074 | /// Computes values, ensuring `rect` is properly bounded by ancestor clipping rects. |
5075 | /// |
5076 | /// See also: |
5077 | /// |
5078 | /// * [RenderObject.describeSemanticsClip], typically used to determine `parentSemanticsClipRect`. |
5079 | /// * [RenderObject.describeApproximatePaintClip], typically used to determine `parentPaintClipRect`. |
5080 | void _computeValues(Rect? parentSemanticsClipRect, Rect? parentPaintClipRect, List<RenderObject> ancestors) { |
5081 | assert(ancestors.length > 1); |
5082 | |
5083 | _transform = Matrix4.identity(); |
5084 | _semanticsClipRect = parentSemanticsClipRect; |
5085 | _paintClipRect = parentPaintClipRect; |
5086 | |
5087 | for (int index = ancestors.length-1; index > 0; index -= 1) { |
5088 | final RenderObject semanticsParent = ancestors[index]; |
5089 | final RenderObject semanticsChild = ancestors[index-1]; |
5090 | _applyIntermediatePaintTransforms(semanticsParent, semanticsChild, _transform); |
5091 | |
5092 | if (identical(semanticsParent, semanticsChild.parent)) { |
5093 | // The easier and more common case: semanticsParent is directly |
5094 | // responsible for painting (and potentially clipping) the semanticsChild |
5095 | // RenderObject. |
5096 | _computeClipRect(semanticsParent, semanticsChild, _semanticsClipRect, _paintClipRect); |
5097 | } else { |
5098 | // Otherwise we have to find the closest ancestor RenderObject that |
5099 | // has up-to-date semantics geometry and compute the clip rects from there. |
5100 | // |
5101 | // Currently it can only happen when the subtree contains an OverlayPortal. |
5102 | final List<RenderObject> clipPath = <RenderObject>[semanticsChild]; |
5103 | |
5104 | RenderObject? ancestor = semanticsChild.parent; |
5105 | while (ancestor != null && ancestor._semantics == null) { |
5106 | clipPath.add(ancestor); |
5107 | ancestor = ancestor.parent; |
5108 | } |
5109 | _paintClipRect = ancestor?._semantics?.parentPaintClipRect; |
5110 | _semanticsClipRect = ancestor?._semantics?.parentSemanticsClipRect; |
5111 | if (ancestor != null) { |
5112 | assert(ancestor._semantics != null); |
5113 | assert(!ancestor._needsSemanticsUpdate); |
5114 | RenderObject parent = ancestor; |
5115 | for (int i = clipPath.length - 1; i >= 0; i -= 1) { |
5116 | _computeClipRect(parent, clipPath[i], _semanticsClipRect, _paintClipRect); |
5117 | parent = clipPath[i]; |
5118 | } |
5119 | } |
5120 | } |
5121 | } |
5122 | |
5123 | final RenderObject owner = ancestors.first; |
5124 | _rect = _semanticsClipRect?.intersect(owner.semanticBounds) ?? owner.semanticBounds; |
5125 | if (_paintClipRect != null) { |
5126 | final Rect paintRect = _paintClipRect!.intersect(_rect); |
5127 | _markAsHidden = paintRect.isEmpty && !_rect.isEmpty; |
5128 | if (!_markAsHidden) { |
5129 | _rect = paintRect; |
5130 | } |
5131 | } |
5132 | } |
5133 | |
5134 | /// From parent to child coordinate system. |
5135 | static Rect? _transformRect(Rect? rect, Matrix4 transform) { |
5136 | if (rect == null) { |
5137 | return null; |
5138 | } |
5139 | if (rect.isEmpty || transform.isZero()) { |
5140 | return Rect.zero; |
5141 | } |
5142 | return MatrixUtils.inverseTransformRect(transform, rect); |
5143 | } |
5144 | |
5145 | // Computes the paint transform from `childFragmentOwner` to |
5146 | // `parentFragmentOwner` and applies the paint transform to `transform` in |
5147 | // place. |
5148 | // |
5149 | // The `parentFragmentOwner` and `childFragmentOwner` [RenderObject]s must be |
5150 | // in the same render tree (so they have a common ancestor). |
5151 | static void _applyIntermediatePaintTransforms( |
5152 | RenderObject parentFragmentOwner, |
5153 | RenderObject childFragmentOwner, |
5154 | Matrix4 transform, |
5155 | ) { |
5156 | Matrix4? parentToCommonAncestorTransform; |
5157 | RenderObject from = childFragmentOwner; |
5158 | RenderObject to = parentFragmentOwner; |
5159 | |
5160 | while (!identical(from, to)) { |
5161 | final int fromDepth = from.depth; |
5162 | final int toDepth = to.depth; |
5163 | |
5164 | if (fromDepth >= toDepth) { |
5165 | assert(from.parent != null, ' $parentFragmentOwner and $childFragmentOwner are not in the same render tree.' ); |
5166 | final RenderObject fromParent = from.parent!; |
5167 | fromParent.applyPaintTransform(from, transform); |
5168 | from = fromParent; |
5169 | } |
5170 | if (fromDepth <= toDepth) { |
5171 | assert(to.parent != null, ' $parentFragmentOwner and $childFragmentOwner are not in the same render tree.' ); |
5172 | final RenderObject toParent = to.parent!; |
5173 | toParent.applyPaintTransform(to, parentToCommonAncestorTransform ??= Matrix4.identity()); |
5174 | to = toParent; |
5175 | } |
5176 | } |
5177 | |
5178 | if (parentToCommonAncestorTransform != null) { |
5179 | if (parentToCommonAncestorTransform.invert() != 0) { |
5180 | transform.multiply(parentToCommonAncestorTransform); |
5181 | } else { |
5182 | transform.setZero(); |
5183 | } |
5184 | } |
5185 | } |
5186 | |
5187 | // A matrix used to store transient transform data. |
5188 | // |
5189 | // Reusing this matrix avoids allocating a new matrix every time a temporary |
5190 | // matrix is needed. |
5191 | // |
5192 | // This instance should never be returned to the caller. Otherwise, the data |
5193 | // stored in it will be overwritten unpredictably by subsequent reuses. |
5194 | static final Matrix4 _temporaryTransformHolder = Matrix4.zero(); |
5195 | |
5196 | // Computes the semantics and painting clip rects for the given child and |
5197 | // assigns the rects to _semanticsClipRect and _paintClipRect respectively. |
5198 | // |
5199 | // The caller must guarantee that child.parent == parent. The resulting rects |
5200 | // are in `child`'s coordinate system. |
5201 | void _computeClipRect(RenderObject parent, RenderObject child, Rect? parentSemanticsClipRect, Rect? parentPaintClipRect) { |
5202 | assert(identical(child.parent, parent)); |
5203 | // Computes the paint transform from child to parent. The _transformRect |
5204 | // method will compute the inverse. |
5205 | _temporaryTransformHolder.setIdentity(); // clears data from previous call(s) |
5206 | parent.applyPaintTransform(child, _temporaryTransformHolder); |
5207 | |
5208 | final Rect? additionalPaintClip = parent.describeApproximatePaintClip(child); |
5209 | _paintClipRect = _transformRect( |
5210 | _intersectRects(additionalPaintClip, parentPaintClipRect), |
5211 | _temporaryTransformHolder, |
5212 | ); |
5213 | |
5214 | if (_paintClipRect == null) { |
5215 | _semanticsClipRect = null; |
5216 | } else { |
5217 | final Rect? semanticsClip = parent.describeSemanticsClip(child) ?? _intersectRects(parentSemanticsClipRect, additionalPaintClip); |
5218 | _semanticsClipRect = _transformRect(semanticsClip, _temporaryTransformHolder); |
5219 | } |
5220 | } |
5221 | |
5222 | static Rect? _intersectRects(Rect? a, Rect? b) { |
5223 | if (b == null) { |
5224 | return a; |
5225 | } |
5226 | return a?.intersect(b) ?? b; |
5227 | } |
5228 | |
5229 | /// Whether the [SemanticsNode] annotated with the geometric information tracked |
5230 | /// by this object can be dropped from the semantics tree without losing |
5231 | /// semantics information. |
5232 | bool get dropFromTree { |
5233 | return _rect.isEmpty || _transform.isZero(); |
5234 | } |
5235 | |
5236 | /// Whether the [SemanticsNode] annotated with the geometric information |
5237 | /// tracked by this object should be marked as hidden because it is not |
5238 | /// visible on screen. |
5239 | /// |
5240 | /// Hidden elements should still be included in the tree to work around |
5241 | /// platform limitations (e.g. accessibility scrolling on iOS). |
5242 | /// |
5243 | /// See also: |
5244 | /// |
5245 | /// * [SemanticsFlag.isHidden] for the purpose of marking a node as hidden. |
5246 | bool get markAsHidden => _markAsHidden; |
5247 | bool _markAsHidden = false; |
5248 | } |
5249 | |
5250 | /// A class that creates [DiagnosticsNode] by wrapping [RenderObject.debugCreator]. |
5251 | /// |
5252 | /// Attach a [DiagnosticsDebugCreator] into [FlutterErrorDetails.informationCollector] |
5253 | /// when a [RenderObject.debugCreator] is available. This will lead to improved |
5254 | /// error message. |
5255 | class DiagnosticsDebugCreator extends DiagnosticsProperty<Object> { |
5256 | /// Create a [DiagnosticsProperty] with its [value] initialized to input |
5257 | /// [RenderObject.debugCreator]. |
5258 | DiagnosticsDebugCreator(Object value) |
5259 | : super( |
5260 | 'debugCreator' , |
5261 | value, |
5262 | level: DiagnosticLevel.hidden, |
5263 | ); |
5264 | } |
5265 | |