binding.dart 33.1 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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';
Ian Hickson's avatar
Ian Hickson committed
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

Ian Hickson's avatar
Ian Hickson committed
19
export 'package:flutter/gestures.dart' show HitTestResult;
20

21
// Examples can assume:
22
// late BuildContext context;
23

24 25 26 27 28 29 30 31 32 33 34 35
/// The glue between the render trees and the Flutter engine.
///
/// The [RendererBinding] manages multiple independent render trees. Each render
/// tree is rooted in a [RenderView] that must be added to the binding via
/// [addRenderView] to be considered during frame production, hit testing, etc.
/// Furthermore, the render tree must be managed by a [PipelineOwner] that is
/// part of the pipeline owner tree rooted at [rootPipelineOwner].
///
/// Adding [PipelineOwner]s and [RenderView]s to this binding in the way
/// described above is left as a responsibility for a higher level abstraction.
/// The widgets library, for example, introduces the [View] widget, which
/// registers its [RenderView] and [PipelineOwner] with this binding.
36
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
37
  @override
Ian Hickson's avatar
Ian Hickson committed
38 39
  void initInstances() {
    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 57 58 59
  ///
  /// Provides access to the features exposed by this mixin. The binding must
  /// be initialized before using this getter; this is typically done by calling
  /// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
  static RendererBinding get instance => BindingBase.checkInstance(_instance);
60
  static RendererBinding? _instance;
61 62 63 64

  @override
  void initServiceExtensions() {
    super.initServiceExtensions();
65

66
    assert(() {
67
      // these service extensions only work in debug mode
68
      registerBoolServiceExtension(
69
        name: RenderingServiceExtensions.invertOversizedImages.name,
70 71 72 73 74 75 76 77 78
        getter: () async => debugInvertOversizedImages,
        setter: (bool value) async {
          if (debugInvertOversizedImages != value) {
            debugInvertOversizedImages = value;
            return _forceRepaint();
          }
          return Future<void>.value();
        },
      );
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 93
        getter: () async => debugPaintBaselinesEnabled,
        setter: (bool value) {
94
          if (debugPaintBaselinesEnabled == value) {
95
            return Future<void>.value();
96
          }
97 98
          debugPaintBaselinesEnabled = value;
          return _forceRepaint();
99
        },
100 101
      );
      registerBoolServiceExtension(
102
        name: RenderingServiceExtensions.repaintRainbow.name,
103 104 105 106
        getter: () async => debugRepaintRainbowEnabled,
        setter: (bool value) {
          final bool repaint = debugRepaintRainbowEnabled && !value;
          debugRepaintRainbowEnabled = value;
107
          if (repaint) {
108
            return _forceRepaint();
109
          }
110
          return Future<void>.value();
111 112
        },
      );
113
      registerServiceExtension(
114
        name: RenderingServiceExtensions.debugDumpLayerTree.name,
115 116
        callback: (Map<String, String> parameters) async {
          return <String, Object>{
117
            'data': _debugCollectLayerTrees(),
118
          };
119 120
        },
      );
121
      registerBoolServiceExtension(
122
        name: RenderingServiceExtensions.debugDisableClipLayers.name,
123 124
        getter: () async => debugDisableClipLayers,
        setter: (bool value) {
125
          if (debugDisableClipLayers == value) {
126
            return Future<void>.value();
127
          }
128 129 130 131 132
          debugDisableClipLayers = value;
          return _forceRepaint();
        },
      );
      registerBoolServiceExtension(
133
        name: RenderingServiceExtensions.debugDisablePhysicalShapeLayers.name,
134 135
        getter: () async => debugDisablePhysicalShapeLayers,
        setter: (bool value) {
136
          if (debugDisablePhysicalShapeLayers == value) {
137
            return Future<void>.value();
138
          }
139 140 141 142 143
          debugDisablePhysicalShapeLayers = value;
          return _forceRepaint();
        },
      );
      registerBoolServiceExtension(
144
        name: RenderingServiceExtensions.debugDisableOpacityLayers.name,
145 146
        getter: () async => debugDisableOpacityLayers,
        setter: (bool value) {
147
          if (debugDisableOpacityLayers == value) {
148
            return Future<void>.value();
149
          }
150 151 152 153
          debugDisableOpacityLayers = value;
          return _forceRepaint();
        },
      );
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 162
        callback: (Map<String, String> parameters) async {
          return <String, Object>{
163
            'data': _debugCollectRenderTrees(),
164
          };
165
        },
166
      );
167
      registerServiceExtension(
168
        name: RenderingServiceExtensions.debugDumpSemanticsTreeInTraversalOrder.name,
169 170
        callback: (Map<String, String> parameters) async {
          return <String, Object>{
171
            'data': _debugCollectSemanticsTrees(DebugSemanticsDumpOrder.traversalOrder),
172
          };
173 174
        },
      );
175
      registerServiceExtension(
176
        name: RenderingServiceExtensions.debugDumpSemanticsTreeInInverseHitTestOrder.name,
177 178
        callback: (Map<String, String> parameters) async {
          return <String, Object>{
179
            'data': _debugCollectSemanticsTrees(DebugSemanticsDumpOrder.inverseHitTest),
180
          };
181 182
        },
      );
183
      registerBoolServiceExtension(
184
        name: RenderingServiceExtensions.profileRenderObjectPaints.name,
185 186
        getter: () async => debugProfilePaintsEnabled,
        setter: (bool value) async {
187
          if (debugProfilePaintsEnabled != value) {
188
            debugProfilePaintsEnabled = value;
189
          }
190 191 192
        },
      );
      registerBoolServiceExtension(
193
        name: RenderingServiceExtensions.profileRenderObjectLayouts.name,
194 195
        getter: () async => debugProfileLayoutsEnabled,
        setter: (bool value) async {
196
          if (debugProfileLayoutsEnabled != value) {
197
            debugProfileLayoutsEnabled = value;
198
          }
199 200
        },
      );
201
    }
202
  }
203

204 205
  late final PipelineManifold _manifold = _BindingPipelineManifold(this);

206 207
  /// The object that manages state about currently connected mice, for hover
  /// notification.
208 209
  MouseTracker get mouseTracker => _mouseTracker!;
  MouseTracker? _mouseTracker;
210

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
  /// Deprecated. Will be removed in a future version of Flutter.
  ///
  /// This is typically the owner of the render tree bootstrapped by [runApp]
  /// and rooted in [renderView]. It maintains dirty state for layout,
  /// composite, paint, and accessibility semantics for that tree.
  ///
  /// However, by default, the [pipelineOwner] does not participate in frame
  /// production because it is not automatically attached to the
  /// [rootPipelineOwner] or any of its descendants. It is also not
  /// automatically associated with the [renderView]. This is left as a
  /// responsibility for a higher level abstraction. The [WidgetsBinding], for
  /// example, wires this up in [WidgetsBinding.wrapWithDefaultView], which is
  /// called indirectly from [runApp].
  ///
  /// Apps, that don't use the [WidgetsBinding] or don't call [runApp] (or
  /// [WidgetsBinding.wrapWithDefaultView]) must manually add this pipeline owner
  /// to the pipeline owner tree rooted at [rootPipelineOwner] and assign a
  /// [RenderView] to it if the they want to use this deprecated property.
  ///
  /// Instead of accessing this deprecated property, consider interacting with
  /// the root of the [PipelineOwner] tree (exposed in [rootPipelineOwner]) or
  /// instead of accessing the [SemanticsOwner] of any [PipelineOwner] consider
  /// interacting with the [SemanticsBinding] (exposed via
  /// [SemanticsBinding.instance]) directly.
  @Deprecated(
    'Interact with the pipelineOwner tree rooted at RendererBinding.rootPipelineOwner instead. '
    'Or instead of accessing the SemanticsOwner of any PipelineOwner interact with the SemanticsBinding directly. '
    'This feature was deprecated after v3.10.0-12.0.pre.'
  )
  late final PipelineOwner pipelineOwner = PipelineOwner(
    onSemanticsOwnerCreated: () {
      (pipelineOwner.rootNode as RenderView?)?.scheduleInitialSemantics();
    },
    onSemanticsUpdate: (ui.SemanticsUpdate update) {
      (pipelineOwner.rootNode as RenderView?)?.updateSemantics(update);
    },
    onSemanticsOwnerDisposed: () {
      (pipelineOwner.rootNode as RenderView?)?.clearSemantics();
    }
  );

  /// Deprecated. Will be removed in a future version of Flutter.
  ///
  /// This is typically the root of the render tree bootstrapped by [runApp].
  ///
  /// However, by default this render view is not associated with any
  /// [PipelineOwner] and therefore isn't considered during frame production.
  /// It is also not registered with this binding via [addRenderView].
  /// Wiring this up is left as a responsibility for a higher level. The
  /// [WidgetsBinding], for example, sets this up in
  /// [WidgetsBinding.wrapWithDefaultView], which is called indirectly from
  /// [runApp].
  ///
  /// Apps that don't use the [WidgetsBinding] or don't call [runApp] (or
  /// [WidgetsBinding.wrapWithDefaultView]) must manually assign a
  /// [PipelineOwner] to this [RenderView], make sure the pipeline owner is part
  /// of the pipeline owner tree rooted at [rootPipelineOwner], and call
  /// [addRenderView] if they want to use this deprecated property.
  ///
  /// Instead of interacting with this deprecated property, consider using
  /// [renderViews] instead, which contains all [RenderView]s managed by the
  /// binding.
  @Deprecated(
    'Consider using RendererBinding.renderViews instead as the binding may manage multiple RenderViews. '
    'This feature was deprecated after v3.10.0-12.0.pre.'
  )
  // TODO(goderbauer): When this deprecated property is removed also delete the _ReusableRenderView class.
  late final RenderView renderView = _ReusableRenderView(
    view: platformDispatcher.implicitView!,
  );

  /// Creates the [PipelineOwner] that serves as the root of the pipeline owner
  /// tree ([rootPipelineOwner]).
  ///
  /// {@template flutter.rendering.createRootPipelineOwner}
  /// By default, the root pipeline owner is not setup to manage a render tree
  /// and its [PipelineOwner.rootNode] must not be assigned. If necessary,
  /// [createRootPipelineOwner] may be overridden to create a root pipeline
  /// owner configured to manage its own render tree.
  ///
  /// In typical use, child pipeline owners are added to the root pipeline owner
  /// (via [PipelineOwner.adoptChild]). Those children typically do each manage
  /// their own [RenderView] and produce distinct render trees which render
  /// their content into the [FlutterView] associated with that [RenderView].
  /// {@endtemplate}
  PipelineOwner createRootPipelineOwner() {
    return _DefaultRootPipelineOwner();
  }

  /// The [PipelineOwner] that is the root of the PipelineOwner tree.
  ///
  /// {@macro flutter.rendering.createRootPipelineOwner}
  PipelineOwner get rootPipelineOwner => _rootPipelineOwner;
  late PipelineOwner _rootPipelineOwner;

  /// The [RenderView]s managed by this binding.
  ///
  /// A [RenderView] is added by [addRenderView] and removed by [removeRenderView].
  Iterable<RenderView> get renderViews => _viewIdToRenderView.values;
  final Map<Object, RenderView> _viewIdToRenderView = <Object, RenderView>{};

  /// Adds a [RenderView] to this binding.
  ///
  /// The binding will interact with the [RenderView] in the following ways:
  ///
  ///  * setting and updating [RenderView.configuration],
  ///  * calling [RenderView.compositeFrame] when it is time to produce a new
  ///    frame, and
  ///  * forwarding relevant pointer events to the [RenderView] for hit testing.
  ///
  /// To remove a [RenderView] from the binding, call [removeRenderView].
  void addRenderView(RenderView view) {
    final Object viewId = view.flutterView.viewId;
    assert(!_viewIdToRenderView.containsValue(view));
    assert(!_viewIdToRenderView.containsKey(viewId));
    _viewIdToRenderView[viewId] = view;
    view.configuration = createViewConfigurationFor(view);
  }
329

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
  /// Removes a [RenderView] previously added with [addRenderView] from the
  /// binding.
  void removeRenderView(RenderView view) {
    final Object viewId = view.flutterView.viewId;
    assert(_viewIdToRenderView[viewId] == view);
    _viewIdToRenderView.remove(viewId);
  }

  /// Returns a [ViewConfiguration] configured for the provided [RenderView]
  /// based on the current environment.
  ///
  /// This is called during [addRenderView] and also in response to changes to
  /// the system metrics to update all [renderViews] added to the binding.
  ///
  /// Bindings can override this method to change what size or device pixel
  /// ratio the [RenderView] will use. For example, the testing framework uses
  /// this to force the display into 800x600 when a test is run on the device
  /// using `flutter run`.
  @protected
  ViewConfiguration createViewConfigurationFor(RenderView renderView) {
350
    return ViewConfiguration.fromView(renderView.flutterView);
351 352
  }

353
  /// Called when the system metrics change.
354
  ///
355
  /// See [dart:ui.PlatformDispatcher.onMetricsChanged].
356
  @protected
357
  @visibleForTesting
Ian Hickson's avatar
Ian Hickson committed
358
  void handleMetricsChanged() {
359 360 361 362 363 364
    bool forceFrame = false;
    for (final RenderView view in renderViews) {
      forceFrame = forceFrame || view.child != null;
      view.configuration = createViewConfigurationFor(view);
    }
    if (forceFrame) {
365 366
      scheduleForcedFrame();
    }
367 368
  }

369 370
  /// Called when the platform text scale factor changes.
  ///
371
  /// See [dart:ui.PlatformDispatcher.onTextScaleFactorChanged].
372
  @protected
373 374
  void handleTextScaleFactorChanged() { }

375 376
  /// Called when the platform brightness changes.
  ///
377 378 379 380
  /// The current platform brightness can be queried from a Flutter binding or
  /// from a [MediaQuery] widget. The latter is preferred from widgets because
  /// it causes the widget to be automatically rebuilt when the brightness
  /// changes.
381
  ///
382
  /// {@tool snippet}
383
  /// Querying [MediaQuery.platformBrightnessOf] directly. Preferred.
384 385
  ///
  /// ```dart
386
  /// final Brightness brightness = MediaQuery.platformBrightnessOf(context);
387
  /// ```
388
  /// {@end-tool}
389
  ///
390
  /// {@tool snippet}
391
  /// Querying [PlatformDispatcher.platformBrightness].
392 393
  ///
  /// ```dart
394
  /// final Brightness brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness;
395
  /// ```
396
  /// {@end-tool}
397
  ///
398
  /// See [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged].
399 400 401
  @protected
  void handlePlatformBrightnessChanged() { }

402 403 404 405 406
  /// Creates a [MouseTracker] which manages state about currently connected
  /// mice, for hover notification.
  ///
  /// Used by testing framework to reinitialize the mouse tracker between tests.
  @visibleForTesting
407
  void initMouseTracker([MouseTracker? tracker]) {
408
    _mouseTracker?.dispose();
409 410 411 412 413
    _mouseTracker = tracker ?? MouseTracker((Offset position, int viewId) {
      final HitTestResult result = HitTestResult();
      hitTestInView(result, position, viewId);
      return result;
    });
414 415 416
  }

  @override // from GestureBinding
417
  void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
418 419
    _mouseTracker!.updateWithEvent(
      event,
420
      // When the button is pressed, normal hit test uses a cached
421 422
      // result, but MouseTracker requires that the hit test is re-executed to
      // update the hovering events.
423
      event is PointerMoveEvent ? null : hitTestResult,
424
    );
425
    super.dispatchEvent(event, hitTestResult);
426 427
  }

428 429
  @override
  void performSemanticsAction(SemanticsActionEvent action) {
430 431 432 433
    // Due to the asynchronicity in some screen readers (they may not have
    // processed the latest semantics update yet) this code is more forgiving
    // and actions for views/nodes that no longer exist are gracefully ignored.
    _viewIdToRenderView[action.viewId]?.owner?.semanticsOwner?.performAction(action.nodeId, action.type, action.arguments);
Hixie's avatar
Hixie committed
434 435
  }

436 437 438 439 440 441
  void _handleWebFirstFrame(Duration _) {
    assert(kIsWeb);
    const MethodChannel methodChannel = MethodChannel('flutter/service_worker');
    methodChannel.invokeMethod<void>('first-frame');
  }

442
  void _handlePersistentFrameCallback(Duration timeStamp) {
443
    drawFrame();
444 445 446 447 448 449 450 451 452 453
    _scheduleMouseTrackerUpdate();
  }

  bool _debugMouseTrackerUpdateScheduled = false;
  void _scheduleMouseTrackerUpdate() {
    assert(!_debugMouseTrackerUpdateScheduled);
    assert(() {
      _debugMouseTrackerUpdateScheduled = true;
      return true;
    }());
454
    SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
455 456 457 458 459
      assert(_debugMouseTrackerUpdateScheduled);
      assert(() {
        _debugMouseTrackerUpdateScheduled = false;
        return true;
      }());
460
      _mouseTracker!.updateAllDevices();
461
    }, debugLabel: 'RendererBinding.mouseTrackerUpdate');
462 463
  }

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
  int _firstFrameDeferredCount = 0;
  bool _firstFrameSent = false;

  /// Whether frames produced by [drawFrame] are sent to the engine.
  ///
  /// If false the framework will do all the work to produce a frame,
  /// but the frame is never sent to the engine to actually appear on screen.
  ///
  /// See also:
  ///
  ///  * [deferFirstFrame], which defers when the first frame is sent to the
  ///    engine.
  bool get sendFramesToEngine => _firstFrameSent || _firstFrameDeferredCount == 0;

  /// Tell the framework to not send the first frames to the engine until there
  /// is a corresponding call to [allowFirstFrame].
  ///
  /// Call this to perform asynchronous initialization work before the first
  /// frame is rendered (which takes down the splash screen). The framework
  /// will still do all the work to produce frames, but those frames are never
  /// sent to the engine and will not appear on screen.
  ///
  /// Calling this has no effect after the first frame has been sent to the
  /// engine.
  void deferFirstFrame() {
    assert(_firstFrameDeferredCount >= 0);
    _firstFrameDeferredCount += 1;
  }

  /// Called after [deferFirstFrame] to tell the framework that it is ok to
  /// send the first frame to the engine now.
  ///
  /// For best performance, this method should only be called while the
  /// [schedulerPhase] is [SchedulerPhase.idle].
  ///
  /// This method may only be called once for each corresponding call
  /// to [deferFirstFrame].
  void allowFirstFrame() {
    assert(_firstFrameDeferredCount > 0);
    _firstFrameDeferredCount -= 1;
    // Always schedule a warm up frame even if the deferral count is not down to
    // zero yet since the removal of a deferral may uncover new deferrals that
    // are lower in the widget tree.
507
    if (!_firstFrameSent) {
508
      scheduleWarmUpFrame();
509
    }
510 511 512 513 514 515 516 517 518 519 520
  }

  /// Call this to pretend that no frames have been sent to the engine yet.
  ///
  /// This is useful for tests that want to call [deferFirstFrame] and
  /// [allowFirstFrame] since those methods only have an effect if no frames
  /// have been sent to the engine yet.
  void resetFirstFrameSent() {
    _firstFrameSent = false;
  }

Ian Hickson's avatar
Ian Hickson committed
521
  /// Pump the rendering pipeline to generate a frame.
522
  ///
523
  /// This method is called by [handleDrawFrame], which itself is called
524
  /// automatically by the engine when it is time to lay out and paint a frame.
525 526 527 528
  ///
  /// Each frame consists of the following phases:
  ///
  /// 1. The animation phase: The [handleBeginFrame] method, which is registered
529 530 531 532 533
  /// with [PlatformDispatcher.onBeginFrame], invokes all the transient frame
  /// callbacks registered with [scheduleFrameCallback], in registration order.
  /// This includes all the [Ticker] instances that are driving
  /// [AnimationController] objects, which means all of the active [Animation]
  /// objects tick at this point.
534
  ///
535 536 537 538
  /// 2. Microtasks: After [handleBeginFrame] returns, any microtasks that got
  /// scheduled by transient frame callbacks get to run. This typically includes
  /// callbacks for futures from [Ticker]s and [AnimationController]s that
  /// completed this frame.
539
  ///
540
  /// After [handleBeginFrame], [handleDrawFrame], which is registered with
541 542 543
  /// [dart:ui.PlatformDispatcher.onDrawFrame], is called, which invokes all the
  /// persistent frame callbacks, of which the most notable is this method,
  /// [drawFrame], which proceeds as follows:
544 545
  ///
  /// 3. The layout phase: All the dirty [RenderObject]s in the system are laid
546 547 548
  /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout]
  /// for further details on marking an object dirty for layout.
  ///
549
  /// 4. The compositing bits phase: The compositing bits on any dirty
550 551 552
  /// [RenderObject] objects are updated. See
  /// [RenderObject.markNeedsCompositingBitsUpdate].
  ///
553
  /// 5. The paint phase: All the dirty [RenderObject]s in the system are
554 555 556 557
  /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See
  /// [RenderObject.markNeedsPaint] for further details on marking an object
  /// dirty for paint.
  ///
558
  /// 6. The compositing phase: The layer tree is turned into a [Scene] and
559 560
  /// sent to the GPU.
  ///
561
  /// 7. The semantics phase: All the dirty [RenderObject]s in the system have
562
  /// their semantics updated. This generates the [SemanticsNode] tree. See
563 564 565
  /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an
  /// object dirty for semantics.
  ///
566
  /// For more details on steps 3-7, see [PipelineOwner].
567
  ///
568 569
  /// 8. The finalization phase: After [drawFrame] returns, [handleDrawFrame]
  /// then invokes post-frame callbacks (registered with [addPostFrameCallback]).
570 571
  ///
  /// Some bindings (for example, the [WidgetsBinding]) add extra steps to this
572
  /// list (for example, see [WidgetsBinding.drawFrame]).
573 574
  //
  // When editing the above, also update widgets/binding.dart's copy.
Ian Hickson's avatar
Ian Hickson committed
575
  @protected
576
  void drawFrame() {
577 578 579
    rootPipelineOwner.flushLayout();
    rootPipelineOwner.flushCompositingBits();
    rootPipelineOwner.flushPaint();
580
    if (sendFramesToEngine) {
581 582 583 584
      for (final RenderView renderView in renderViews) {
        renderView.compositeFrame(); // this sends the bits to the GPU
      }
      rootPipelineOwner.flushSemantics(); // this sends the semantics to the OS.
585 586
      _firstFrameSent = true;
    }
587 588
  }

589
  @override
590
  Future<void> performReassemble() async {
591
    await super.performReassemble();
592 593 594 595 596 597
    if (!kReleaseMode) {
      FlutterTimeline.startSync('Preparing Hot Reload (layout)');
    }
    try {
      for (final RenderView renderView in renderViews) {
        renderView.reassemble();
598
      }
599 600 601
    } finally {
      if (!kReleaseMode) {
        FlutterTimeline.finishSync();
602
      }
603
    }
604
    scheduleWarmUpFrame();
605
    await endOfFrame;
606 607
  }

608
  @override
609
  void hitTestInView(HitTestResult result, Offset position, int viewId) {
610
    _viewIdToRenderView[viewId]?.hitTest(result, position: position);
611
    super.hitTestInView(result, position, viewId);
612
  }
613

614
  Future<void> _forceRepaint() {
615
    late RenderObjectVisitor visitor;
616 617 618 619
    visitor = (RenderObject child) {
      child.markNeedsPaint();
      child.visitChildren(visitor);
    };
620 621 622
    for (final RenderView renderView in renderViews) {
      renderView.visitChildren(visitor);
    }
623
    return endOfFrame;
624
  }
625
}
626

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
String _debugCollectRenderTrees() {
  if (RendererBinding.instance.renderViews.isEmpty) {
    return 'No render tree root was added to the binding.';
  }
  return <String>[
    for (final RenderView renderView in RendererBinding.instance.renderViews)
      renderView.toStringDeep(),
  ].join('\n\n');
}

/// Prints a textual representation of the render trees.
///
/// {@template flutter.rendering.debugDumpRenderTree}
/// It prints the trees associated with every [RenderView] in
/// [RendererBinding.renderView], separated by two blank lines.
/// {@endtemplate}
643
void debugDumpRenderTree() {
644 645 646 647 648 649 650 651 652 653 654
  debugPrint(_debugCollectRenderTrees());
}

String _debugCollectLayerTrees() {
  if (RendererBinding.instance.renderViews.isEmpty) {
    return 'No render tree root was added to the binding.';
  }
  return <String>[
    for (final RenderView renderView in RendererBinding.instance.renderViews)
      renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable for $renderView.',
  ].join('\n\n');
655
}
656

657 658 659
/// Prints a textual representation of the layer trees.
///
/// {@macro flutter.rendering.debugDumpRenderTree}
660
void debugDumpLayerTree() {
661
  debugPrint(_debugCollectLayerTrees());
Ian Hickson's avatar
Ian Hickson committed
662 663
}

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
String _debugCollectSemanticsTrees(DebugSemanticsDumpOrder childOrder) {
  if (RendererBinding.instance.renderViews.isEmpty) {
    return 'No render tree root was added to the binding.';
  }
  const String explanation = 'For performance reasons, the framework only generates semantics when asked to do so by the platform.\n'
      'Usually, platforms only ask for semantics when assistive technologies (like screen readers) are running.\n'
      'To generate semantics, try turning on an assistive technology (like VoiceOver or TalkBack) on your device.';
  final List<String> trees = <String>[];
  bool printedExplanation = false;
  for (final RenderView renderView in RendererBinding.instance.renderViews) {
    final String? tree = renderView.debugSemantics?.toStringDeep(childOrder: childOrder);
    if (tree != null) {
      trees.add(tree);
    } else {
      String message = 'Semantics not generated for $renderView.';
      if (!printedExplanation) {
        printedExplanation = true;
        message = '$message\n$explanation';
      }
      trees.add(message);
    }
  }
  return trees.join('\n\n');
}

/// Prints a textual representation of the semantics trees.
///
/// {@macro flutter.rendering.debugDumpRenderTree}
///
/// Semantics trees are only constructed when semantics are enabled (see
/// [SemanticsBinding.semanticsEnabled]). If a semantics tree is not available,
/// a notice about the missing semantics tree is printed instead.
696 697 698
///
/// The order in which the children of a [SemanticsNode] will be printed is
/// controlled by the [childOrder] parameter.
699
void debugDumpSemanticsTree([DebugSemanticsDumpOrder childOrder = DebugSemanticsDumpOrder.traversalOrder]) {
700
  debugPrint(_debugCollectSemanticsTrees(childOrder));
701 702
}

703 704 705 706
/// Prints a textual representation of the [PipelineOwner] tree rooted at
/// [RendererBinding.rootPipelineOwner].
void debugDumpPipelineOwnerTree() {
  debugPrint(RendererBinding.instance.rootPipelineOwner.toStringDeep());
Hixie's avatar
Hixie committed
707 708
}

Ian Hickson's avatar
Ian Hickson committed
709 710
/// A concrete binding for applications that use the Rendering framework
/// directly. This is the glue that binds the framework to the Flutter engine.
711
///
712 713 714 715 716 717 718 719 720 721 722
/// When using the rendering framework directly, this binding, or one that
/// implements the same interfaces, must be used. The following
/// mixins are used to implement this binding:
///
/// * [GestureBinding], which implements the basics of hit testing.
/// * [SchedulerBinding], which introduces the concepts of frames.
/// * [ServicesBinding], which provides access to the plugin subsystem.
/// * [SemanticsBinding], which supports accessibility.
/// * [PaintingBinding], which enables decoding images.
/// * [RendererBinding], which handles the render tree.
///
723 724 725
/// You would only use this binding if you are writing to the
/// rendering layer directly. If you are writing to a higher-level
/// library, such as the Flutter Widgets library, then you would use
726
/// that layer's binding (see [WidgetsFlutterBinding]).
727 728 729 730 731 732 733 734 735 736
///
/// The [RenderingFlutterBinding] can manage multiple render trees. Each render
/// tree is rooted in a [RenderView] that must be added to the binding via
/// [addRenderView] to be consider during frame production, hit testing, etc.
/// Furthermore, the render tree must be managed by a [PipelineOwner] that is
/// part of the pipeline owner tree rooted at [rootPipelineOwner].
///
/// Adding [PipelineOwner]s and [RenderView]s to this binding in the way
/// described above is left as a responsibility for a higher level abstraction.
/// The binding does not own any [RenderView]s directly.
737
class RenderingFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, SemanticsBinding, PaintingBinding, RendererBinding {
738 739 740 741 742 743 744 745 746
  /// Returns an instance of the binding that implements
  /// [RendererBinding]. If no binding has yet been initialized, the
  /// [RenderingFlutterBinding] class is used to create and initialize
  /// one.
  ///
  /// You need to call this method before using the rendering framework
  /// if you are using it directly. If you are using the widgets framework,
  /// see [WidgetsFlutterBinding.ensureInitialized].
  static RendererBinding ensureInitialized() {
747
    if (RendererBinding._instance == null) {
748
      RenderingFlutterBinding();
749
    }
750 751
    return RendererBinding.instance;
  }
752
}
753 754 755 756

/// A [PipelineManifold] implementation that is backed by the [RendererBinding].
class _BindingPipelineManifold extends ChangeNotifier implements PipelineManifold {
  _BindingPipelineManifold(this._binding) {
757 758 759
    if (kFlutterMemoryAllocationsEnabled) {
      ChangeNotifier.maybeDispatchObjectCreation(this);
    }
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    _binding.addSemanticsEnabledListener(notifyListeners);
  }

  final RendererBinding _binding;

  @override
  void requestVisualUpdate() {
    _binding.ensureVisualUpdate();
  }

  @override
  bool get semanticsEnabled => _binding.semanticsEnabled;

  @override
  void dispose() {
    _binding.removeSemanticsEnabledListener(notifyListeners);
    super.dispose();
  }
}
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857

// A [PipelineOwner] that cannot have a root node.
class _DefaultRootPipelineOwner extends PipelineOwner {
  _DefaultRootPipelineOwner() : super(onSemanticsUpdate: _onSemanticsUpdate);

  @override
  set rootNode(RenderObject? _) {
    assert(() {
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary(
          'Cannot set a rootNode on the default root pipeline owner.',
        ),
        ErrorDescription(
          'By default, the RendererBinding.rootPipelineOwner is not configured '
          'to manage a root node because this pipeline owner does not define a '
          'proper onSemanticsUpdate callback to handle semantics for that node.',
        ),
        ErrorHint(
          'Typically, the root pipeline owner does not manage a root node. '
          'Instead, properly configured child pipeline owners (which do manage '
          'root nodes) are added to it. Alternatively, if you do want to set a '
          'root node for the root pipeline owner, override '
          'RendererBinding.createRootPipelineOwner to create a '
          'pipeline owner that is configured to properly handle semantics for '
          'the provided root node.'
        ),
      ]);
    }());
  }

  static void _onSemanticsUpdate(ui.SemanticsUpdate _) {
    // Neve called because we don't have a root node.
    assert(false);
  }
}

// Prior to multi view support, the [RendererBinding] would own a long-lived
// [RenderView], that was never disposed (see [RendererBinding.renderView]).
// With multi view support, the [RendererBinding] no longer owns a [RenderView]
// and instead higher level abstractions (like the [View] widget) can add/remove
// multiple [RenderView]s to the binding as needed. When the [View] widget is no
// longer needed, it expects to dispose its [RenderView].
//
// This special version of a [RenderView] now exists as a bridge between those
// worlds to continue supporting the [RendererBinding.renderView] property
// through its deprecation period. Per the property's contract, it is supposed
// to be long-lived, but it is also managed by a [View] widget (introduced by
// [WidgetsBinding.wrapWithDefaultView]), that expects to dispose its render
// object at the end of the widget's life time. This special version now
// implements logic to reset the [RenderView] when it is "disposed" so it can be
// reused by another [View] widget.
//
// Once the deprecated [RendererBinding.renderView] property is removed, this
// class is no longer necessary.
class _ReusableRenderView extends RenderView {
  _ReusableRenderView({required super.view});

  bool _initialFramePrepared = false;

  @override
  void prepareInitialFrame() {
    if (_initialFramePrepared) {
      return;
    }
    super.prepareInitialFrame();
    _initialFramePrepared = true;
  }

  @override
  void scheduleInitialSemantics() {
    clearSemantics();
    super.scheduleInitialSemantics();
  }

  @override
  void dispose() { // ignore: must_call_super
    child = null;
  }
}