binding.dart 43.2 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:async';
6
import 'dart:developer' as developer;
7
import 'dart:ui' show AppLifecycleState, Locale, AccessibilityFeatures, FrameTiming, TimingsCallback;
8

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

15
import 'app.dart';
16
import 'debug.dart';
17
import 'focus_manager.dart';
18
import 'framework.dart';
19
import 'widget_inspector.dart';
20

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

23 24
/// Interface for classes that register with the Widgets layer binding.
///
25
/// When used as a mixin, provides no-op method implementations.
26
///
27
/// See [WidgetsBinding.addObserver] and [WidgetsBinding.removeObserver].
28 29 30 31 32
///
/// This class can be extended directly, to get default behaviors for all of the
/// handlers, or can used with the `implements` keyword, in which case all the
/// handlers must be implemented (and the analyzer will list those that have
/// been omitted).
33
///
34
/// {@tool snippet}
35 36 37 38 39 40
///
/// This [StatefulWidget] implements the parts of the [State] and
/// [WidgetsBindingObserver] protocols necessary to react to application
/// lifecycle messages. See [didChangeAppLifecycleState].
///
/// ```dart
41 42
/// class AppLifecycleReactor extends StatefulWidget {
///   const AppLifecycleReactor({ Key key }) : super(key: key);
43 44
///
///   @override
45
///   _AppLifecycleReactorState createState() => _AppLifecycleReactorState();
46 47
/// }
///
48
/// class _AppLifecycleReactorState extends State<AppLifecycleReactor> with WidgetsBindingObserver {
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
///   @override
///   void initState() {
///     super.initState();
///     WidgetsBinding.instance.addObserver(this);
///   }
///
///   @override
///   void dispose() {
///     WidgetsBinding.instance.removeObserver(this);
///     super.dispose();
///   }
///
///   AppLifecycleState _notification;
///
///   @override
///   void didChangeAppLifecycleState(AppLifecycleState state) {
///     setState(() { _notification = state; });
///   }
///
///   @override
///   Widget build(BuildContext context) {
70
///     return Text('Last notification: $_notification');
71 72 73
///   }
/// }
/// ```
74
/// {@end-tool}
75 76 77
///
/// To respond to other notifications, replace the [didChangeAppLifecycleState]
/// method above with other methods from this class.
78 79 80 81 82 83 84 85 86 87 88 89 90
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.
91 92 93
  ///
  /// This method exposes the `popRoute` notification from
  /// [SystemChannels.navigation].
94
  Future<bool> didPopRoute() => Future<bool>.value(false);
95

96 97 98 99
  /// Called when the host tells the app to push a new route onto the
  /// navigator.
  ///
  /// Observers are expected to return true if they were able to
100
  /// handle the notification. Observers are notified in registration
101
  /// order until one returns true.
102 103 104
  ///
  /// This method exposes the `pushRoute` notification from
  /// [SystemChannels.navigation].
105
  Future<bool> didPushRoute(String route) => Future<bool>.value(false);
106

107 108
  /// Called when the application's dimensions change. For example,
  /// when a phone is rotated.
109
  ///
110 111
  /// This method exposes notifications from [Window.onMetricsChanged].
  ///
112
  /// {@tool snippet}
113 114 115 116 117 118
  ///
  /// This [StatefulWidget] implements the parts of the [State] and
  /// [WidgetsBindingObserver] protocols necessary to react when the device is
  /// rotated (or otherwise changes dimensions).
  ///
  /// ```dart
119 120
  /// class MetricsReactor extends StatefulWidget {
  ///   const MetricsReactor({ Key key }) : super(key: key);
121 122
  ///
  ///   @override
123
  ///   _MetricsReactorState createState() => _MetricsReactorState();
124 125
  /// }
  ///
126
  /// class _MetricsReactorState extends State<MetricsReactor> with WidgetsBindingObserver {
127 128
  ///   Size _lastSize;
  ///
129 130 131
  ///   @override
  ///   void initState() {
  ///     super.initState();
132
  ///     _lastSize = WidgetsBinding.instance.window.physicalSize;
133 134 135 136 137 138 139 140 141 142 143
  ///     WidgetsBinding.instance.addObserver(this);
  ///   }
  ///
  ///   @override
  ///   void dispose() {
  ///     WidgetsBinding.instance.removeObserver(this);
  ///     super.dispose();
  ///   }
  ///
  ///   @override
  ///   void didChangeMetrics() {
144
  ///     setState(() { _lastSize = WidgetsBinding.instance.window.physicalSize; });
145 146 147 148
  ///   }
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
149
  ///     return Text('Current size: $_lastSize');
150 151 152
  ///   }
  /// }
  /// ```
153
  /// {@end-tool}
154 155 156 157 158 159 160 161 162
  ///
  /// In general, this is unnecessary as the layout system takes care of
  /// automatically recomputing the application geometry when the application
  /// size changes.
  ///
  /// See also:
  ///
  ///  * [MediaQuery.of], which provides a similar service with less
  ///    boilerplate.
163
  void didChangeMetrics() { }
164

165 166 167 168 169 170
  /// Called when the platform's text scale factor changes.
  ///
  /// This typically happens as the result of the user changing system
  /// preferences, and it should affect all of the text sizes in the
  /// application.
  ///
171 172
  /// This method exposes notifications from [Window.onTextScaleFactorChanged].
  ///
173
  /// {@tool snippet}
174 175 176 177 178 179
  ///
  /// ```dart
  /// class TextScaleFactorReactor extends StatefulWidget {
  ///   const TextScaleFactorReactor({ Key key }) : super(key: key);
  ///
  ///   @override
180
  ///   _TextScaleFactorReactorState createState() => _TextScaleFactorReactorState();
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
  /// }
  ///
  /// class _TextScaleFactorReactorState extends State<TextScaleFactorReactor> with WidgetsBindingObserver {
  ///   @override
  ///   void initState() {
  ///     super.initState();
  ///     WidgetsBinding.instance.addObserver(this);
  ///   }
  ///
  ///   @override
  ///   void dispose() {
  ///     WidgetsBinding.instance.removeObserver(this);
  ///     super.dispose();
  ///   }
  ///
  ///   double _lastTextScaleFactor;
  ///
  ///   @override
  ///   void didChangeTextScaleFactor() {
200
  ///     setState(() { _lastTextScaleFactor = WidgetsBinding.instance.window.textScaleFactor; });
201 202 203 204
  ///   }
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
205
  ///     return Text('Current scale factor: $_lastTextScaleFactor');
206 207 208
  ///   }
  /// }
  /// ```
209
  /// {@end-tool}
210 211 212 213 214 215 216
  ///
  /// See also:
  ///
  ///  * [MediaQuery.of], which provides a similar service with less
  ///    boilerplate.
  void didChangeTextScaleFactor() { }

217 218 219
  /// Called when the platform brightness changes.
  ///
  /// This method exposes notifications from [Window.onPlatformBrightnessChanged].
220 221
  void didChangePlatformBrightness() { }

222 223 224
  /// Called when the system tells the app that the user's locale has
  /// changed. For example, if the user changes the system language
  /// settings.
225 226
  ///
  /// This method exposes notifications from [Window.onLocaleChanged].
227
  void didChangeLocales(List<Locale> locale) { }
228 229 230

  /// Called when the system puts the app in the background or returns
  /// the app to the foreground.
231 232 233
  ///
  /// An example of implementing this method is provided in the class-level
  /// documentation for the [WidgetsBindingObserver] class.
234 235
  ///
  /// This method exposes notifications from [SystemChannels.lifecycle].
236
  void didChangeAppLifecycleState(AppLifecycleState state) { }
237 238

  /// Called when the system is running low on memory.
239 240 241
  ///
  /// This method exposes the `memoryPressure` notification from
  /// [SystemChannels.system].
242
  void didHaveMemoryPressure() { }
243 244 245 246 247

  /// Called when the system changes the set of currently active accessibility
  /// features.
  ///
  /// This method exposes notifications from [Window.onAccessibilityFeaturesChanged].
248
  void didChangeAccessibilityFeatures() { }
Ian Hickson's avatar
Ian Hickson committed
249
}
250

251
/// The glue between the widgets layer and the Flutter engine.
252
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
253
  @override
254
  void initInstances() {
Ian Hickson's avatar
Ian Hickson committed
255 256
    super.initInstances();
    _instance = this;
257 258 259 260 261 262

    assert(() {
      _debugAddStackFilters();
      return true;
    }());

263 264 265 266
    // Initialization of [_buildOwner] has to be done after
    // [super.initInstances] is called, as it requires [ServicesBinding] to
    // properly setup the [defaultBinaryMessenger] instance.
    _buildOwner = BuildOwner();
267
    buildOwner.onBuildScheduled = _handleBuildScheduled;
268 269
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
270
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
271
    FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
272 273
  }

274 275 276 277 278
  void _debugAddStackFilters() {
    const PartialStackFrame elementInflateWidget = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'inflateWidget');
    const PartialStackFrame elementUpdateChild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'updateChild');
    const PartialStackFrame elementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'rebuild');
    const PartialStackFrame componentElementPerformRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'performRebuild');
Dan Field's avatar
Dan Field committed
279
    const PartialStackFrame componentElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: '_firstBuild');
280
    const PartialStackFrame componentElementMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'mount');
Dan Field's avatar
Dan Field committed
281
    const PartialStackFrame statefulElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: '_firstBuild');
282
    const PartialStackFrame singleChildMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'SingleChildRenderObjectElement', method: 'mount');
Dan Field's avatar
Dan Field committed
283
    const PartialStackFrame statefulElementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: 'performRebuild');
284 285 286 287 288 289 290 291 292 293

    const String replacementString = '...     Normal element mounting';

    // ComponentElement variations
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementInflateWidget,
        elementUpdateChild,
        componentElementPerformRebuild,
        elementRebuild,
Dan Field's avatar
Dan Field committed
294
        componentElementFirstBuild,
295 296 297 298 299 300 301 302 303
        componentElementMount,
      ],
      replacement: replacementString,
    ));
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementUpdateChild,
        componentElementPerformRebuild,
        elementRebuild,
Dan Field's avatar
Dan Field committed
304
        componentElementFirstBuild,
305 306 307 308 309 310 311 312 313 314 315
        componentElementMount,
      ],
      replacement: replacementString,
    ));

    // StatefulElement variations
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementInflateWidget,
        elementUpdateChild,
        componentElementPerformRebuild,
Dan Field's avatar
Dan Field committed
316
        statefulElementRebuild,
317
        elementRebuild,
Dan Field's avatar
Dan Field committed
318 319
        componentElementFirstBuild,
        statefulElementFirstBuild,
320 321 322 323 324 325 326 327
        componentElementMount,
      ],
      replacement: replacementString,
    ));
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementUpdateChild,
        componentElementPerformRebuild,
Dan Field's avatar
Dan Field committed
328
        statefulElementRebuild,
329
        elementRebuild,
Dan Field's avatar
Dan Field committed
330 331
        componentElementFirstBuild,
        statefulElementFirstBuild,
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
        componentElementMount,
      ],
      replacement: replacementString,
    ));

    // SingleChildRenderObjectElement variations
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementInflateWidget,
        elementUpdateChild,
        singleChildMount,
      ],
      replacement: replacementString,
    ));
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementUpdateChild,
        singleChildMount,
      ],
      replacement: replacementString,
    ));
  }

355
  /// The current [WidgetsBinding], if one has been created.
356 357 358
  ///
  /// If you need the binding to be constructed before calling [runApp],
  /// you can ensure a Widget binding has been constructed by calling the
359 360 361
  /// `WidgetsFlutterBinding.ensureInitialized()` function.
  static WidgetsBinding get instance => _instance;
  static WidgetsBinding _instance;
362

363 364 365 366
  @override
  void initServiceExtensions() {
    super.initServiceExtensions();

367
    if (!kReleaseMode) {
368 369 370 371 372 373 374
      registerSignalServiceExtension(
        name: 'debugDumpApp',
        callback: () {
          debugDumpApp();
          return debugPrintDone;
        },
      );
375

376 377 378 379 380 381 382 383 384 385 386 387 388
      if (!kIsWeb) {
        registerBoolServiceExtension(
          name: 'showPerformanceOverlay',
          getter: () =>
          Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
          setter: (bool value) {
            if (WidgetsApp.showPerformanceOverlayOverride == value)
              return Future<void>.value();
            WidgetsApp.showPerformanceOverlayOverride = value;
            return _forceRebuild();
          },
        );
      }
389 390 391 392 393

      registerServiceExtension(
        name: 'didSendFirstFrameEvent',
        callback: (_) async {
          return <String, dynamic>{
394 395 396
            // This is defined to return a STRING, not a boolean.
            // Devtools, the Intellij plugin, and the flutter tool all depend
            // on it returning a string and not a boolean.
397
            'enabled': _needToReportFirstFrame ? 'false' : 'true',
398 399 400
          };
        },
      );
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415
      // This returns 'true' when the first frame is rasterized, and the trace
      // event 'Rasterized first useful frame' is sent out.
      registerServiceExtension(
        name: 'didSendFirstFrameRasterizedEvent',
        callback: (_) async {
          return <String, dynamic>{
            // This is defined to return a STRING, not a boolean.
            // Devtools, the Intellij plugin, and the flutter tool all depend
            // on it returning a string and not a boolean.
            'enabled': firstFrameRasterized ? 'true' : 'false',
          };
        },
      );

416 417 418 419 420 421
      // Register the ability to quickly mark elements as dirty.
      // The performance of this method may be improved with additional
      // information from https://github.com/flutter/flutter/issues/46195.
      registerServiceExtension(
        name: 'fastReassemble',
        callback: (Map<String, Object> params) async {
422
          final String className = params['class'] as String;
423 424 425 426 427 428 429 430 431 432 433 434 435 436
          void markElementsDirty(Element element) {
            if (element == null) {
              return;
            }
            if (element.widget?.runtimeType?.toString()?.startsWith(className) ?? false) {
              element.markNeedsBuild();
            }
            element.visitChildElements(markElementsDirty);
          }
          markElementsDirty(renderViewElement);
          return <String, String>{'Success': 'true'};
        },
      );

437 438 439 440 441 442 443 444 445
      // Expose the ability to send Widget rebuilds as [Timeline] events.
      registerBoolServiceExtension(
        name: 'profileWidgetBuilds',
        getter: () async => debugProfileBuildsEnabled,
        setter: (bool value) async {
          if (debugProfileBuildsEnabled != value)
            debugProfileBuildsEnabled = value;
        },
      );
446
    }
447

448
    assert(() {
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
      registerBoolServiceExtension(
        name: 'debugAllowBanner',
        getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
        setter: (bool value) {
          if (WidgetsApp.debugAllowBannerOverride == value)
            return Future<void>.value();
          WidgetsApp.debugAllowBannerOverride = value;
          return _forceRebuild();
        },
      );

      // This service extension is deprecated and will be removed by 12/1/2018.
      // Use ext.flutter.inspector.show instead.
      registerBoolServiceExtension(
          name: 'debugWidgetInspector',
          getter: () async => WidgetsApp.debugShowWidgetInspectorOverride,
          setter: (bool value) {
            if (WidgetsApp.debugShowWidgetInspectorOverride == value)
              return Future<void>.value();
            WidgetsApp.debugShowWidgetInspectorOverride = value;
            return _forceRebuild();
470
          },
471
      );
472

473 474 475 476
      WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);

      return true;
    }());
477 478
  }

479
  Future<void> _forceRebuild() {
480 481 482 483
    if (renderViewElement != null) {
      buildOwner.reassemble(renderViewElement);
      return endOfFrame;
    }
484
    return Future<void>.value();
485 486
  }

487 488 489
  /// The [BuildOwner] in charge of executing the build pipeline for the
  /// widget tree rooted at this binding.
  BuildOwner get buildOwner => _buildOwner;
490 491 492 493
  // Initialization of [_buildOwner] has to be done within the [initInstances]
  // method, as it requires [ServicesBinding] to properly setup the
  // [defaultBinaryMessenger] instance.
  BuildOwner _buildOwner;
Ian Hickson's avatar
Ian Hickson committed
494

495
  /// The object in charge of the focus tree.
496
  ///
497 498
  /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
  /// the [FocusScopeNode] for a given [BuildContext].
499
  ///
500
  /// See [FocusManager] for more details.
501
  FocusManager get focusManager => _buildOwner.focusManager;
502

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

505 506 507 508 509 510 511 512 513 514 515 516
  /// 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).
517 518 519 520 521
  ///
  /// See also:
  ///
  ///  * [removeObserver], to release the resources reserved by this method.
  ///  * [WidgetsBindingObserver], which has an example of using this method.
522 523 524 525 526
  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).
527 528 529 530 531
  ///
  /// See also:
  ///
  ///  * [addObserver], for the method that adds observers in the first place.
  ///  * [WidgetsBindingObserver], which has an example of using this method.
532 533
  bool removeObserver(WidgetsBindingObserver observer) => _observers.remove(observer);

534
  @override
Ian Hickson's avatar
Ian Hickson committed
535 536
  void handleMetricsChanged() {
    super.handleMetricsChanged();
537
    for (final WidgetsBindingObserver observer in _observers)
538
      observer.didChangeMetrics();
Ian Hickson's avatar
Ian Hickson committed
539 540
  }

541 542 543
  @override
  void handleTextScaleFactorChanged() {
    super.handleTextScaleFactorChanged();
544
    for (final WidgetsBindingObserver observer in _observers)
545 546 547
      observer.didChangeTextScaleFactor();
  }

548 549 550
  @override
  void handlePlatformBrightnessChanged() {
    super.handlePlatformBrightnessChanged();
551
    for (final WidgetsBindingObserver observer in _observers)
552 553 554
      observer.didChangePlatformBrightness();
  }

555 556 557
  @override
  void handleAccessibilityFeaturesChanged() {
    super.handleAccessibilityFeaturesChanged();
558
    for (final WidgetsBindingObserver observer in _observers)
559 560 561
      observer.didChangeAccessibilityFeatures();
  }

562
  /// Called when the system locale changes.
563 564 565
  ///
  /// Calls [dispatchLocaleChanged] to notify the binding observers.
  ///
566
  /// See [Window.onLocaleChanged].
567 568
  @protected
  @mustCallSuper
Ian Hickson's avatar
Ian Hickson committed
569
  void handleLocaleChanged() {
570
    dispatchLocalesChanged(window.locales);
Ian Hickson's avatar
Ian Hickson committed
571 572
  }

573
  /// Notify all the observers that the locale has changed (using
574 575
  /// [WidgetsBindingObserver.didChangeLocales]), giving them the
  /// `locales` argument.
576 577 578
  ///
  /// This is called by [handleLocaleChanged] when the [Window.onLocaleChanged]
  /// notification is received.
579 580
  @protected
  @mustCallSuper
581
  void dispatchLocalesChanged(List<Locale> locales) {
582
    for (final WidgetsBindingObserver observer in _observers)
583
      observer.didChangeLocales(locales);
Ian Hickson's avatar
Ian Hickson committed
584 585
  }

586 587 588 589 590
  /// Notify all the observers that the active set of [AccessibilityFeatures]
  /// has changed (using [WidgetsBindingObserver.didChangeAccessibilityFeatures]),
  /// giving them the `features` argument.
  ///
  /// This is called by [handleAccessibilityFeaturesChanged] when the
591
  /// [Window.onAccessibilityFeaturesChanged] notification is received.
592 593 594
  @protected
  @mustCallSuper
  void dispatchAccessibilityFeaturesChanged() {
595
    for (final WidgetsBindingObserver observer in _observers)
596 597 598
      observer.didChangeAccessibilityFeatures();
  }

599
  /// Called when the system pops the current route.
600 601
  ///
  /// This first notifies the binding observers (using
602 603 604 605
  /// [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 by calling [SystemNavigator.pop].
606 607 608 609
  ///
  /// [WidgetsApp] uses this in conjunction with a [Navigator] to
  /// cause the back button to close dialog boxes, return from modal
  /// pages, and so forth.
610 611 612
  ///
  /// This method exposes the `popRoute` notification from
  /// [SystemChannels.navigation].
613
  @protected
614
  Future<void> handlePopRoute() async {
615
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
616
      if (await observer.didPopRoute())
617
        return;
Ian Hickson's avatar
Ian Hickson committed
618
    }
619
    SystemNavigator.pop();
Ian Hickson's avatar
Ian Hickson committed
620
  }
Hixie's avatar
Hixie committed
621

622 623
  /// Called when the host tells the app to push a new route onto the
  /// navigator.
624 625 626 627 628 629 630 631
  ///
  /// This notifies the binding observers (using
  /// [WidgetsBindingObserver.didPushRoute]), in registration order, until one
  /// returns true, meaning that it was able to handle the request (e.g. by
  /// opening a dialog box). If none return true, then nothing happens.
  ///
  /// This method exposes the `pushRoute` notification from
  /// [SystemChannels.navigation].
632 633
  @protected
  @mustCallSuper
634
  Future<void> handlePushRoute(String route) async {
635
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
636 637 638 639 640 641 642 643 644 645
      if (await observer.didPushRoute(route))
        return;
    }
  }

  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        return handlePopRoute();
      case 'pushRoute':
646
        return handlePushRoute(methodCall.arguments as String);
647
    }
648
    return Future<dynamic>.value();
649 650
  }

651
  @override
652
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
653
    super.handleAppLifecycleStateChanged(state);
654
    for (final WidgetsBindingObserver observer in _observers)
655 656 657
      observer.didChangeAppLifecycleState(state);
  }

658 659 660 661 662 663 664 665 666
  /// Called when the operating system notifies the application of a memory
  /// pressure situation.
  ///
  /// Notifies all the observers using
  /// [WidgetsBindingObserver.didHaveMemoryPressure].
  ///
  /// This method exposes the `memoryPressure` notification from
  /// [SystemChannels.system].
  void handleMemoryPressure() {
667
    for (final WidgetsBindingObserver observer in _observers)
668 669 670
      observer.didHaveMemoryPressure();
  }

671 672 673
  @override
  Future<void> handleSystemMessage(Object systemMessage) async {
    await super.handleSystemMessage(systemMessage);
674 675
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
676 677 678 679
    switch (type) {
      case 'memoryPressure':
        handleMemoryPressure();
        break;
680
    }
681
    return;
682 683
  }

684
  bool _needToReportFirstFrame = true;
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708

  final Completer<void> _firstFrameCompleter = Completer<void>();

  /// Whether the Flutter engine has rasterized the first frame.
  ///
  /// {@macro flutter.frame_rasterized_vs_presented}
  ///
  /// See also:
  ///
  ///  * [waitUntilFirstFrameRasterized], the future when [firstFrameRasterized]
  ///    becomes true.
  bool get firstFrameRasterized => _firstFrameCompleter.isCompleted;

  /// A future that completes when the Flutter engine has rasterized the first
  /// frame.
  ///
  /// {@macro flutter.frame_rasterize_vs_presented}
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether this future has completed or not.
  Future<void> get waitUntilFirstFrameRasterized => _firstFrameCompleter.future;

  /// Whether the first frame has finished building.
709
  ///
710 711
  /// This value can also be obtained over the VM service protocol as
  /// `ext.flutter.didSendFirstFrameEvent`.
712 713 714 715
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether the first frame has finished rendering.
716 717
  bool get debugDidSendFirstFrameEvent => !_needToReportFirstFrame;

718 719
  /// Tell the framework not to report the frame it is building as a "useful"
  /// first frame until there is a corresponding call to [allowFirstFrameReport].
720
  ///
721 722 723 724 725 726
  /// Deprecated. Use [deferFirstFrame]/[allowFirstFrame] to delay rendering the
  /// first frame.
  @Deprecated(
    'Use deferFirstFrame/allowFirstFrame to delay rendering the first frame. '
    'This feature was deprecated after v1.12.4.'
  )
727
  void deferFirstFrameReport() {
728
    if (!kReleaseMode) {
729
      deferFirstFrame();
730
    }
731 732
  }

733 734 735
  /// When called after [deferFirstFrameReport]: tell the framework to report
  /// the frame it is building as a "useful" first frame.
  ///
736 737 738 739 740 741
  /// Deprecated. Use [deferFirstFrame]/[allowFirstFrame] to delay rendering the
  /// first frame.
  @Deprecated(
    'Use deferFirstFrame/allowFirstFrame to delay rendering the first frame. '
    'This feature was deprecated after v1.12.4.'
  )
742
  void allowFirstFrameReport() {
743
    if (!kReleaseMode) {
744
      allowFirstFrame();
745
    }
746 747
  }

748
  void _handleBuildScheduled() {
749 750 751 752
    // If we're in the process of building dirty elements, then changes
    // should not trigger a new frame.
    assert(() {
      if (debugBuildingDirtyElements) {
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('Build scheduled during frame.'),
          ErrorDescription(
            'While the widget tree was being built, laid out, and painted, '
            'a new frame was scheduled to rebuild the widget tree.'
          ),
          ErrorHint(
            '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.',
          )
        ]);
774 775
      }
      return true;
776
    }());
777
    ensureVisualUpdate();
778 779
  }

780 781 782 783 784 785 786 787
  /// 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;
788

789 790
  /// Pump the build and rendering pipeline to generate a frame.
  ///
791
  /// This method is called by [handleDrawFrame], which itself is called
Wu Zhong's avatar
Wu Zhong committed
792
  /// automatically by the engine when it is time to lay out and paint a
793 794 795 796 797
  /// frame.
  ///
  /// Each frame consists of the following phases:
  ///
  /// 1. The animation phase: The [handleBeginFrame] method, which is registered
Ian Hickson's avatar
Ian Hickson committed
798
  /// with [Window.onBeginFrame], invokes all the transient frame callbacks
799
  /// registered with [scheduleFrameCallback], in
800 801 802 803
  /// 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.
  ///
804 805 806 807
  /// 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.
808
  ///
809
  /// After [handleBeginFrame], [handleDrawFrame], which is registered with
Ian Hickson's avatar
Ian Hickson committed
810
  /// [Window.onDrawFrame], is called, which invokes all the persistent frame
811 812 813 814
  /// callbacks, of which the most notable is this method, [drawFrame], which
  /// proceeds as follows:
  ///
  /// 3. The build phase: All the dirty [Element]s in the widget tree are
815 816 817 818
  /// 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.
  ///
819
  /// 4. The layout phase: All the dirty [RenderObject]s in the system are laid
820 821 822
  /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout]
  /// for further details on marking an object dirty for layout.
  ///
823
  /// 5. The compositing bits phase: The compositing bits on any dirty
824 825 826
  /// [RenderObject] objects are updated. See
  /// [RenderObject.markNeedsCompositingBitsUpdate].
  ///
827
  /// 6. The paint phase: All the dirty [RenderObject]s in the system are
828 829 830 831
  /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See
  /// [RenderObject.markNeedsPaint] for further details on marking an object
  /// dirty for paint.
  ///
832
  /// 7. The compositing phase: The layer tree is turned into a [Scene] and
833 834
  /// sent to the GPU.
  ///
835
  /// 8. The semantics phase: All the dirty [RenderObject]s in the system have
836
  /// their semantics updated (see [RenderObject.assembleSemanticsNode]). This
837 838 839 840
  /// generates the [SemanticsNode] tree. See
  /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an
  /// object dirty for semantics.
  ///
841
  /// For more details on steps 4-8, see [PipelineOwner].
842
  ///
843
  /// 9. The finalization phase in the widgets layer: The widgets tree is
844 845 846 847
  /// 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.
  ///
848 849 850
  /// 10. The finalization phase in the scheduler layer: After [drawFrame]
  /// returns, [handleDrawFrame] then invokes post-frame callbacks (registered
  /// with [addPostFrameCallback]).
851 852
  //
  // When editing the above, also update rendering/binding.dart's copy.
853
  @override
854
  void drawFrame() {
855 856 857 858
    assert(!debugBuildingDirtyElements);
    assert(() {
      debugBuildingDirtyElements = true;
      return true;
859
    }());
860

861 862
    TimingsCallback firstFrameCallback;
    if (_needToReportFirstFrame) {
863
      assert(!_firstFrameCompleter.isCompleted);
864 865

      firstFrameCallback = (List<FrameTiming> timings) {
866
        assert(sendFramesToEngine);
867 868 869 870
        if (!kReleaseMode) {
          developer.Timeline.instantSync('Rasterized first useful frame');
          developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
        }
871
        SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
872
        firstFrameCallback = null;
873
        _firstFrameCompleter.complete();
874
      };
875 876 877
      // Callback is only invoked when [Window.render] is called. When
      // [sendFramesToEngine] is set to false during the frame, it will not
      // be called and we need to remove the callback (see below).
878
      SchedulerBinding.instance.addTimingsCallback(firstFrameCallback);
879 880
    }

881
    try {
882 883
      if (renderViewElement != null)
        buildOwner.buildScope(renderViewElement);
884
      super.drawFrame();
885 886 887 888 889
      buildOwner.finalizeTree();
    } finally {
      assert(() {
        debugBuildingDirtyElements = false;
        return true;
890
      }());
891
    }
892
    if (!kReleaseMode) {
893
      if (_needToReportFirstFrame && sendFramesToEngine) {
894
        developer.Timeline.instantSync('Widgets built first useful frame');
895
      }
896
    }
897
    _needToReportFirstFrame = false;
898 899 900
    if (firstFrameCallback != null && !sendFramesToEngine) {
      SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
    }
901
  }
902 903 904

  /// The [Element] that is at the root of the hierarchy (and which wraps the
  /// [RenderView] object at the root of the rendering hierarchy).
905 906
  ///
  /// This is initialized the first time [runApp] is called.
907 908
  Element get renderViewElement => _renderViewElement;
  Element _renderViewElement;
909

910 911 912 913 914 915 916 917 918 919 920
  /// Schedules a [Timer] for attaching the root widget.
  ///
  /// This is called by [runApp] to configure the widget tree. Consider using
  /// [attachRootWidget] if you want to build the widget tree synchronously.
  @protected
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }

921 922 923 924 925
  /// Takes a widget and attaches it to the [renderViewElement], creating it if
  /// necessary.
  ///
  /// This is called by [runApp] to configure the widget tree.
  ///
926 927 928 929
  /// See also:
  ///
  ///  * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
  ///    widget and attaches it to the render tree.
930
  void attachRootWidget(Widget rootWidget) {
931
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
932
      container: renderView,
933
      debugShortDescription: '[root]',
934
      child: rootWidget,
935
    ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
936
  }
937

938 939 940 941 942 943
  /// Whether the [renderViewElement] has been initialized.
  ///
  /// This will be false until [runApp] is called (or [WidgetTester.pumpWidget]
  /// is called in the context of a [TestWidgetsFlutterBinding]).
  bool get isRootWidgetAttached => _renderViewElement != null;

944
  @override
945
  Future<void> performReassemble() {
946 947 948 949 950
    assert(() {
      WidgetInspectorService.instance.performReassemble();
      return true;
    }());

951 952
    if (renderViewElement != null)
      buildOwner.reassemble(renderViewElement);
953
    return super.performReassemble();
954
  }
955
}
Hixie's avatar
Hixie committed
956

957
/// Inflate the given widget and attach it to the screen.
958
///
959 960 961 962 963
/// 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
///
964 965 966 967 968 969
/// Calling [runApp] again will detach the previous root widget from the screen
/// and attach the given widget in its place. The new widget tree is compared
/// against the previous widget tree and any differences are applied to the
/// underlying render tree, similar to what happens when a [StatefulWidget]
/// rebuilds after calling [State.setState].
///
970
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
971 972 973
///
/// See also:
///
974 975 976 977 978 979
///  * [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
980
void runApp(Widget app) {
981
  WidgetsFlutterBinding.ensureInitialized()
982
    ..scheduleAttachRootWidget(app)
983
    ..scheduleWarmUpFrame();
Hixie's avatar
Hixie committed
984 985
}

986
/// Print a string representation of the currently running app.
987
void debugDumpApp() {
988
  assert(WidgetsBinding.instance != null);
Hixie's avatar
Hixie committed
989
  String mode = 'RELEASE MODE';
990 991 992 993
  assert(() {
    mode = 'CHECKED MODE';
    return true;
  }());
994
  debugPrint('${WidgetsBinding.instance.runtimeType} - $mode');
995 996 997 998 999
  if (WidgetsBinding.instance.renderViewElement != null) {
    debugPrint(WidgetsBinding.instance.renderViewElement.toStringDeep());
  } else {
    debugPrint('<no tree currently mounted>');
  }
1000 1001
}

1002 1003 1004 1005 1006 1007 1008 1009
/// 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
1010
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
1011 1012 1013
  /// Creates a bridge from a [RenderObject] to an [Element] tree.
  ///
  /// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
1014 1015
  RenderObjectToWidgetAdapter({
    this.child,
1016
    this.container,
1017
    this.debugShortDescription,
1018
  }) : super(key: GlobalObjectKey(container));
Hixie's avatar
Hixie committed
1019

1020
  /// The widget below this widget in the tree.
1021 1022
  ///
  /// {@macro flutter.widgets.child}
Hixie's avatar
Hixie committed
1023
  final Widget child;
1024

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

1028
  /// A short description of this widget used by debugging aids.
1029
  final String debugShortDescription;
Hixie's avatar
Hixie committed
1030

1031
  @override
1032
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
Hixie's avatar
Hixie committed
1033

1034
  @override
1035
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
Hixie's avatar
Hixie committed
1036

1037
  @override
1038
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }
1039

1040 1041
  /// Inflate this widget and actually set the resulting [RenderObject] as the
  /// child of [container].
1042 1043
  ///
  /// If `element` is null, this function will create a new element. Otherwise,
1044
  /// the given element will have an update scheduled to switch to this widget.
1045 1046
  ///
  /// Used by [runApp] to bootstrap applications.
1047
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
1048 1049
    if (element == null) {
      owner.lockState(() {
1050
        element = createElement();
1051
        assert(element != null);
1052
        element.assignOwner(owner);
1053 1054
      });
      owner.buildScope(element, () {
1055
        element.mount(null, null);
1056
      });
1057 1058 1059
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance.ensureVisualUpdate();
1060
    } else {
1061 1062
      element._newWidget = this;
      element.markNeedsBuild();
1063
    }
1064 1065
    return element;
  }
1066

1067
  @override
1068
  String toStringShort() => debugShortDescription ?? super.toStringShort();
Hixie's avatar
Hixie committed
1069 1070
}

1071 1072 1073 1074 1075
/// 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
1076
///
1077 1078
/// 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
1079
/// this usage, it is normally instantiated by the bootstrapping logic in the
1080
/// [WidgetsFlutterBinding] singleton created by [runApp].
1081
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
1082 1083 1084 1085 1086
  /// 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
1087 1088
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);

1089
  @override
1090
  RenderObjectToWidgetAdapter<T> get widget => super.widget as RenderObjectToWidgetAdapter<T>;
1091

Hixie's avatar
Hixie committed
1092 1093
  Element _child;

1094
  static const Object _rootChildSlot = Object();
Hixie's avatar
Hixie committed
1095

1096
  @override
Hixie's avatar
Hixie committed
1097 1098 1099 1100 1101
  void visitChildren(ElementVisitor visitor) {
    if (_child != null)
      visitor(_child);
  }

1102
  @override
1103
  void forgetChild(Element child) {
1104 1105
    assert(child == _child);
    _child = null;
1106
    super.forgetChild(child);
1107 1108
  }

1109
  @override
Hixie's avatar
Hixie committed
1110
  void mount(Element parent, dynamic newSlot) {
Hixie's avatar
Hixie committed
1111
    assert(parent == null);
Hixie's avatar
Hixie committed
1112
    super.mount(parent, newSlot);
1113
    _rebuild();
Hixie's avatar
Hixie committed
1114 1115
  }

1116
  @override
Hixie's avatar
Hixie committed
1117 1118 1119
  void update(RenderObjectToWidgetAdapter<T> newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
1120 1121 1122
    _rebuild();
  }

1123 1124 1125 1126 1127 1128
  // 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
1129 1130 1131 1132 1133
    if (_newWidget != null) {
      // _newWidget can be null if, for instance, we were rebuilt
      // due to a reassemble.
      final Widget newWidget = _newWidget;
      _newWidget = null;
1134
      update(newWidget as RenderObjectToWidgetAdapter<T>);
Ian Hickson's avatar
Ian Hickson committed
1135
    }
1136 1137 1138 1139
    super.performRebuild();
    assert(_newWidget == null);
  }

1140 1141 1142 1143 1144
  void _rebuild() {
    try {
      _child = updateChild(_child, widget.child, _rootChildSlot);
      assert(_child != null);
    } catch (exception, stack) {
1145
      final FlutterErrorDetails details = FlutterErrorDetails(
1146 1147 1148
        exception: exception,
        stack: stack,
        library: 'widgets library',
1149
        context: ErrorDescription('attaching to the render tree'),
1150 1151 1152
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
1153 1154
      _child = updateChild(null, error, _rootChildSlot);
    }
Hixie's avatar
Hixie committed
1155 1156
  }

1157
  @override
1158
  RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>;
Hixie's avatar
Hixie committed
1159

1160
  @override
Hixie's avatar
Hixie committed
1161
  void insertChildRenderObject(RenderObject child, dynamic slot) {
Ian Hickson's avatar
Ian Hickson committed
1162
    assert(slot == _rootChildSlot);
1163
    assert(renderObject.debugValidateChild(child));
1164
    renderObject.child = child as T;
Hixie's avatar
Hixie committed
1165 1166
  }

1167
  @override
Adam Barth's avatar
Adam Barth committed
1168 1169 1170 1171
  void moveChildRenderObject(RenderObject child, dynamic slot) {
    assert(false);
  }

1172
  @override
Hixie's avatar
Hixie committed
1173 1174 1175 1176
  void removeChildRenderObject(RenderObject child) {
    assert(renderObject.child == child);
    renderObject.child = null;
  }
Adam Barth's avatar
Adam Barth committed
1177
}
1178 1179

/// A concrete binding for applications based on the Widgets framework.
1180
///
1181
/// This is the glue that binds the framework to the Flutter engine.
1182
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
1183 1184 1185 1186 1187

  /// 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].
1188 1189 1190
  ///
  /// You only need to call this method if you need the binding to be
  /// initialized before calling [runApp].
1191 1192 1193 1194 1195
  ///
  /// In the `flutter_test` framework, [testWidgets] initializes the
  /// binding instance to a [TestWidgetsFlutterBinding], not a
  /// [WidgetsFlutterBinding].
  static WidgetsBinding ensureInitialized() {
1196
    if (WidgetsBinding.instance == null)
1197
      WidgetsFlutterBinding();
1198
    return WidgetsBinding.instance;
1199 1200
  }
}