binding.dart 19.3 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:developer';
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 'box.dart';
14
import 'debug.dart';
15
import 'mouse_tracker.dart';
16
import 'object.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

Florian Loitsch's avatar
Florian Loitsch committed
24
/// The glue between the render tree and the Flutter engine.
25
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
26
  @override
Ian Hickson's avatar
Ian Hickson committed
27 28
  void initInstances() {
    super.initInstances();
29
    _instance = this;
30
    _pipelineOwner = PipelineOwner(
31
      onNeedVisualUpdate: ensureVisualUpdate,
32 33
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
34
    );
35
    window
36
      ..onMetricsChanged = handleMetricsChanged
37
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
38
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
39 40
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
Ian Hickson's avatar
Ian Hickson committed
41
    initRenderView();
42
    _handleSemanticsEnabledChanged();
43
    assert(renderView != null);
44
    addPersistentFrameCallback(_handlePersistentFrameCallback);
45
    initMouseTracker();
46 47 48
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
49 50
  }

51
  /// The current [RendererBinding], if one has been created.
52 53
  static RendererBinding? get instance => _instance;
  static RendererBinding? _instance;
54 55 56 57

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

59
    assert(() {
60
      // these service extensions only work in debug mode
61 62 63 64 65 66 67 68 69 70 71
      registerBoolServiceExtension(
        name: 'invertOversizedImages',
        getter: () async => debugInvertOversizedImages,
        setter: (bool value) async {
          if (debugInvertOversizedImages != value) {
            debugInvertOversizedImages = value;
            return _forceRepaint();
          }
          return Future<void>.value();
        },
      );
72
      registerBoolServiceExtension(
73
        name: 'debugPaint',
74
        getter: () async => debugPaintSizeEnabled,
75 76
        setter: (bool value) {
          if (debugPaintSizeEnabled == value)
77
            return Future<void>.value();
78
          debugPaintSizeEnabled = value;
79
          return _forceRepaint();
80
        },
81
      );
82
      registerBoolServiceExtension(
83 84 85
        name: 'debugPaintBaselinesEnabled',
        getter: () async => debugPaintBaselinesEnabled,
        setter: (bool value) {
86
          if (debugPaintBaselinesEnabled == value)
87
            return Future<void>.value();
88 89
          debugPaintBaselinesEnabled = value;
          return _forceRepaint();
90
        },
91 92
      );
      registerBoolServiceExtension(
93 94 95 96 97 98 99
        name: 'repaintRainbow',
        getter: () async => debugRepaintRainbowEnabled,
        setter: (bool value) {
          final bool repaint = debugRepaintRainbowEnabled && !value;
          debugRepaintRainbowEnabled = value;
          if (repaint)
            return _forceRepaint();
100
          return Future<void>.value();
101 102
        },
      );
103 104 105 106 107 108 109 110 111
      registerBoolServiceExtension(
        name: 'debugCheckElevationsEnabled',
        getter: () async => debugCheckElevationsEnabled,
        setter: (bool value) {
          if (debugCheckElevationsEnabled == value) {
            return Future<void>.value();
          }
          debugCheckElevationsEnabled = value;
          return _forceRepaint();
112
        },
113
      );
114
      registerServiceExtension(
115
        name: 'debugDumpLayerTree',
116 117 118 119 120
        callback: (Map<String, String> parameters) async {
          final String data = RendererBinding.instance?.renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable.';
          return <String, Object>{
            'data': data,
          };
121 122
        },
      );
123
      return true;
124
    }());
125

126
    if (!kReleaseMode) {
127
      // these service extensions work in debug or profile mode
128
      registerServiceExtension(
129
        name: 'debugDumpRenderTree',
130 131 132 133 134
        callback: (Map<String, String> parameters) async {
          final String data = RendererBinding.instance?.renderView.toStringDeep() ?? 'Render tree unavailable.';
          return <String, Object>{
            'data': data,
          };
135
        },
136
      );
137

138
      registerServiceExtension(
139
        name: 'debugDumpSemanticsTreeInTraversalOrder',
140 141 142 143 144 145
        callback: (Map<String, String> parameters) async {
          final String data = RendererBinding.instance?.renderView.debugSemantics
            ?.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversalOrder) ?? 'Semantics not collected.';
          return <String, Object>{
            'data': data,
          };
146 147
        },
      );
148

149
      registerServiceExtension(
150
        name: 'debugDumpSemanticsTreeInInverseHitTestOrder',
151 152 153 154 155 156
        callback: (Map<String, String> parameters) async {
          final String data = RendererBinding.instance?.renderView.debugSemantics
            ?.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest) ?? 'Semantics not collected.';
          return <String, Object>{
            'data': data,
          };
157 158 159
        },
      );
    }
160
  }
161

162 163
  /// Creates a [RenderView] object to be the root of the
  /// [RenderObject] rendering tree, and initializes it so that it
164
  /// will be rendered when the next frame is requested.
165 166
  ///
  /// Called automatically when the binding is created.
Ian Hickson's avatar
Ian Hickson committed
167
  void initRenderView() {
168 169 170 171 172
    assert(!_debugIsRenderViewInitialized);
    assert(() {
      _debugIsRenderViewInitialized = true;
      return true;
    }());
173
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
174
    renderView.prepareInitialFrame();
175
  }
176
  bool _debugIsRenderViewInitialized = false;
177

178 179
  /// The object that manages state about currently connected mice, for hover
  /// notification.
180 181
  MouseTracker get mouseTracker => _mouseTracker!;
  MouseTracker? _mouseTracker;
182

183
  /// The render tree's owner, which maintains dirty state for layout,
184
  /// composite, paint, and accessibility semantics.
185
  PipelineOwner get pipelineOwner => _pipelineOwner;
186
  late PipelineOwner _pipelineOwner;
187

Florian Loitsch's avatar
Florian Loitsch committed
188
  /// The render tree that's attached to the output surface.
189
  RenderView get renderView => _pipelineOwner.rootNode! as RenderView;
190 191
  /// Sets the given [RenderView] object (which must not be null), and its tree, to
  /// be the new render tree to display. The previous tree, if any, is detached.
192
  set renderView(RenderView value) {
193
    assert(value != null);
194
    _pipelineOwner.rootNode = value;
195 196
  }

197
  /// Called when the system metrics change.
198
  ///
199
  /// See [dart:ui.PlatformDispatcher.onMetricsChanged].
200
  @protected
Ian Hickson's avatar
Ian Hickson committed
201
  void handleMetricsChanged() {
202
    assert(renderView != null);
203
    renderView.configuration = createViewConfiguration();
204
    scheduleForcedFrame();
205 206
  }

207 208
  /// Called when the platform text scale factor changes.
  ///
209
  /// See [dart:ui.PlatformDispatcher.onTextScaleFactorChanged].
210
  @protected
211 212
  void handleTextScaleFactorChanged() { }

213 214
  /// Called when the platform brightness changes.
  ///
215 216 217 218
  /// 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.
219
  ///
220
  /// {@tool snippet}
221
  /// Querying [MediaQuery] directly. Preferred.
222 223
  ///
  /// ```dart
224
  /// final Brightness brightness = MediaQuery.platformBrightnessOf(context);
225
  /// ```
226
  /// {@end-tool}
227
  ///
228
  /// {@tool snippet}
229
  /// Querying [PlatformDispatcher.platformBrightness].
230 231
  ///
  /// ```dart
232
  /// final Brightness brightness = WidgetsBinding.instance!.platformDispatcher.platformBrightness;
233
  /// ```
234
  /// {@end-tool}
235
  ///
236
  /// {@tool snippet}
237
  /// Querying [MediaQueryData].
238 239 240 241 242
  ///
  /// ```dart
  /// final MediaQueryData mediaQueryData = MediaQuery.of(context);
  /// final Brightness brightness = mediaQueryData.platformBrightness;
  /// ```
243
  /// {@end-tool}
244
  ///
245
  /// See [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged].
246 247 248
  @protected
  void handlePlatformBrightnessChanged() { }

249 250 251 252 253 254 255 256 257 258 259
  /// Returns a [ViewConfiguration] configured for the [RenderView] based on the
  /// current environment.
  ///
  /// This is called during construction and also in response to changes to the
  /// system metrics.
  ///
  /// 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`.
  ViewConfiguration createViewConfiguration() {
260
    final double devicePixelRatio = window.devicePixelRatio;
261
    return ViewConfiguration(
262
      size: window.physicalSize / devicePixelRatio,
263
      devicePixelRatio: devicePixelRatio,
264
    );
265 266
  }

267
  SemanticsHandle? _semanticsHandle;
268

269 270 271 272 273
  /// 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
274
  void initMouseTracker([MouseTracker? tracker]) {
275
    _mouseTracker?.dispose();
276 277 278 279
    _mouseTracker = tracker ?? MouseTracker();
  }

  @override // from GestureBinding
280
  void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
281 282 283
    if (hitTestResult != null ||
        event is PointerAddedEvent ||
        event is PointerRemovedEvent) {
284
      assert(event.position != null);
285
      _mouseTracker!.updateWithEvent(event, () => hitTestResult ?? renderView.hitTestMouseTrackers(event.position));
286 287
    }
    super.dispatchEvent(event, hitTestResult);
288 289
  }

290
  void _handleSemanticsEnabledChanged() {
291
    setSemanticsEnabled(window.semanticsEnabled);
292 293
  }

294 295
  /// Whether the render tree associated with this binding should produce a tree
  /// of [SemanticsNode] objects.
296 297
  void setSemanticsEnabled(bool enabled) {
    if (enabled) {
298 299 300 301 302 303 304
      _semanticsHandle ??= _pipelineOwner.ensureSemantics();
    } else {
      _semanticsHandle?.dispose();
      _semanticsHandle = null;
    }
  }

305 306 307 308 309 310
  void _handleWebFirstFrame(Duration _) {
    assert(kIsWeb);
    const MethodChannel methodChannel = MethodChannel('flutter/service_worker');
    methodChannel.invokeMethod<void>('first-frame');
  }

311
  void _handleSemanticsAction(int id, SemanticsAction action, ByteData? args) {
312 313 314 315 316
    _pipelineOwner.semanticsOwner?.performAction(
      id,
      action,
      args != null ? const StandardMessageCodec().decodeMessage(args) : null,
    );
317 318 319 320 321 322 323 324
  }

  void _handleSemanticsOwnerCreated() {
    renderView.scheduleInitialSemantics();
  }

  void _handleSemanticsOwnerDisposed() {
    renderView.clearSemantics();
Hixie's avatar
Hixie committed
325 326
  }

327
  void _handlePersistentFrameCallback(Duration timeStamp) {
328
    drawFrame();
329 330 331 332 333 334 335 336 337 338
    _scheduleMouseTrackerUpdate();
  }

  bool _debugMouseTrackerUpdateScheduled = false;
  void _scheduleMouseTrackerUpdate() {
    assert(!_debugMouseTrackerUpdateScheduled);
    assert(() {
      _debugMouseTrackerUpdateScheduled = true;
      return true;
    }());
339
    SchedulerBinding.instance!.addPostFrameCallback((Duration duration) {
340 341 342 343 344
      assert(_debugMouseTrackerUpdateScheduled);
      assert(() {
        _debugMouseTrackerUpdateScheduled = false;
        return true;
      }());
345
      _mouseTracker!.updateAllDevices(renderView.hitTestMouseTrackers);
346
    });
347 348
  }

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
  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.
    if (!_firstFrameSent)
      scheduleWarmUpFrame();
  }

  /// 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
405
  /// Pump the rendering pipeline to generate a frame.
406
  ///
407
  /// This method is called by [handleDrawFrame], which itself is called
408
  /// automatically by the engine when it is time to lay out and paint a frame.
409 410 411 412
  ///
  /// Each frame consists of the following phases:
  ///
  /// 1. The animation phase: The [handleBeginFrame] method, which is registered
413 414 415 416 417
  /// 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.
418
  ///
419 420 421 422
  /// 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.
423
  ///
424
  /// After [handleBeginFrame], [handleDrawFrame], which is registered with
425 426 427
  /// [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:
428 429
  ///
  /// 3. The layout phase: All the dirty [RenderObject]s in the system are laid
430 431 432
  /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout]
  /// for further details on marking an object dirty for layout.
  ///
433
  /// 4. The compositing bits phase: The compositing bits on any dirty
434 435 436
  /// [RenderObject] objects are updated. See
  /// [RenderObject.markNeedsCompositingBitsUpdate].
  ///
437
  /// 5. The paint phase: All the dirty [RenderObject]s in the system are
438 439 440 441
  /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See
  /// [RenderObject.markNeedsPaint] for further details on marking an object
  /// dirty for paint.
  ///
442
  /// 6. The compositing phase: The layer tree is turned into a [Scene] and
443 444
  /// sent to the GPU.
  ///
445
  /// 7. The semantics phase: All the dirty [RenderObject]s in the system have
446
  /// their semantics updated. This generates the [SemanticsNode] tree. See
447 448 449
  /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an
  /// object dirty for semantics.
  ///
450
  /// For more details on steps 3-7, see [PipelineOwner].
451
  ///
452 453
  /// 8. The finalization phase: After [drawFrame] returns, [handleDrawFrame]
  /// then invokes post-frame callbacks (registered with [addPostFrameCallback]).
454 455
  ///
  /// Some bindings (for example, the [WidgetsBinding]) add extra steps to this
456
  /// list (for example, see [WidgetsBinding.drawFrame]).
457 458
  //
  // When editing the above, also update widgets/binding.dart's copy.
Ian Hickson's avatar
Ian Hickson committed
459
  @protected
460
  void drawFrame() {
461
    assert(renderView != null);
462 463 464
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    pipelineOwner.flushPaint();
465 466 467 468 469
    if (sendFramesToEngine) {
      renderView.compositeFrame(); // this sends the bits to the GPU
      pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
      _firstFrameSent = true;
    }
470 471
  }

472
  @override
473
  Future<void> performReassemble() async {
474
    await super.performReassemble();
475
    Timeline.startSync('Dirty Render Tree', arguments: timelineArgumentsIndicatingLandmarkEvent);
476 477 478 479 480
    try {
      renderView.reassemble();
    } finally {
      Timeline.finishSync();
    }
481
    scheduleWarmUpFrame();
482
    await endOfFrame;
483 484
  }

485
  @override
486
  void hitTest(HitTestResult result, Offset position) {
487
    assert(renderView != null);
488 489
    assert(result != null);
    assert(position != null);
490
    renderView.hitTest(result, position: position);
491
    super.hitTest(result, position);
492
  }
493

494
  Future<void> _forceRepaint() {
495
    late RenderObjectVisitor visitor;
496 497 498 499
    visitor = (RenderObject child) {
      child.markNeedsPaint();
      child.visitChildren(visitor);
    };
500
    instance?.renderView.visitChildren(visitor);
501
    return endOfFrame;
502
  }
503
}
504

Florian Loitsch's avatar
Florian Loitsch committed
505
/// Prints a textual representation of the entire render tree.
506
void debugDumpRenderTree() {
507
  debugPrint(RendererBinding.instance?.renderView.toStringDeep() ?? 'Render tree unavailable.');
508
}
509

Florian Loitsch's avatar
Florian Loitsch committed
510
/// Prints a textual representation of the entire layer tree.
511
void debugDumpLayerTree() {
512
  debugPrint(RendererBinding.instance?.renderView.debugLayer?.toStringDeep() ?? 'Layer tree unavailable.');
Ian Hickson's avatar
Ian Hickson committed
513 514
}

Hixie's avatar
Hixie committed
515 516
/// Prints a textual representation of the entire semantics tree.
/// This will only work if there is a semantics client attached.
517 518 519 520 521
/// Otherwise, a notice that no semantics are available will be printed.
///
/// The order in which the children of a [SemanticsNode] will be printed is
/// controlled by the [childOrder] parameter.
void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) {
522
  debugPrint(RendererBinding.instance?.renderView.debugSemantics?.toStringDeep(childOrder: childOrder) ?? 'Semantics not collected.');
Hixie's avatar
Hixie committed
523 524
}

Ian Hickson's avatar
Ian Hickson committed
525 526
/// A concrete binding for applications that use the Rendering framework
/// directly. This is the glue that binds the framework to the Flutter engine.
527 528 529 530 531
///
/// 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
/// that layer's binding.
532
class RenderingFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, SemanticsBinding, PaintingBinding, RendererBinding {
533 534 535 536
  /// Creates a binding for the rendering layer.
  ///
  /// The `root` render box is attached directly to the [renderView] and is
  /// given constraints that require it to fill the window.
537 538 539
  ///
  /// This binding does not automatically schedule any frames. Callers are
  /// responsible for deciding when to first call [scheduleFrame].
540
  RenderingFlutterBinding({ RenderBox? root }) {
541
    assert(renderView != null);
Ian Hickson's avatar
Ian Hickson committed
542 543
    renderView.child = root;
  }
544
}