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 SemanticsUpdate; |
6 | |
7 | import 'package:flutter/foundation.dart'; |
8 | import 'package:flutter/gestures.dart'; |
9 | import 'package:flutter/scheduler.dart'; |
10 | import 'package:flutter/semantics.dart'; |
11 | import 'package:flutter/services.dart'; |
12 | |
13 | import 'debug.dart'; |
14 | import 'mouse_tracker.dart'; |
15 | import 'object.dart'; |
16 | import 'service_extensions.dart'; |
17 | import 'view.dart'; |
18 | |
19 | export 'package:flutter/gestures.dart' show HitTestResult; |
20 | |
21 | // Examples can assume: |
22 | // late BuildContext context; |
23 | |
24 | /// The glue between the render trees and the Flutter engine. |
25 | /// |
26 | /// The [RendererBinding] manages multiple independent render trees. Each render |
27 | /// tree is rooted in a [RenderView] that must be added to the binding via |
28 | /// [addRenderView] to be considered during frame production, hit testing, etc. |
29 | /// Furthermore, the render tree must be managed by a [PipelineOwner] that is |
30 | /// part of the pipeline owner tree rooted at [rootPipelineOwner]. |
31 | /// |
32 | /// Adding [PipelineOwner]s and [RenderView]s to this binding in the way |
33 | /// described above is left as a responsibility for a higher level abstraction. |
34 | /// The widgets library, for example, introduces the [View] widget, which |
35 | /// registers its [RenderView] and [PipelineOwner] with this binding. |
36 | mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { |
37 | @override |
38 | void initInstances() { |
39 | super.initInstances(); |
40 | _instance = this; |
41 | _rootPipelineOwner = createRootPipelineOwner(); |
42 | platformDispatcher |
43 | ..onMetricsChanged = handleMetricsChanged |
44 | ..onTextScaleFactorChanged = handleTextScaleFactorChanged |
45 | ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged; |
46 | addPersistentFrameCallback(_handlePersistentFrameCallback); |
47 | initMouseTracker(); |
48 | if (kIsWeb) { |
49 | addPostFrameCallback(_handleWebFirstFrame, debugLabel: 'RendererBinding.webFirstFrame' ); |
50 | } |
51 | rootPipelineOwner.attach(_manifold); |
52 | } |
53 | |
54 | /// The current [RendererBinding], if one has been created. |
55 | /// |
56 | /// Provides access to the features exposed by this mixin. The binding must |
57 | /// be initialized before using this getter; this is typically done by calling |
58 | /// [runApp] or [WidgetsFlutterBinding.ensureInitialized]. |
59 | static RendererBinding get instance => BindingBase.checkInstance(_instance); |
60 | static RendererBinding? _instance; |
61 | |
62 | @override |
63 | void initServiceExtensions() { |
64 | super.initServiceExtensions(); |
65 | |
66 | assert(() { |
67 | // these service extensions only work in debug mode |
68 | registerBoolServiceExtension( |
69 | name: RenderingServiceExtensions.invertOversizedImages.name, |
70 | getter: () async => debugInvertOversizedImages, |
71 | setter: (bool value) async { |
72 | if (debugInvertOversizedImages != value) { |
73 | debugInvertOversizedImages = value; |
74 | return _forceRepaint(); |
75 | } |
76 | return Future<void>.value(); |
77 | }, |
78 | ); |
79 | registerBoolServiceExtension( |
80 | name: RenderingServiceExtensions.debugPaint.name, |
81 | getter: () async => debugPaintSizeEnabled, |
82 | setter: (bool value) { |
83 | if (debugPaintSizeEnabled == value) { |
84 | return Future<void>.value(); |
85 | } |
86 | debugPaintSizeEnabled = value; |
87 | return _forceRepaint(); |
88 | }, |
89 | ); |
90 | registerBoolServiceExtension( |
91 | name: RenderingServiceExtensions.debugPaintBaselinesEnabled.name, |
92 | getter: () async => debugPaintBaselinesEnabled, |
93 | setter: (bool value) { |
94 | if (debugPaintBaselinesEnabled == value) { |
95 | return Future<void>.value(); |
96 | } |
97 | debugPaintBaselinesEnabled = value; |
98 | return _forceRepaint(); |
99 | }, |
100 | ); |
101 | registerBoolServiceExtension( |
102 | name: RenderingServiceExtensions.repaintRainbow.name, |
103 | getter: () async => debugRepaintRainbowEnabled, |
104 | setter: (bool value) { |
105 | final bool repaint = debugRepaintRainbowEnabled && !value; |
106 | debugRepaintRainbowEnabled = value; |
107 | if (repaint) { |
108 | return _forceRepaint(); |
109 | } |
110 | return Future<void>.value(); |
111 | }, |
112 | ); |
113 | registerServiceExtension( |
114 | name: RenderingServiceExtensions.debugDumpLayerTree.name, |
115 | callback: (Map<String, String> parameters) async { |
116 | return <String, Object>{ |
117 | 'data' : _debugCollectLayerTrees(), |
118 | }; |
119 | }, |
120 | ); |
121 | registerBoolServiceExtension( |
122 | name: RenderingServiceExtensions.debugDisableClipLayers.name, |
123 | getter: () async => debugDisableClipLayers, |
124 | setter: (bool value) { |
125 | if (debugDisableClipLayers == value) { |
126 | return Future<void>.value(); |
127 | } |
128 | debugDisableClipLayers = value; |
129 | return _forceRepaint(); |
130 | }, |
131 | ); |
132 | registerBoolServiceExtension( |
133 | name: RenderingServiceExtensions.debugDisablePhysicalShapeLayers.name, |
134 | getter: () async => debugDisablePhysicalShapeLayers, |
135 | setter: (bool value) { |
136 | if (debugDisablePhysicalShapeLayers == value) { |
137 | return Future<void>.value(); |
138 | } |
139 | debugDisablePhysicalShapeLayers = value; |
140 | return _forceRepaint(); |
141 | }, |
142 | ); |
143 | registerBoolServiceExtension( |
144 | name: RenderingServiceExtensions.debugDisableOpacityLayers.name, |
145 | getter: () async => debugDisableOpacityLayers, |
146 | setter: (bool value) { |
147 | if (debugDisableOpacityLayers == value) { |
148 | return Future<void>.value(); |
149 | } |
150 | debugDisableOpacityLayers = value; |
151 | return _forceRepaint(); |
152 | }, |
153 | ); |
154 | return true; |
155 | }()); |
156 | |
157 | if (!kReleaseMode) { |
158 | // these service extensions work in debug or profile mode |
159 | registerServiceExtension( |
160 | name: RenderingServiceExtensions.debugDumpRenderTree.name, |
161 | callback: (Map<String, String> parameters) async { |
162 | return <String, Object>{ |
163 | 'data' : _debugCollectRenderTrees(), |
164 | }; |
165 | }, |
166 | ); |
167 | registerServiceExtension( |
168 | name: RenderingServiceExtensions.debugDumpSemanticsTreeInTraversalOrder.name, |
169 | callback: (Map<String, String> parameters) async { |
170 | return <String, Object>{ |
171 | 'data' : _debugCollectSemanticsTrees(DebugSemanticsDumpOrder.traversalOrder), |
172 | }; |
173 | }, |
174 | ); |
175 | registerServiceExtension( |
176 | name: RenderingServiceExtensions.debugDumpSemanticsTreeInInverseHitTestOrder.name, |
177 | callback: (Map<String, String> parameters) async { |
178 | return <String, Object>{ |
179 | 'data' : _debugCollectSemanticsTrees(DebugSemanticsDumpOrder.inverseHitTest), |
180 | }; |
181 | }, |
182 | ); |
183 | registerBoolServiceExtension( |
184 | name: RenderingServiceExtensions.profileRenderObjectPaints.name, |
185 | getter: () async => debugProfilePaintsEnabled, |
186 | setter: (bool value) async { |
187 | if (debugProfilePaintsEnabled != value) { |
188 | debugProfilePaintsEnabled = value; |
189 | } |
190 | }, |
191 | ); |
192 | registerBoolServiceExtension( |
193 | name: RenderingServiceExtensions.profileRenderObjectLayouts.name, |
194 | getter: () async => debugProfileLayoutsEnabled, |
195 | setter: (bool value) async { |
196 | if (debugProfileLayoutsEnabled != value) { |
197 | debugProfileLayoutsEnabled = value; |
198 | } |
199 | }, |
200 | ); |
201 | } |
202 | } |
203 | |
204 | late final PipelineManifold _manifold = _BindingPipelineManifold(this); |
205 | |
206 | /// The object that manages state about currently connected mice, for hover |
207 | /// notification. |
208 | MouseTracker get mouseTracker => _mouseTracker!; |
209 | MouseTracker? _mouseTracker; |
210 | |
211 | /// Deprecated. Will be removed in a future version of Flutter. |
212 | /// |
213 | /// This is typically the owner of the render tree bootstrapped by [runApp] |
214 | /// and rooted in [renderView]. It maintains dirty state for layout, |
215 | /// composite, paint, and accessibility semantics for that tree. |
216 | /// |
217 | /// However, by default, the [pipelineOwner] does not participate in frame |
218 | /// production because it is not automatically attached to the |
219 | /// [rootPipelineOwner] or any of its descendants. It is also not |
220 | /// automatically associated with the [renderView]. This is left as a |
221 | /// responsibility for a higher level abstraction. The [WidgetsBinding], for |
222 | /// example, wires this up in [WidgetsBinding.wrapWithDefaultView], which is |
223 | /// called indirectly from [runApp]. |
224 | /// |
225 | /// Apps, that don't use the [WidgetsBinding] or don't call [runApp] (or |
226 | /// [WidgetsBinding.wrapWithDefaultView]) must manually add this pipeline owner |
227 | /// to the pipeline owner tree rooted at [rootPipelineOwner] and assign a |
228 | /// [RenderView] to it if the they want to use this deprecated property. |
229 | /// |
230 | /// Instead of accessing this deprecated property, consider interacting with |
231 | /// the root of the [PipelineOwner] tree (exposed in [rootPipelineOwner]) or |
232 | /// instead of accessing the [SemanticsOwner] of any [PipelineOwner] consider |
233 | /// interacting with the [SemanticsBinding] (exposed via |
234 | /// [SemanticsBinding.instance]) directly. |
235 | @Deprecated( |
236 | 'Interact with the pipelineOwner tree rooted at RendererBinding.rootPipelineOwner instead. ' |
237 | 'Or instead of accessing the SemanticsOwner of any PipelineOwner interact with the SemanticsBinding directly. ' |
238 | 'This feature was deprecated after v3.10.0-12.0.pre.' |
239 | ) |
240 | late final PipelineOwner pipelineOwner = PipelineOwner( |
241 | onSemanticsOwnerCreated: () { |
242 | (pipelineOwner.rootNode as RenderView?)?.scheduleInitialSemantics(); |
243 | }, |
244 | onSemanticsUpdate: (ui.SemanticsUpdate update) { |
245 | (pipelineOwner.rootNode as RenderView?)?.updateSemantics(update); |
246 | }, |
247 | onSemanticsOwnerDisposed: () { |
248 | (pipelineOwner.rootNode as RenderView?)?.clearSemantics(); |
249 | } |
250 | ); |
251 | |
252 | /// Deprecated. Will be removed in a future version of Flutter. |
253 | /// |
254 | /// This is typically the root of the render tree bootstrapped by [runApp]. |
255 | /// |
256 | /// However, by default this render view is not associated with any |
257 | /// [PipelineOwner] and therefore isn't considered during frame production. |
258 | /// It is also not registered with this binding via [addRenderView]. |
259 | /// Wiring this up is left as a responsibility for a higher level. The |
260 | /// [WidgetsBinding], for example, sets this up in |
261 | /// [WidgetsBinding.wrapWithDefaultView], which is called indirectly from |
262 | /// [runApp]. |
263 | /// |
264 | /// Apps that don't use the [WidgetsBinding] or don't call [runApp] (or |
265 | /// [WidgetsBinding.wrapWithDefaultView]) must manually assign a |
266 | /// [PipelineOwner] to this [RenderView], make sure the pipeline owner is part |
267 | /// of the pipeline owner tree rooted at [rootPipelineOwner], and call |
268 | /// [addRenderView] if they want to use this deprecated property. |
269 | /// |
270 | /// Instead of interacting with this deprecated property, consider using |
271 | /// [renderViews] instead, which contains all [RenderView]s managed by the |
272 | /// binding. |
273 | @Deprecated( |
274 | 'Consider using RendererBinding.renderViews instead as the binding may manage multiple RenderViews. ' |
275 | 'This feature was deprecated after v3.10.0-12.0.pre.' |
276 | ) |
277 | // TODO(goderbauer): When this deprecated property is removed also delete the _ReusableRenderView class. |
278 | late final RenderView renderView = _ReusableRenderView( |
279 | view: platformDispatcher.implicitView!, |
280 | ); |
281 | |
282 | /// Creates the [PipelineOwner] that serves as the root of the pipeline owner |
283 | /// tree ([rootPipelineOwner]). |
284 | /// |
285 | /// {@template flutter.rendering.createRootPipelineOwner} |
286 | /// By default, the root pipeline owner is not setup to manage a render tree |
287 | /// and its [PipelineOwner.rootNode] must not be assigned. If necessary, |
288 | /// [createRootPipelineOwner] may be overridden to create a root pipeline |
289 | /// owner configured to manage its own render tree. |
290 | /// |
291 | /// In typical use, child pipeline owners are added to the root pipeline owner |
292 | /// (via [PipelineOwner.adoptChild]). Those children typically do each manage |
293 | /// their own [RenderView] and produce distinct render trees which render |
294 | /// their content into the [FlutterView] associated with that [RenderView]. |
295 | /// {@endtemplate} |
296 | PipelineOwner createRootPipelineOwner() { |
297 | return _DefaultRootPipelineOwner(); |
298 | } |
299 | |
300 | /// The [PipelineOwner] that is the root of the PipelineOwner tree. |
301 | /// |
302 | /// {@macro flutter.rendering.createRootPipelineOwner} |
303 | PipelineOwner get rootPipelineOwner => _rootPipelineOwner; |
304 | late PipelineOwner _rootPipelineOwner; |
305 | |
306 | /// The [RenderView]s managed by this binding. |
307 | /// |
308 | /// A [RenderView] is added by [addRenderView] and removed by [removeRenderView]. |
309 | Iterable<RenderView> get renderViews => _viewIdToRenderView.values; |
310 | final Map<Object, RenderView> _viewIdToRenderView = <Object, RenderView>{}; |
311 | |
312 | /// Adds a [RenderView] to this binding. |
313 | /// |
314 | /// The binding will interact with the [RenderView] in the following ways: |
315 | /// |
316 | /// * setting and updating [RenderView.configuration], |
317 | /// * calling [RenderView.compositeFrame] when it is time to produce a new |
318 | /// frame, and |
319 | /// * forwarding relevant pointer events to the [RenderView] for hit testing. |
320 | /// |
321 | /// To remove a [RenderView] from the binding, call [removeRenderView]. |
322 | void addRenderView(RenderView view) { |
323 | final Object viewId = view.flutterView.viewId; |
324 | assert(!_viewIdToRenderView.containsValue(view)); |
325 | assert(!_viewIdToRenderView.containsKey(viewId)); |
326 | _viewIdToRenderView[viewId] = view; |
327 | view.configuration = createViewConfigurationFor(view); |
328 | } |
329 | |
330 | /// Removes a [RenderView] previously added with [addRenderView] from the |
331 | /// binding. |
332 | void removeRenderView(RenderView view) { |
333 | final Object viewId = view.flutterView.viewId; |
334 | assert(_viewIdToRenderView[viewId] == view); |
335 | _viewIdToRenderView.remove(viewId); |
336 | } |
337 | |
338 | /// Returns a [ViewConfiguration] configured for the provided [RenderView] |
339 | /// based on the current environment. |
340 | /// |
341 | /// This is called during [addRenderView] and also in response to changes to |
342 | /// the system metrics to update all [renderViews] added to the binding. |
343 | /// |
344 | /// Bindings can override this method to change what size or device pixel |
345 | /// ratio the [RenderView] will use. For example, the testing framework uses |
346 | /// this to force the display into 800x600 when a test is run on the device |
347 | /// using `flutter run`. |
348 | @protected |
349 | ViewConfiguration createViewConfigurationFor(RenderView renderView) { |
350 | final FlutterView view = renderView.flutterView; |
351 | final double devicePixelRatio = view.devicePixelRatio; |
352 | return ViewConfiguration( |
353 | size: view.physicalSize / devicePixelRatio, |
354 | devicePixelRatio: devicePixelRatio, |
355 | ); |
356 | } |
357 | |
358 | /// Called when the system metrics change. |
359 | /// |
360 | /// See [dart:ui.PlatformDispatcher.onMetricsChanged]. |
361 | @protected |
362 | @visibleForTesting |
363 | void handleMetricsChanged() { |
364 | bool forceFrame = false; |
365 | for (final RenderView view in renderViews) { |
366 | forceFrame = forceFrame || view.child != null; |
367 | view.configuration = createViewConfigurationFor(view); |
368 | } |
369 | if (forceFrame) { |
370 | scheduleForcedFrame(); |
371 | } |
372 | } |
373 | |
374 | /// Called when the platform text scale factor changes. |
375 | /// |
376 | /// See [dart:ui.PlatformDispatcher.onTextScaleFactorChanged]. |
377 | @protected |
378 | void handleTextScaleFactorChanged() { } |
379 | |
380 | /// Called when the platform brightness changes. |
381 | /// |
382 | /// The current platform brightness can be queried from a Flutter binding or |
383 | /// from a [MediaQuery] widget. The latter is preferred from widgets because |
384 | /// it causes the widget to be automatically rebuilt when the brightness |
385 | /// changes. |
386 | /// |
387 | /// {@tool snippet} |
388 | /// Querying [MediaQuery.platformBrightnessOf] directly. Preferred. |
389 | /// |
390 | /// ```dart |
391 | /// final Brightness brightness = MediaQuery.platformBrightnessOf(context); |
392 | /// ``` |
393 | /// {@end-tool} |
394 | /// |
395 | /// {@tool snippet} |
396 | /// Querying [PlatformDispatcher.platformBrightness]. |
397 | /// |
398 | /// ```dart |
399 | /// final Brightness brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness; |
400 | /// ``` |
401 | /// {@end-tool} |
402 | /// |
403 | /// See [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged]. |
404 | @protected |
405 | void handlePlatformBrightnessChanged() { } |
406 | |
407 | /// Creates a [MouseTracker] which manages state about currently connected |
408 | /// mice, for hover notification. |
409 | /// |
410 | /// Used by testing framework to reinitialize the mouse tracker between tests. |
411 | @visibleForTesting |
412 | void initMouseTracker([MouseTracker? tracker]) { |
413 | _mouseTracker?.dispose(); |
414 | _mouseTracker = tracker ?? MouseTracker((Offset position, int viewId) { |
415 | final HitTestResult result = HitTestResult(); |
416 | hitTestInView(result, position, viewId); |
417 | return result; |
418 | }); |
419 | } |
420 | |
421 | @override // from GestureBinding |
422 | void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) { |
423 | _mouseTracker!.updateWithEvent( |
424 | event, |
425 | // When the button is pressed, normal hit test uses a cached |
426 | // result, but MouseTracker requires that the hit test is re-executed to |
427 | // update the hovering events. |
428 | event is PointerMoveEvent ? null : hitTestResult, |
429 | ); |
430 | super.dispatchEvent(event, hitTestResult); |
431 | } |
432 | |
433 | @override |
434 | void performSemanticsAction(SemanticsActionEvent action) { |
435 | // Due to the asynchronicity in some screen readers (they may not have |
436 | // processed the latest semantics update yet) this code is more forgiving |
437 | // and actions for views/nodes that no longer exist are gracefully ignored. |
438 | _viewIdToRenderView[action.viewId]?.owner?.semanticsOwner?.performAction(action.nodeId, action.type, action.arguments); |
439 | } |
440 | |
441 | void _handleWebFirstFrame(Duration _) { |
442 | assert(kIsWeb); |
443 | const MethodChannel methodChannel = MethodChannel('flutter/service_worker' ); |
444 | methodChannel.invokeMethod<void>('first-frame' ); |
445 | } |
446 | |
447 | void _handlePersistentFrameCallback(Duration timeStamp) { |
448 | drawFrame(); |
449 | _scheduleMouseTrackerUpdate(); |
450 | } |
451 | |
452 | bool _debugMouseTrackerUpdateScheduled = false; |
453 | void _scheduleMouseTrackerUpdate() { |
454 | assert(!_debugMouseTrackerUpdateScheduled); |
455 | assert(() { |
456 | _debugMouseTrackerUpdateScheduled = true; |
457 | return true; |
458 | }()); |
459 | SchedulerBinding.instance.addPostFrameCallback((Duration duration) { |
460 | assert(_debugMouseTrackerUpdateScheduled); |
461 | assert(() { |
462 | _debugMouseTrackerUpdateScheduled = false; |
463 | return true; |
464 | }()); |
465 | _mouseTracker!.updateAllDevices(); |
466 | }, debugLabel: 'RendererBinding.mouseTrackerUpdate' ); |
467 | } |
468 | |
469 | int _firstFrameDeferredCount = 0; |
470 | bool _firstFrameSent = false; |
471 | |
472 | /// Whether frames produced by [drawFrame] are sent to the engine. |
473 | /// |
474 | /// If false the framework will do all the work to produce a frame, |
475 | /// but the frame is never sent to the engine to actually appear on screen. |
476 | /// |
477 | /// See also: |
478 | /// |
479 | /// * [deferFirstFrame], which defers when the first frame is sent to the |
480 | /// engine. |
481 | bool get sendFramesToEngine => _firstFrameSent || _firstFrameDeferredCount == 0; |
482 | |
483 | /// Tell the framework to not send the first frames to the engine until there |
484 | /// is a corresponding call to [allowFirstFrame]. |
485 | /// |
486 | /// Call this to perform asynchronous initialization work before the first |
487 | /// frame is rendered (which takes down the splash screen). The framework |
488 | /// will still do all the work to produce frames, but those frames are never |
489 | /// sent to the engine and will not appear on screen. |
490 | /// |
491 | /// Calling this has no effect after the first frame has been sent to the |
492 | /// engine. |
493 | void deferFirstFrame() { |
494 | assert(_firstFrameDeferredCount >= 0); |
495 | _firstFrameDeferredCount += 1; |
496 | } |
497 | |
498 | /// Called after [deferFirstFrame] to tell the framework that it is ok to |
499 | /// send the first frame to the engine now. |
500 | /// |
501 | /// For best performance, this method should only be called while the |
502 | /// [schedulerPhase] is [SchedulerPhase.idle]. |
503 | /// |
504 | /// This method may only be called once for each corresponding call |
505 | /// to [deferFirstFrame]. |
506 | void allowFirstFrame() { |
507 | assert(_firstFrameDeferredCount > 0); |
508 | _firstFrameDeferredCount -= 1; |
509 | // Always schedule a warm up frame even if the deferral count is not down to |
510 | // zero yet since the removal of a deferral may uncover new deferrals that |
511 | // are lower in the widget tree. |
512 | if (!_firstFrameSent) { |
513 | scheduleWarmUpFrame(); |
514 | } |
515 | } |
516 | |
517 | /// Call this to pretend that no frames have been sent to the engine yet. |
518 | /// |
519 | /// This is useful for tests that want to call [deferFirstFrame] and |
520 | /// [allowFirstFrame] since those methods only have an effect if no frames |
521 | /// have been sent to the engine yet. |
522 | void resetFirstFrameSent() { |
523 | _firstFrameSent = false; |
524 | } |
525 | |
526 | /// Pump the rendering pipeline to generate a frame. |
527 | /// |
528 | /// This method is called by [handleDrawFrame], which itself is called |
529 | /// automatically by the engine when it is time to lay out and paint a frame. |
530 | /// |
531 | /// Each frame consists of the following phases: |
532 | /// |
533 | /// 1. The animation phase: The [handleBeginFrame] method, which is registered |
534 | /// with [PlatformDispatcher.onBeginFrame], invokes all the transient frame |
535 | /// callbacks registered with [scheduleFrameCallback], in registration order. |
536 | /// This includes all the [Ticker] instances that are driving |
537 | /// [AnimationController] objects, which means all of the active [Animation] |
538 | /// objects tick at this point. |
539 | /// |
540 | /// 2. Microtasks: After [handleBeginFrame] returns, any microtasks that got |
541 | /// scheduled by transient frame callbacks get to run. This typically includes |
542 | /// callbacks for futures from [Ticker]s and [AnimationController]s that |
543 | /// completed this frame. |
544 | /// |
545 | /// After [handleBeginFrame], [handleDrawFrame], which is registered with |
546 | /// [dart:ui.PlatformDispatcher.onDrawFrame], is called, which invokes all the |
547 | /// persistent frame callbacks, of which the most notable is this method, |
548 | /// [drawFrame], which proceeds as follows: |
549 | /// |
550 | /// 3. The layout phase: All the dirty [RenderObject]s in the system are laid |
551 | /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout] |
552 | /// for further details on marking an object dirty for layout. |
553 | /// |
554 | /// 4. The compositing bits phase: The compositing bits on any dirty |
555 | /// [RenderObject] objects are updated. See |
556 | /// [RenderObject.markNeedsCompositingBitsUpdate]. |
557 | /// |
558 | /// 5. The paint phase: All the dirty [RenderObject]s in the system are |
559 | /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See |
560 | /// [RenderObject.markNeedsPaint] for further details on marking an object |
561 | /// dirty for paint. |
562 | /// |
563 | /// 6. The compositing phase: The layer tree is turned into a [Scene] and |
564 | /// sent to the GPU. |
565 | /// |
566 | /// 7. The semantics phase: All the dirty [RenderObject]s in the system have |
567 | /// their semantics updated. This generates the [SemanticsNode] tree. See |
568 | /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an |
569 | /// object dirty for semantics. |
570 | /// |
571 | /// For more details on steps 3-7, see [PipelineOwner]. |
572 | /// |
573 | /// 8. The finalization phase: After [drawFrame] returns, [handleDrawFrame] |
574 | /// then invokes post-frame callbacks (registered with [addPostFrameCallback]). |
575 | /// |
576 | /// Some bindings (for example, the [WidgetsBinding]) add extra steps to this |
577 | /// list (for example, see [WidgetsBinding.drawFrame]). |
578 | // |
579 | // When editing the above, also update widgets/binding.dart's copy. |
580 | @protected |
581 | void drawFrame() { |
582 | rootPipelineOwner.flushLayout(); |
583 | rootPipelineOwner.flushCompositingBits(); |
584 | rootPipelineOwner.flushPaint(); |
585 | if (sendFramesToEngine) { |
586 | for (final RenderView renderView in renderViews) { |
587 | renderView.compositeFrame(); // this sends the bits to the GPU |
588 | } |
589 | rootPipelineOwner.flushSemantics(); // this sends the semantics to the OS. |
590 | _firstFrameSent = true; |
591 | } |
592 | } |
593 | |
594 | @override |
595 | Future<void> performReassemble() async { |
596 | await super.performReassemble(); |
597 | if (!kReleaseMode) { |
598 | FlutterTimeline.startSync('Preparing Hot Reload (layout)' ); |
599 | } |
600 | try { |
601 | for (final RenderView renderView in renderViews) { |
602 | renderView.reassemble(); |
603 | } |
604 | } finally { |
605 | if (!kReleaseMode) { |
606 | FlutterTimeline.finishSync(); |
607 | } |
608 | } |
609 | scheduleWarmUpFrame(); |
610 | await endOfFrame; |
611 | } |
612 | |
613 | @override |
614 | void hitTestInView(HitTestResult result, Offset position, int viewId) { |
615 | _viewIdToRenderView[viewId]?.hitTest(result, position: position); |
616 | super.hitTestInView(result, position, viewId); |
617 | } |
618 | |
619 | Future<void> _forceRepaint() { |
620 | late RenderObjectVisitor visitor; |
621 | visitor = (RenderObject child) { |
622 | child.markNeedsPaint(); |
623 | child.visitChildren(visitor); |
624 | }; |
625 | for (final RenderView renderView in renderViews) { |
626 | renderView.visitChildren(visitor); |
627 | } |
628 | return endOfFrame; |
629 | } |
630 | } |
631 | |
632 | String _debugCollectRenderTrees() { |
633 | if (RendererBinding.instance.renderViews.isEmpty) { |
634 | return 'No render tree root was added to the binding.' ; |
635 | } |
636 | return <String>[ |
637 | for (final RenderView renderView in RendererBinding.instance.renderViews) |
638 | renderView.toStringDeep(), |
639 | ].join('\n\n' ); |
640 | } |
641 | |
642 | /// Prints a textual representation of the render trees. |
643 | /// |
644 | /// {@template flutter.rendering.debugDumpRenderTree} |
645 | /// It prints the trees associated with every [RenderView] in |
646 | /// [RendererBinding.renderView], separated by two blank lines. |
647 | /// {@endtemplate} |
648 | void debugDumpRenderTree() { |
649 | debugPrint(_debugCollectRenderTrees()); |
650 | } |
651 | |
652 | String _debugCollectLayerTrees() { |
653 | if (RendererBinding.instance.renderViews.isEmpty) { |
654 | return 'No render tree root was added to the binding.' ; |
655 | } |
656 | return <String>[ |
657 | for (final RenderView renderView in RendererBinding.instance.renderViews) |
658 | renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable for $renderView.' , |
659 | ].join('\n\n' ); |
660 | } |
661 | |
662 | /// Prints a textual representation of the layer trees. |
663 | /// |
664 | /// {@macro flutter.rendering.debugDumpRenderTree} |
665 | void debugDumpLayerTree() { |
666 | debugPrint(_debugCollectLayerTrees()); |
667 | } |
668 | |
669 | String _debugCollectSemanticsTrees(DebugSemanticsDumpOrder childOrder) { |
670 | if (RendererBinding.instance.renderViews.isEmpty) { |
671 | return 'No render tree root was added to the binding.' ; |
672 | } |
673 | const String explanation = 'For performance reasons, the framework only generates semantics when asked to do so by the platform.\n' |
674 | 'Usually, platforms only ask for semantics when assistive technologies (like screen readers) are running.\n' |
675 | 'To generate semantics, try turning on an assistive technology (like VoiceOver or TalkBack) on your device.' ; |
676 | final List<String> trees = <String>[]; |
677 | bool printedExplanation = false; |
678 | for (final RenderView renderView in RendererBinding.instance.renderViews) { |
679 | final String? tree = renderView.debugSemantics?.toStringDeep(childOrder: childOrder); |
680 | if (tree != null) { |
681 | trees.add(tree); |
682 | } else { |
683 | String message = 'Semantics not generated for $renderView.' ; |
684 | if (!printedExplanation) { |
685 | printedExplanation = true; |
686 | message = ' $message\n $explanation' ; |
687 | } |
688 | trees.add(message); |
689 | } |
690 | } |
691 | return trees.join('\n\n' ); |
692 | } |
693 | |
694 | /// Prints a textual representation of the semantics trees. |
695 | /// |
696 | /// {@macro flutter.rendering.debugDumpRenderTree} |
697 | /// |
698 | /// Semantics trees are only constructed when semantics are enabled (see |
699 | /// [SemanticsBinding.semanticsEnabled]). If a semantics tree is not available, |
700 | /// a notice about the missing semantics tree is printed instead. |
701 | /// |
702 | /// The order in which the children of a [SemanticsNode] will be printed is |
703 | /// controlled by the [childOrder] parameter. |
704 | void debugDumpSemanticsTree([DebugSemanticsDumpOrder childOrder = DebugSemanticsDumpOrder.traversalOrder]) { |
705 | debugPrint(_debugCollectSemanticsTrees(childOrder)); |
706 | } |
707 | |
708 | /// Prints a textual representation of the [PipelineOwner] tree rooted at |
709 | /// [RendererBinding.rootPipelineOwner]. |
710 | void debugDumpPipelineOwnerTree() { |
711 | debugPrint(RendererBinding.instance.rootPipelineOwner.toStringDeep()); |
712 | } |
713 | |
714 | /// A concrete binding for applications that use the Rendering framework |
715 | /// directly. This is the glue that binds the framework to the Flutter engine. |
716 | /// |
717 | /// When using the rendering framework directly, this binding, or one that |
718 | /// implements the same interfaces, must be used. The following |
719 | /// mixins are used to implement this binding: |
720 | /// |
721 | /// * [GestureBinding], which implements the basics of hit testing. |
722 | /// * [SchedulerBinding], which introduces the concepts of frames. |
723 | /// * [ServicesBinding], which provides access to the plugin subsystem. |
724 | /// * [SemanticsBinding], which supports accessibility. |
725 | /// * [PaintingBinding], which enables decoding images. |
726 | /// * [RendererBinding], which handles the render tree. |
727 | /// |
728 | /// You would only use this binding if you are writing to the |
729 | /// rendering layer directly. If you are writing to a higher-level |
730 | /// library, such as the Flutter Widgets library, then you would use |
731 | /// that layer's binding (see [WidgetsFlutterBinding]). |
732 | /// |
733 | /// The [RenderingFlutterBinding] can manage multiple render trees. Each render |
734 | /// tree is rooted in a [RenderView] that must be added to the binding via |
735 | /// [addRenderView] to be consider during frame production, hit testing, etc. |
736 | /// Furthermore, the render tree must be managed by a [PipelineOwner] that is |
737 | /// part of the pipeline owner tree rooted at [rootPipelineOwner]. |
738 | /// |
739 | /// Adding [PipelineOwner]s and [RenderView]s to this binding in the way |
740 | /// described above is left as a responsibility for a higher level abstraction. |
741 | /// The binding does not own any [RenderView]s directly. |
742 | class RenderingFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, SemanticsBinding, PaintingBinding, RendererBinding { |
743 | /// Returns an instance of the binding that implements |
744 | /// [RendererBinding]. If no binding has yet been initialized, the |
745 | /// [RenderingFlutterBinding] class is used to create and initialize |
746 | /// one. |
747 | /// |
748 | /// You need to call this method before using the rendering framework |
749 | /// if you are using it directly. If you are using the widgets framework, |
750 | /// see [WidgetsFlutterBinding.ensureInitialized]. |
751 | static RendererBinding ensureInitialized() { |
752 | if (RendererBinding._instance == null) { |
753 | RenderingFlutterBinding(); |
754 | } |
755 | return RendererBinding.instance; |
756 | } |
757 | } |
758 | |
759 | /// A [PipelineManifold] implementation that is backed by the [RendererBinding]. |
760 | class _BindingPipelineManifold extends ChangeNotifier implements PipelineManifold { |
761 | _BindingPipelineManifold(this._binding) { |
762 | _binding.addSemanticsEnabledListener(notifyListeners); |
763 | } |
764 | |
765 | final RendererBinding _binding; |
766 | |
767 | @override |
768 | void requestVisualUpdate() { |
769 | _binding.ensureVisualUpdate(); |
770 | } |
771 | |
772 | @override |
773 | bool get semanticsEnabled => _binding.semanticsEnabled; |
774 | |
775 | @override |
776 | void dispose() { |
777 | _binding.removeSemanticsEnabledListener(notifyListeners); |
778 | super.dispose(); |
779 | } |
780 | } |
781 | |
782 | // A [PipelineOwner] that cannot have a root node. |
783 | class _DefaultRootPipelineOwner extends PipelineOwner { |
784 | _DefaultRootPipelineOwner() : super(onSemanticsUpdate: _onSemanticsUpdate); |
785 | |
786 | @override |
787 | set rootNode(RenderObject? _) { |
788 | assert(() { |
789 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
790 | ErrorSummary( |
791 | 'Cannot set a rootNode on the default root pipeline owner.' , |
792 | ), |
793 | ErrorDescription( |
794 | 'By default, the RendererBinding.rootPipelineOwner is not configured ' |
795 | 'to manage a root node because this pipeline owner does not define a ' |
796 | 'proper onSemanticsUpdate callback to handle semantics for that node.' , |
797 | ), |
798 | ErrorHint( |
799 | 'Typically, the root pipeline owner does not manage a root node. ' |
800 | 'Instead, properly configured child pipeline owners (which do manage ' |
801 | 'root nodes) are added to it. Alternatively, if you do want to set a ' |
802 | 'root node for the root pipeline owner, override ' |
803 | 'RendererBinding.createRootPipelineOwner to create a ' |
804 | 'pipeline owner that is configured to properly handle semantics for ' |
805 | 'the provided root node.' |
806 | ), |
807 | ]); |
808 | }()); |
809 | } |
810 | |
811 | static void _onSemanticsUpdate(ui.SemanticsUpdate _) { |
812 | // Neve called because we don't have a root node. |
813 | assert(false); |
814 | } |
815 | } |
816 | |
817 | // Prior to multi view support, the [RendererBinding] would own a long-lived |
818 | // [RenderView], that was never disposed (see [RendererBinding.renderView]). |
819 | // With multi view support, the [RendererBinding] no longer owns a [RenderView] |
820 | // and instead higher level abstractions (like the [View] widget) can add/remove |
821 | // multiple [RenderView]s to the binding as needed. When the [View] widget is no |
822 | // longer needed, it expects to dispose its [RenderView]. |
823 | // |
824 | // This special version of a [RenderView] now exists as a bridge between those |
825 | // worlds to continue supporting the [RendererBinding.renderView] property |
826 | // through its deprecation period. Per the property's contract, it is supposed |
827 | // to be long-lived, but it is also managed by a [View] widget (introduced by |
828 | // [WidgetsBinding.wrapWithDefaultView]), that expects to dispose its render |
829 | // object at the end of the widget's life time. This special version now |
830 | // implements logic to reset the [RenderView] when it is "disposed" so it can be |
831 | // reused by another [View] widget. |
832 | // |
833 | // Once the deprecated [RendererBinding.renderView] property is removed, this |
834 | // class is no longer necessary. |
835 | class _ReusableRenderView extends RenderView { |
836 | _ReusableRenderView({required super.view}); |
837 | |
838 | bool _initialFramePrepared = false; |
839 | |
840 | @override |
841 | void prepareInitialFrame() { |
842 | if (_initialFramePrepared) { |
843 | return; |
844 | } |
845 | super.prepareInitialFrame(); |
846 | _initialFramePrepared = true; |
847 | } |
848 | |
849 | @override |
850 | void scheduleInitialSemantics() { |
851 | clearSemantics(); |
852 | super.scheduleInitialSemantics(); |
853 | } |
854 | |
855 | @override |
856 | void dispose() { // ignore: must_call_super |
857 | child = null; |
858 | } |
859 | } |
860 | |