binding.dart 22.9 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:async';
6
import 'dart:developer' as developer;
7
import 'dart:ui' show AppLifecycleState, Locale;
8
import 'dart:ui' as ui show window;
9

10
import 'package:flutter/foundation.dart';
Ian Hickson's avatar
Ian Hickson committed
11
import 'package:flutter/gestures.dart';
12
import 'package:flutter/rendering.dart';
13
import 'package:flutter/scheduler.dart';
Ian Hickson's avatar
Ian Hickson committed
14
import 'package:flutter/services.dart';
15

16
import 'app.dart';
17
import 'focus_manager.dart';
18
import 'framework.dart';
19

20 21
export 'dart:ui' show AppLifecycleState, Locale;

22 23
/// Interface for classes that register with the Widgets layer binding.
///
24
/// See [WidgetsBinding.addObserver] and [WidgetsBinding.removeObserver].
25 26 27 28 29 30 31 32 33 34 35 36 37
abstract class WidgetsBindingObserver {
  /// Called when the system tells the app to pop the current route.
  /// For example, on Android, this is called when the user presses
  /// the back button.
  ///
  /// Observers are notified in registration order until one returns
  /// true. If none return true, the application quits.
  ///
  /// Observers are expected to return true if they were able to
  /// handle the notification, for example by closing an active dialog
  /// box, and false otherwise. The [WidgetsApp] widget uses this
  /// mechanism to notify the [Navigator] widget that it should pop
  /// its current route if possible.
38
  Future<bool> didPopRoute() => new Future<bool>.value(false);
39 40 41

  /// Called when the application's dimensions change. For example,
  /// when a phone is rotated.
42
  void didChangeMetrics() { }
43 44 45 46

  /// Called when the system tells the app that the user's locale has
  /// changed. For example, if the user changes the system language
  /// settings.
47
  void didChangeLocale(Locale locale) { }
48 49 50

  /// Called when the system puts the app in the background or returns
  /// the app to the foreground.
51
  void didChangeAppLifecycleState(AppLifecycleState state) { }
52 53 54

  /// Called when the system is running low on memory.
  void didHaveMemoryPressure() { }
Ian Hickson's avatar
Ian Hickson committed
55
}
56

57
/// The glue between the widgets layer and the Flutter engine.
58
abstract class WidgetsBinding extends BindingBase implements GestureBinding, RendererBinding {
59
  @override
60
  void initInstances() {
Ian Hickson's avatar
Ian Hickson committed
61 62
    super.initInstances();
    _instance = this;
63
    buildOwner.onBuildScheduled = _handleBuildScheduled;
Ian Hickson's avatar
Ian Hickson committed
64
    ui.window.onLocaleChanged = handleLocaleChanged;
65 66 67
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
    SystemChannels.system.setMessageHandler(_handleSystemMessage);
68 69
  }

70
  /// The current [WidgetsBinding], if one has been created.
71 72 73
  ///
  /// If you need the binding to be constructed before calling [runApp],
  /// you can ensure a Widget binding has been constructed by calling the
74 75 76
  /// `WidgetsFlutterBinding.ensureInitialized()` function.
  static WidgetsBinding get instance => _instance;
  static WidgetsBinding _instance;
77

78 79 80 81
  @override
  void initServiceExtensions() {
    super.initServiceExtensions();

82 83
    registerSignalServiceExtension(
      name: 'debugDumpApp',
84
      callback: () { debugDumpApp(); return debugPrintDone; }
85 86
    );

87 88
    registerBoolServiceExtension(
      name: 'showPerformanceOverlay',
89
      getter: () => new Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
90 91
      setter: (bool value) {
        if (WidgetsApp.showPerformanceOverlayOverride == value)
92
          return new Future<Null>.value();
93
        WidgetsApp.showPerformanceOverlayOverride = value;
94
        return _forceRebuild();
95 96
      }
    );
97 98 99

    registerBoolServiceExtension(
      name: 'debugAllowBanner',
100
      getter: () => new Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
101 102
      setter: (bool value) {
        if (WidgetsApp.debugAllowBannerOverride == value)
103
          return new Future<Null>.value();
104
        WidgetsApp.debugAllowBannerOverride = value;
105
        return _forceRebuild();
106 107
      }
    );
108 109
  }

110 111 112 113 114 115 116 117
  Future<Null> _forceRebuild() {
    if (renderViewElement != null) {
      buildOwner.reassemble(renderViewElement);
      return endOfFrame;
    }
    return new Future<Null>.value();
  }

118 119 120 121
  /// The [BuildOwner] in charge of executing the build pipeline for the
  /// widget tree rooted at this binding.
  BuildOwner get buildOwner => _buildOwner;
  final BuildOwner _buildOwner = new BuildOwner();
Ian Hickson's avatar
Ian Hickson committed
122

123 124 125 126 127 128 129 130
  /// The object in charge of the focus tree.
  /// 
  /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
  /// the [FocusScopeNode] for a given [BuildContext].
  /// 
  /// See [FocusManager] for more details.
  final FocusManager focusManager = new FocusManager();

131
  final List<WidgetsBindingObserver> _observers = <WidgetsBindingObserver>[];
Ian Hickson's avatar
Ian Hickson committed
132

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
  /// Registers the given object as a binding observer. Binding
  /// observers are notified when various application events occur,
  /// for example when the system locale changes. Generally, one
  /// widget in the widget tree registers itself as a binding
  /// observer, and converts the system state into inherited widgets.
  ///
  /// For example, the [WidgetsApp] widget registers as a binding
  /// observer and passes the screen size to a [MediaQuery] widget
  /// each time it is built, which enables other widgets to use the
  /// [MediaQuery.of] static method and (implicitly) the
  /// [InheritedWidget] mechanism to be notified whenever the screen
  /// size changes (e.g. whenever the screen rotates).
  void addObserver(WidgetsBindingObserver observer) => _observers.add(observer);

  /// Unregisters the given observer. This should be used sparingly as
  /// it is relatively expensive (O(N) in the number of registered
  /// observers).
  bool removeObserver(WidgetsBindingObserver observer) => _observers.remove(observer);

152
  /// Called when the system metrics change.
153 154 155 156 157
  ///
  /// Notifies all the observers using
  /// [WidgetsBindingObserver.didChangeMetrics].
  ///
  /// See [ui.window.onMetricsChanged].
158
  @override
Ian Hickson's avatar
Ian Hickson committed
159 160
  void handleMetricsChanged() {
    super.handleMetricsChanged();
161
    for (WidgetsBindingObserver observer in _observers)
162
      observer.didChangeMetrics();
Ian Hickson's avatar
Ian Hickson committed
163 164
  }

165
  /// Called when the system locale changes.
166 167 168 169
  ///
  /// Calls [dispatchLocaleChanged] to notify the binding observers.
  ///
  /// See [ui.window.onLocaleChanged].
Ian Hickson's avatar
Ian Hickson committed
170 171 172 173
  void handleLocaleChanged() {
    dispatchLocaleChanged(ui.window.locale);
  }

174 175 176
  /// Notify all the observers that the locale has changed (using
  /// [WidgetsBindingObserver.didChangeLocale]), giving them the
  /// `locale` argument.
177
  void dispatchLocaleChanged(Locale locale) {
178
    for (WidgetsBindingObserver observer in _observers)
Ian Hickson's avatar
Ian Hickson committed
179 180 181
      observer.didChangeLocale(locale);
  }

182
  /// Called when the system pops the current route.
183 184 185 186 187 188 189 190 191 192
  ///
  /// This first notifies the binding observers (using
  /// [WidgetsBindingObserver.didPopRoute]), in registration order,
  /// until one returns true, meaning that it was able to handle the
  /// request (e.g. by closing a dialog box). If none return true,
  /// then the application is shut down.
  ///
  /// [WidgetsApp] uses this in conjunction with a [Navigator] to
  /// cause the back button to close dialog boxes, return from modal
  /// pages, and so forth.
193 194 195
  Future<Null> handlePopRoute() async {
    for (WidgetsBindingObserver observer in  new List<WidgetsBindingObserver>.from(_observers)) {
      if (await observer.didPopRoute())
196
        return;
Ian Hickson's avatar
Ian Hickson committed
197
    }
198
    SystemNavigator.pop();
Ian Hickson's avatar
Ian Hickson committed
199
  }
Hixie's avatar
Hixie committed
200

201 202
  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) async {
    if (methodCall.method == 'popRoute')
203 204 205 206
      handlePopRoute();
    // TODO(abarth): Handle 'pushRoute'.
  }

207
  /// Called when the application lifecycle state changes.
208 209 210
  ///
  /// Notifies all the observers using
  /// [WidgetsBindingObserver.didChangeAppLifecycleState].
211
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
212
    for (WidgetsBindingObserver observer in _observers)
213 214 215
      observer.didChangeAppLifecycleState(state);
  }

216 217 218 219 220 221 222 223 224 225 226 227
  Future<String> _handleLifecycleMessage(String message) async {
    switch (message) {
      case 'AppLifecycleState.paused':
        handleAppLifecycleStateChanged(AppLifecycleState.paused);
        break;
      case 'AppLifecycleState.resumed':
        handleAppLifecycleStateChanged(AppLifecycleState.resumed);
        break;
    }
    return null;
  }

228 229 230 231 232 233 234 235 236
  Future<dynamic> _handleSystemMessage(Map<String, dynamic> message) async {
    final String type = message['type'];
    if (type == 'memoryPressure') {
      for (WidgetsBindingObserver observer in _observers)
        observer.didHaveMemoryPressure();
    }
    return null;
  }

237 238
  bool _needToReportFirstFrame = true;
  bool _thisFrameWasUseful = true;
239

240 241
  /// Tell the framework that the frame we are currently building
  /// should not be considered to be a useful first frame.
242 243
  ///
  /// This is used by [WidgetsApp] to report the first frame.
244 245 246 247
  //
  // TODO(ianh): This method should only be available in debug and profile modes.
  void preventThisFrameFromBeingReportedAsFirstFrame() {
    _thisFrameWasUseful = false;
248 249
  }

250
  void _handleBuildScheduled() {
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    // If we're in the process of building dirty elements, then changes
    // should not trigger a new frame.
    assert(() {
      if (debugBuildingDirtyElements) {
        throw new FlutterError(
          'Build scheduled during frame.\n'
          'While the widget tree was being built, laid out, and painted, '
          'a new frame was scheduled to rebuild the widget tree. '
          'This might be because setState() was called from a layout or '
          'paint callback. '
          'If a change is needed to the widget tree, it should be applied '
          'as the tree is being built. Scheduling a change for the subsequent '
          'frame instead results in an interface that lags behind by one frame. '
          'If this was done to make your build dependent on a size measured at '
          'layout time, consider using a LayoutBuilder, CustomSingleChildLayout, '
          'or CustomMultiChildLayout. If, on the other hand, the one frame delay '
          'is the desired effect, for example because this is an '
          'animation, consider scheduling the frame in a post-frame callback '
          'using SchedulerBinding.addPostFrameCallback or '
          'using an AnimationController to trigger the animation.'
        );
      }
      return true;
    });
275 276 277
    scheduleFrame();
  }

278 279 280 281 282 283 284 285
  /// Whether we are currently in a frame. This is used to verify
  /// that frames are not scheduled redundantly.
  ///
  /// This is public so that test frameworks can change it.
  ///
  /// This flag is not used in release builds.
  @protected
  bool debugBuildingDirtyElements = false;
286

287 288 289 290 291 292 293 294 295 296
  /// Pump the build and rendering pipeline to generate a frame.
  ///
  /// This method is called by [handleBeginFrame], which itself is called
  /// automatically by the engine when when it is time to lay out and paint a
  /// frame.
  ///
  /// Each frame consists of the following phases:
  ///
  /// 1. The animation phase: The [handleBeginFrame] method, which is registered
  /// with [ui.window.onBeginFrame], invokes all the transient frame callbacks
297
  /// registered with [scheduleFrameCallback], in
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
  /// 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.
  ///
  /// [handleBeginFrame] then invokes all the persistent frame callbacks, of which
  /// the most notable is this method, [beginFrame], which proceeds as follows:
  ///
  /// 2. The build phase: All the dirty [Element]s in the widget tree are
  /// rebuilt (see [State.build]). See [State.setState] for further details on
  /// marking a widget dirty for building. See [BuildOwner] for more information
  /// on this step.
  ///
  /// 3. The layout phase: All the dirty [RenderObject]s in the system are laid
  /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout]
  /// for further details on marking an object dirty for layout.
  ///
  /// 4. The compositing bits phase: The compositing bits on any dirty
  /// [RenderObject] objects are updated. See
  /// [RenderObject.markNeedsCompositingBitsUpdate].
  ///
  /// 5. The paint phase: All the dirty [RenderObject]s in the system are
  /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See
  /// [RenderObject.markNeedsPaint] for further details on marking an object
  /// dirty for paint.
  ///
  /// 6. The compositing phase: The layer tree is turned into a [ui.Scene] and
  /// sent to the GPU.
  ///
  /// 7. The semantics phase: All the dirty [RenderObject]s in the system have
327
  /// their semantics updated (see [RenderObject.SemanticsAnnotator]). This
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
  /// generates the [SemanticsNode] tree. See
  /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an
  /// object dirty for semantics.
  ///
  /// For more details on steps 3-7, see [PipelineOwner].
  ///
  /// 8. The finalization phase in the widgets layer: The widgets tree is
  /// finalized. This causes [State.dispose] to be invoked on any objects that
  /// were removed from the widgets tree this frame. See
  /// [BuildOwner.finalizeTree] for more details.
  ///
  /// 9. The finalization phase in the scheduler layer: After [beginFrame]
  /// returns, [handleBeginFrame] then invokes post-frame callbacks (registered
  /// with [addPostFrameCallback].
  //
  // When editing the above, also update rendering/binding.dart's copy.
344
  @override
345
  void beginFrame() {
346 347 348 349 350 351
    assert(!debugBuildingDirtyElements);
    assert(() {
      debugBuildingDirtyElements = true;
      return true;
    });
    try {
352 353
      if (renderViewElement != null)
        buildOwner.buildScope(renderViewElement);
354 355 356 357 358 359 360 361
      super.beginFrame();
      buildOwner.finalizeTree();
    } finally {
      assert(() {
        debugBuildingDirtyElements = false;
        return true;
      });
    }
362
    // TODO(ianh): Following code should not be included in release mode, only profile and debug modes.
363
    // See https://github.com/dart-lang/sdk/issues/27192
364
    if (_needToReportFirstFrame) {
365
      if (_thisFrameWasUseful) {
366
        developer.Timeline.instantSync('Widgets completed first useful frame');
367
        developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
368
        _needToReportFirstFrame = false;
369 370
      } else {
        _thisFrameWasUseful = true;
371
      }
372
    }
373
  }
374 375 376

  /// The [Element] that is at the root of the hierarchy (and which wraps the
  /// [RenderView] object at the root of the rendering hierarchy).
377 378
  ///
  /// This is initialized the first time [runApp] is called.
379 380
  Element get renderViewElement => _renderViewElement;
  Element _renderViewElement;
381 382 383 384 385 386 387 388

  /// Takes a widget and attaches it to the [renderViewElement], creating it if
  /// necessary.
  ///
  /// This is called by [runApp] to configure the widget tree.
  ///
  /// See also [RenderObjectToWidgetAdapter.attachToRenderTree].
  void attachRootWidget(Widget rootWidget) {
389 390
    _renderViewElement = new RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
391
      debugShortDescription: '[root]',
392
      child: rootWidget
393
    ).attachToRenderTree(buildOwner, renderViewElement);
394
  }
395 396

  @override
397
  Future<Null> reassembleApplication() {
398 399
    _needToReportFirstFrame = true;
    preventThisFrameFromBeingReportedAsFirstFrame();
400 401
    if (renderViewElement != null)
      buildOwner.reassemble(renderViewElement);
402
    return super.reassembleApplication();
403
  }
404
}
Hixie's avatar
Hixie committed
405

406
/// Inflate the given widget and attach it to the screen.
407
///
408 409 410 411 412
/// The widget is given constraints during layout that force it to fill the
/// entire screen. If you wish to align your widget to one side of the screen
/// (e.g., the top), consider using the [Align] widget. If you wish to center
/// your widget, you can also use the [Center] widget
///
413
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
414 415 416 417 418 419 420 421 422
///
/// See also:
///
/// * [WidgetsBinding.attachRootWidget], which creates the root widget for the
///   widget hierarchy.
/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root
///   element for the element hierarchy.
/// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to
///   ensure the widget, element, and render trees are all built.
Hixie's avatar
Hixie committed
423
void runApp(Widget app) {
424 425 426
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..handleBeginFrame(null);
Hixie's avatar
Hixie committed
427 428
}

429
/// Print a string representation of the currently running app.
430
void debugDumpApp() {
431
  assert(WidgetsBinding.instance != null);
Hixie's avatar
Hixie committed
432 433
  String mode = 'RELEASE MODE';
  assert(() { mode = 'CHECKED MODE'; return true; });
434
  debugPrint('${WidgetsBinding.instance.runtimeType} - $mode');
435 436 437 438 439
  if (WidgetsBinding.instance.renderViewElement != null) {
    debugPrint(WidgetsBinding.instance.renderViewElement.toStringDeep());
  } else {
    debugPrint('<no tree currently mounted>');
  }
440 441
}

442 443 444 445 446 447 448 449
/// A bridge from a [RenderObject] to an [Element] tree.
///
/// The given container is the [RenderObject] that the [Element] tree should be
/// inserted into. It must be a [RenderObject] that implements the
/// [RenderObjectWithChildMixin] protocol. The type argument `T` is the kind of
/// [RenderObject] that the container expects as its child.
///
/// Used by [runApp] to bootstrap applications.
Hixie's avatar
Hixie committed
450
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
451 452 453
  /// Creates a bridge from a [RenderObject] to an [Element] tree.
  ///
  /// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
454 455
  RenderObjectToWidgetAdapter({
    this.child,
456
    this.container,
457
    this.debugShortDescription
458
  }) : super(key: new GlobalObjectKey(container));
Hixie's avatar
Hixie committed
459

460
  /// The widget below this widget in the tree.
Hixie's avatar
Hixie committed
461
  final Widget child;
462

463
  /// The [RenderObject] that is the parent of the [Element] created by this widget.
Hixie's avatar
Hixie committed
464
  final RenderObjectWithChildMixin<T> container;
465

466
  /// A short description of this widget used by debugging aids.
467
  final String debugShortDescription;
Hixie's avatar
Hixie committed
468

469
  @override
Hixie's avatar
Hixie committed
470 471
  RenderObjectToWidgetElement<T> createElement() => new RenderObjectToWidgetElement<T>(this);

472
  @override
473
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
Hixie's avatar
Hixie committed
474

475
  @override
476
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }
477

478 479
  /// Inflate this widget and actually set the resulting [RenderObject] as the
  /// child of [container].
480 481
  ///
  /// If `element` is null, this function will create a new element. Otherwise,
482
  /// the given element will have an update scheduled to switch to this widget.
483 484
  ///
  /// Used by [runApp] to bootstrap applications.
485
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
486 487
    if (element == null) {
      owner.lockState(() {
488
        element = createElement();
489
        assert(element != null);
490
        element.assignOwner(owner);
491 492
      });
      owner.buildScope(element, () {
493
        element.mount(null, null);
494 495
      });
    } else {
496 497
      element._newWidget = this;
      element.markNeedsBuild();
498
    }
499 500
    return element;
  }
501

502
  @override
503
  String toStringShort() => debugShortDescription ?? super.toStringShort();
Hixie's avatar
Hixie committed
504 505
}

506 507 508 509 510
/// A [RootRenderObjectElement] that is hosted by a [RenderObject].
///
/// This element class is the instantiation of a [RenderObjectToWidgetAdapter]
/// widget. It can be used only as the root of an [Element] tree (it cannot be
/// mounted into another [Element]; it's parent must be null).
Hixie's avatar
Hixie committed
511
///
512 513
/// In typical usage, it will be instantiated for a [RenderObjectToWidgetAdapter]
/// whose container is the [RenderView] that connects to the Flutter engine. In
Hixie's avatar
Hixie committed
514
/// this usage, it is normally instantiated by the bootstrapping logic in the
515
/// [WidgetsFlutterBinding] singleton created by [runApp].
516
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
517 518 519 520 521
  /// Creates an element that is hosted by a [RenderObject].
  ///
  /// The [RenderObject] created by this element is not automatically set as a
  /// child of the hosting [RenderObject]. To actually attach this element to
  /// the render tree, call [RenderObjectToWidgetAdapter.attachToRenderTree].
Hixie's avatar
Hixie committed
522 523
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);

524
  @override
525 526
  RenderObjectToWidgetAdapter<T> get widget => super.widget;

Hixie's avatar
Hixie committed
527 528
  Element _child;

529
  static const Object _rootChildSlot = const Object();
Hixie's avatar
Hixie committed
530

531
  @override
Hixie's avatar
Hixie committed
532 533 534 535 536
  void visitChildren(ElementVisitor visitor) {
    if (_child != null)
      visitor(_child);
  }

537
  @override
538
  void forgetChild(Element child) {
539 540 541 542
    assert(child == _child);
    _child = null;
  }

543
  @override
Hixie's avatar
Hixie committed
544
  void mount(Element parent, dynamic newSlot) {
Hixie's avatar
Hixie committed
545
    assert(parent == null);
Hixie's avatar
Hixie committed
546
    super.mount(parent, newSlot);
547
    _rebuild();
Hixie's avatar
Hixie committed
548 549
  }

550
  @override
Hixie's avatar
Hixie committed
551 552 553
  void update(RenderObjectToWidgetAdapter<T> newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
554 555 556
    _rebuild();
  }

557 558 559 560 561 562
  // When we are assigned a new widget, we store it here
  // until we are ready to update to it.
  Widget _newWidget;

  @override
  void performRebuild() {
Ian Hickson's avatar
Ian Hickson committed
563 564 565 566 567 568 569
    if (_newWidget != null) {
      // _newWidget can be null if, for instance, we were rebuilt
      // due to a reassemble.
      final Widget newWidget = _newWidget;
      _newWidget = null;
      update(newWidget);
    }
570 571 572 573
    super.performRebuild();
    assert(_newWidget == null);
  }

574 575 576 577 578 579 580 581 582 583 584
  void _rebuild() {
    try {
      _child = updateChild(_child, widget.child, _rootChildSlot);
      assert(_child != null);
    } catch (exception, stack) {
      FlutterError.reportError(new FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'widgets library',
        context: 'attaching to the render tree'
      ));
585
      final Widget error = new ErrorWidget(exception);
586 587
      _child = updateChild(null, error, _rootChildSlot);
    }
Hixie's avatar
Hixie committed
588 589
  }

590
  @override
Hixie's avatar
Hixie committed
591 592
  RenderObjectWithChildMixin<T> get renderObject => super.renderObject;

593
  @override
Hixie's avatar
Hixie committed
594
  void insertChildRenderObject(RenderObject child, dynamic slot) {
Ian Hickson's avatar
Ian Hickson committed
595
    assert(slot == _rootChildSlot);
Hixie's avatar
Hixie committed
596 597 598
    renderObject.child = child;
  }

599
  @override
Adam Barth's avatar
Adam Barth committed
600 601 602 603
  void moveChildRenderObject(RenderObject child, dynamic slot) {
    assert(false);
  }

604
  @override
Hixie's avatar
Hixie committed
605 606 607 608
  void removeChildRenderObject(RenderObject child) {
    assert(renderObject.child == child);
    renderObject.child = null;
  }
Adam Barth's avatar
Adam Barth committed
609
}
610 611 612

/// A concrete binding for applications based on the Widgets framework.
/// This is the glue that binds the framework to the Flutter engine.
613
class WidgetsFlutterBinding extends BindingBase with SchedulerBinding, GestureBinding, ServicesBinding, RendererBinding, WidgetsBinding {
614 615 616 617 618

  /// Returns an instance of the [WidgetsBinding], creating and
  /// initializing it if necessary. If one is created, it will be a
  /// [WidgetsFlutterBinding]. If one was previously initialized, then
  /// it will at least implement [WidgetsBinding].
619 620 621
  ///
  /// You only need to call this method if you need the binding to be
  /// initialized before calling [runApp].
622 623 624 625 626
  ///
  /// In the `flutter_test` framework, [testWidgets] initializes the
  /// binding instance to a [TestWidgetsFlutterBinding], not a
  /// [WidgetsFlutterBinding].
  static WidgetsBinding ensureInitialized() {
627 628 629
    if (WidgetsBinding.instance == null)
      new WidgetsFlutterBinding();
    return WidgetsBinding.instance;
630 631
  }
}