binding.dart 46.9 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 AccessibilityFeatures, AppLifecycleState, FrameTiming, Locale, PlatformDispatcher, 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 'platform_menu_bar.dart';
20
import 'router.dart';
21
import 'widget_inspector.dart';
22

23 24
export 'dart:ui' show AppLifecycleState, Locale;

25 26
/// Interface for classes that register with the Widgets layer binding.
///
27
/// When used as a mixin, provides no-op method implementations.
28
///
29
/// See [WidgetsBinding.addObserver] and [WidgetsBinding.removeObserver].
30 31 32 33 34
///
/// 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).
35
///
36 37
/// {@tool dartpad}
/// This sample shows how to implement parts of the [State] and
38 39 40
/// [WidgetsBindingObserver] protocols necessary to react to application
/// lifecycle messages. See [didChangeAppLifecycleState].
///
41
/// ** See code in examples/api/lib/widgets/binding/widget_binding_observer.0.dart **
42
/// ```
43
/// {@end-tool}
44 45 46
///
/// To respond to other notifications, replace the [didChangeAppLifecycleState]
/// method above with other methods from this class.
47 48 49 50 51 52 53 54 55 56 57 58 59
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.
60 61 62
  ///
  /// This method exposes the `popRoute` notification from
  /// [SystemChannels.navigation].
63
  Future<bool> didPopRoute() => Future<bool>.value(false);
64

65
  /// Called when the host tells the application to push a new route onto the
66 67 68
  /// navigator.
  ///
  /// Observers are expected to return true if they were able to
69
  /// handle the notification. Observers are notified in registration
70
  /// order until one returns true.
71 72 73
  ///
  /// This method exposes the `pushRoute` notification from
  /// [SystemChannels.navigation].
74
  Future<bool> didPushRoute(String route) => Future<bool>.value(false);
75

76 77 78 79 80 81 82 83 84 85 86 87 88
  /// Called when the host tells the application to push a new
  /// [RouteInformation] and a restoration state onto the router.
  ///
  /// Observers are expected to return true if they were able to
  /// handle the notification. Observers are notified in registration
  /// order until one returns true.
  ///
  /// This method exposes the `pushRouteInformation` notification from
  /// [SystemChannels.navigation].
  ///
  /// The default implementation is to call the [didPushRoute] directly with the
  /// [RouteInformation.location].
  Future<bool> didPushRouteInformation(RouteInformation routeInformation) {
89
    return didPushRoute(routeInformation.location!);
90 91
  }

92 93
  /// Called when the application's dimensions change. For example,
  /// when a phone is rotated.
94
  ///
95 96
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onMetricsChanged].
97
  ///
98
  /// {@tool snippet}
99 100 101 102 103 104
  ///
  /// This [StatefulWidget] implements the parts of the [State] and
  /// [WidgetsBindingObserver] protocols necessary to react when the device is
  /// rotated (or otherwise changes dimensions).
  ///
  /// ```dart
105
  /// class MetricsReactor extends StatefulWidget {
106
  ///   const MetricsReactor({ super.key });
107 108
  ///
  ///   @override
109
  ///   State<MetricsReactor> createState() => _MetricsReactorState();
110 111
  /// }
  ///
112
  /// class _MetricsReactorState extends State<MetricsReactor> with WidgetsBindingObserver {
113
  ///   late Size _lastSize;
114
  ///
115 116 117
  ///   @override
  ///   void initState() {
  ///     super.initState();
118 119
  ///     _lastSize = WidgetsBinding.instance.window.physicalSize;
  ///     WidgetsBinding.instance.addObserver(this);
120 121 122 123
  ///   }
  ///
  ///   @override
  ///   void dispose() {
124
  ///     WidgetsBinding.instance.removeObserver(this);
125 126 127 128 129
  ///     super.dispose();
  ///   }
  ///
  ///   @override
  ///   void didChangeMetrics() {
130
  ///     setState(() { _lastSize = WidgetsBinding.instance.window.physicalSize; });
131 132 133 134
  ///   }
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
135
  ///     return Text('Current size: $_lastSize');
136 137 138
  ///   }
  /// }
  /// ```
139
  /// {@end-tool}
140 141 142 143 144 145 146 147 148
  ///
  /// 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.
149
  void didChangeMetrics() { }
150

151 152 153 154 155 156
  /// 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.
  ///
157 158
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onTextScaleFactorChanged].
159
  ///
160
  /// {@tool snippet}
161 162 163
  ///
  /// ```dart
  /// class TextScaleFactorReactor extends StatefulWidget {
164
  ///   const TextScaleFactorReactor({ super.key });
165 166
  ///
  ///   @override
167
  ///   State<TextScaleFactorReactor> createState() => _TextScaleFactorReactorState();
168 169 170 171 172 173
  /// }
  ///
  /// class _TextScaleFactorReactorState extends State<TextScaleFactorReactor> with WidgetsBindingObserver {
  ///   @override
  ///   void initState() {
  ///     super.initState();
174
  ///     WidgetsBinding.instance.addObserver(this);
175 176 177 178
  ///   }
  ///
  ///   @override
  ///   void dispose() {
179
  ///     WidgetsBinding.instance.removeObserver(this);
180 181 182
  ///     super.dispose();
  ///   }
  ///
183
  ///   late double _lastTextScaleFactor;
184 185 186
  ///
  ///   @override
  ///   void didChangeTextScaleFactor() {
187
  ///     setState(() { _lastTextScaleFactor = WidgetsBinding.instance.window.textScaleFactor; });
188 189 190 191
  ///   }
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
192
  ///     return Text('Current scale factor: $_lastTextScaleFactor');
193 194 195
  ///   }
  /// }
  /// ```
196
  /// {@end-tool}
197 198 199 200 201 202 203
  ///
  /// See also:
  ///
  ///  * [MediaQuery.of], which provides a similar service with less
  ///    boilerplate.
  void didChangeTextScaleFactor() { }

204 205
  /// Called when the platform brightness changes.
  ///
206 207
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged].
208 209
  void didChangePlatformBrightness() { }

210 211 212
  /// Called when the system tells the app that the user's locale has
  /// changed. For example, if the user changes the system language
  /// settings.
213
  ///
214 215
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onLocaleChanged].
216
  void didChangeLocales(List<Locale>? locales) { }
217 218 219

  /// Called when the system puts the app in the background or returns
  /// the app to the foreground.
220 221 222
  ///
  /// An example of implementing this method is provided in the class-level
  /// documentation for the [WidgetsBindingObserver] class.
223 224
  ///
  /// This method exposes notifications from [SystemChannels.lifecycle].
225
  void didChangeAppLifecycleState(AppLifecycleState state) { }
226 227

  /// Called when the system is running low on memory.
228 229 230
  ///
  /// This method exposes the `memoryPressure` notification from
  /// [SystemChannels.system].
231
  void didHaveMemoryPressure() { }
232 233 234 235

  /// Called when the system changes the set of currently active accessibility
  /// features.
  ///
236 237
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onAccessibilityFeaturesChanged].
238
  void didChangeAccessibilityFeatures() { }
Ian Hickson's avatar
Ian Hickson committed
239
}
240

241
/// The glue between the widgets layer and the Flutter engine.
242
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
243
  @override
244
  void initInstances() {
Ian Hickson's avatar
Ian Hickson committed
245 246
    super.initInstances();
    _instance = this;
247 248 249 250 251 252

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

253 254 255 256
    // Initialization of [_buildOwner] has to be done after
    // [super.initInstances] is called, as it requires [ServicesBinding] to
    // properly setup the [defaultBinaryMessenger] instance.
    _buildOwner = BuildOwner();
257
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
258 259
    platformDispatcher.onLocaleChanged = handleLocaleChanged;
    platformDispatcher.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
260
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
261 262 263 264
    assert(() {
      FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
      return true;
    }());
265
    platformMenuDelegate = DefaultPlatformMenuDelegate();
266 267
  }

268 269 270 271 272 273 274 275
  /// The current [WidgetsBinding], if one has been created.
  ///
  /// Provides access to the features exposed by this mixin. The binding must
  /// be initialized before using this getter; this is typically done by calling
  /// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
  static WidgetsBinding get instance => BindingBase.checkInstance(_instance);
  static WidgetsBinding? _instance;

276 277 278 279 280
  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
281
    const PartialStackFrame componentElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: '_firstBuild');
282
    const PartialStackFrame componentElementMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'mount');
Dan Field's avatar
Dan Field committed
283
    const PartialStackFrame statefulElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: '_firstBuild');
284
    const PartialStackFrame singleChildMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'SingleChildRenderObjectElement', method: 'mount');
Dan Field's avatar
Dan Field committed
285
    const PartialStackFrame statefulElementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: 'performRebuild');
286 287 288 289 290 291 292 293 294 295

    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
296
        componentElementFirstBuild,
297 298 299 300 301 302 303 304 305
        componentElementMount,
      ],
      replacement: replacementString,
    ));
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementUpdateChild,
        componentElementPerformRebuild,
        elementRebuild,
Dan Field's avatar
Dan Field committed
306
        componentElementFirstBuild,
307 308 309 310 311 312 313 314 315 316 317
        componentElementMount,
      ],
      replacement: replacementString,
    ));

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

357 358 359 360
  @override
  void initServiceExtensions() {
    super.initServiceExtensions();

361
    if (!kReleaseMode) {
362
      registerServiceExtension(
363
        name: 'debugDumpApp',
364 365 366 367 368
        callback: (Map<String, String> parameters) async {
          final String data = _debugDumpAppString();
          return <String, Object>{
            'data': data,
          };
369 370
        },
      );
371

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

      registerServiceExtension(
        name: 'didSendFirstFrameEvent',
        callback: (_) async {
          return <String, dynamic>{
391 392 393
            // 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.
394
            'enabled': _needToReportFirstFrame ? 'false' : 'true',
395 396 397
          };
        },
      );
398

399 400 401 402 403 404 405 406 407 408 409 410 411 412
      // 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',
          };
        },
      );

413 414 415
      registerServiceExtension(
        name: 'fastReassemble',
        callback: (Map<String, Object> params) async {
416 417 418 419 420 421 422 423 424 425
          // This mirrors the implementation of the 'reassemble' callback registration
          // in lib/src/foundation/binding.dart, but with the extra binding config used
          // to skip some reassemble work.
          final String? className = params['className'] as String?;
          BindingBase.debugReassembleConfig = DebugReassembleConfig(widgetName: className);
          try {
            await reassembleApplication();
          } finally {
            BindingBase.debugReassembleConfig = null;
          }
426
          return <String, String>{'type': 'Success'};
427 428 429
        },
      );

430 431 432 433 434
      // Expose the ability to send Widget rebuilds as [Timeline] events.
      registerBoolServiceExtension(
        name: 'profileWidgetBuilds',
        getter: () async => debugProfileBuildsEnabled,
        setter: (bool value) async {
435
          if (debugProfileBuildsEnabled != value) {
436
            debugProfileBuildsEnabled = value;
437
          }
438 439
        },
      );
440 441 442 443
      registerBoolServiceExtension(
        name: 'profileUserWidgetBuilds',
        getter: () async => debugProfileBuildsEnabledUserWidgets,
        setter: (bool value) async {
444
          if (debugProfileBuildsEnabledUserWidgets != value) {
445
            debugProfileBuildsEnabledUserWidgets = value;
446
          }
447 448
        },
      );
449
    }
450

451
    assert(() {
452 453 454 455
      registerBoolServiceExtension(
        name: 'debugAllowBanner',
        getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
        setter: (bool value) {
456
          if (WidgetsApp.debugAllowBannerOverride == value) {
457
            return Future<void>.value();
458
          }
459 460 461 462 463 464 465 466 467 468 469
          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) {
470
            if (WidgetsApp.debugShowWidgetInspectorOverride == value) {
471
              return Future<void>.value();
472
            }
473 474
            WidgetsApp.debugShowWidgetInspectorOverride = value;
            return _forceRebuild();
475
          },
476
      );
477

478 479 480 481
      WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);

      return true;
    }());
482 483
  }

484
  Future<void> _forceRebuild() {
485
    if (renderViewElement != null) {
486
      buildOwner!.reassemble(renderViewElement!, null);
487 488
      return endOfFrame;
    }
489
    return Future<void>.value();
490 491
  }

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

500
  /// The object in charge of the focus tree.
501
  ///
502 503
  /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
  /// the [FocusScopeNode] for a given [BuildContext].
504
  ///
505
  /// See [FocusManager] for more details.
506
  FocusManager get focusManager => _buildOwner!.focusManager;
507

508 509 510 511 512 513 514
  /// A delegate that communicates with a platform plugin for serializing and
  /// managing platform-rendered menu bars created by [PlatformMenuBar].
  ///
  /// This is set by default to a [DefaultPlatformMenuDelegate] instance in
  /// [initInstances].
  late PlatformMenuDelegate platformMenuDelegate;

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

517 518 519 520 521 522 523 524 525 526 527 528
  /// 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).
529 530 531 532 533
  ///
  /// See also:
  ///
  ///  * [removeObserver], to release the resources reserved by this method.
  ///  * [WidgetsBindingObserver], which has an example of using this method.
534 535 536 537 538
  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).
539 540 541 542 543
  ///
  /// See also:
  ///
  ///  * [addObserver], for the method that adds observers in the first place.
  ///  * [WidgetsBindingObserver], which has an example of using this method.
544 545
  bool removeObserver(WidgetsBindingObserver observer) => _observers.remove(observer);

546
  @override
Ian Hickson's avatar
Ian Hickson committed
547 548
  void handleMetricsChanged() {
    super.handleMetricsChanged();
549
    for (final WidgetsBindingObserver observer in _observers) {
550
      observer.didChangeMetrics();
551
    }
Ian Hickson's avatar
Ian Hickson committed
552 553
  }

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

562 563 564
  @override
  void handlePlatformBrightnessChanged() {
    super.handlePlatformBrightnessChanged();
565
    for (final WidgetsBindingObserver observer in _observers) {
566
      observer.didChangePlatformBrightness();
567
    }
568 569
  }

570 571 572
  @override
  void handleAccessibilityFeaturesChanged() {
    super.handleAccessibilityFeaturesChanged();
573
    for (final WidgetsBindingObserver observer in _observers) {
574
      observer.didChangeAccessibilityFeatures();
575
    }
576 577
  }

578
  /// Called when the system locale changes.
579
  ///
580
  /// Calls [dispatchLocalesChanged] to notify the binding observers.
581
  ///
582
  /// See [dart:ui.PlatformDispatcher.onLocaleChanged].
583 584
  @protected
  @mustCallSuper
Ian Hickson's avatar
Ian Hickson committed
585
  void handleLocaleChanged() {
586
    dispatchLocalesChanged(platformDispatcher.locales);
Ian Hickson's avatar
Ian Hickson committed
587 588
  }

589
  /// Notify all the observers that the locale has changed (using
590 591
  /// [WidgetsBindingObserver.didChangeLocales]), giving them the
  /// `locales` argument.
592
  ///
593 594
  /// This is called by [handleLocaleChanged] when the
  /// [PlatformDispatcher.onLocaleChanged] notification is received.
595 596
  @protected
  @mustCallSuper
597
  void dispatchLocalesChanged(List<Locale>? locales) {
598
    for (final WidgetsBindingObserver observer in _observers) {
599
      observer.didChangeLocales(locales);
600
    }
Ian Hickson's avatar
Ian Hickson committed
601 602
  }

603 604 605 606 607
  /// 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
608
  /// [PlatformDispatcher.onAccessibilityFeaturesChanged] notification is received.
609 610 611
  @protected
  @mustCallSuper
  void dispatchAccessibilityFeaturesChanged() {
612
    for (final WidgetsBindingObserver observer in _observers) {
613
      observer.didChangeAccessibilityFeatures();
614
    }
615 616
  }

617
  /// Called when the system pops the current route.
618 619
  ///
  /// This first notifies the binding observers (using
620 621 622 623
  /// [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].
624 625 626 627
  ///
  /// [WidgetsApp] uses this in conjunction with a [Navigator] to
  /// cause the back button to close dialog boxes, return from modal
  /// pages, and so forth.
628 629 630
  ///
  /// This method exposes the `popRoute` notification from
  /// [SystemChannels.navigation].
631
  @protected
632
  Future<void> handlePopRoute() async {
633
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
634
      if (await observer.didPopRoute()) {
635
        return;
636
      }
Ian Hickson's avatar
Ian Hickson committed
637
    }
638
    SystemNavigator.pop();
Ian Hickson's avatar
Ian Hickson committed
639
  }
Hixie's avatar
Hixie committed
640

641 642
  /// Called when the host tells the app to push a new route onto the
  /// navigator.
643 644 645 646 647 648 649 650
  ///
  /// 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].
651 652
  @protected
  @mustCallSuper
653
  Future<void> handlePushRoute(String route) async {
654
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
655
      if (await observer.didPushRoute(route)) {
656
        return;
657
      }
658 659 660
    }
  }

661
  Future<void> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async {
662
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
663 664 665 666
      if (
        await observer.didPushRouteInformation(
          RouteInformation(
            location: routeArguments['location'] as String,
667
            state: routeArguments['state'] as Object?,
668
          ),
669
        )
670 671 672
      ) {
        return;
      }
673 674 675
    }
  }

676 677 678 679 680
  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        return handlePopRoute();
      case 'pushRoute':
681
        return handlePushRoute(methodCall.arguments as String);
682 683
      case 'pushRouteInformation':
        return _handlePushRouteInformation(methodCall.arguments as Map<dynamic, dynamic>);
684
    }
685
    return Future<dynamic>.value();
686 687
  }

688
  @override
689
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
690
    super.handleAppLifecycleStateChanged(state);
691
    for (final WidgetsBindingObserver observer in _observers) {
692
      observer.didChangeAppLifecycleState(state);
693
    }
694 695
  }

696
  @override
697
  void handleMemoryPressure() {
698
    super.handleMemoryPressure();
699
    for (final WidgetsBindingObserver observer in _observers) {
700
      observer.didHaveMemoryPressure();
701
    }
702 703
  }

704
  bool _needToReportFirstFrame = true;
705 706 707 708 709

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

  /// Whether the Flutter engine has rasterized the first frame.
  ///
710 711 712
  /// Usually, the time that a frame is rasterized is very close to the time that
  /// it gets presented on the display. Specifically, rasterization is the last
  /// expensive phase of a frame that's still in Flutter's control.
713 714 715 716 717 718 719 720 721 722
  ///
  /// 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.
  ///
723 724 725
  /// Usually, the time that a frame is rasterized is very close to the time that
  /// it gets presented on the display. Specifically, rasterization is the last
  /// expensive phase of a frame that's still in Flutter's control.
726 727 728 729 730 731 732
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether this future has completed or not.
  Future<void> get waitUntilFirstFrameRasterized => _firstFrameCompleter.future;

  /// Whether the first frame has finished building.
733
  ///
734 735
  /// This value can also be obtained over the VM service protocol as
  /// `ext.flutter.didSendFirstFrameEvent`.
736 737 738 739
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether the first frame has finished rendering.
740 741
  bool get debugDidSendFirstFrameEvent => !_needToReportFirstFrame;

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

774 775 776 777 778 779 780 781
  /// 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;
782

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

855
    TimingsCallback? firstFrameCallback;
856
    if (_needToReportFirstFrame) {
857
      assert(!_firstFrameCompleter.isCompleted);
858 859

      firstFrameCallback = (List<FrameTiming> timings) {
860
        assert(sendFramesToEngine);
861
        if (!kReleaseMode) {
862 863 864 865 866
          // Change the current user tag back to the default tag. At this point,
          // the user tag should be set to "AppStartUp" (originally set in the
          // engine), so we need to change it back to the default tag to mark
          // the end of app start up for CPU profiles.
          developer.UserTag.defaultTag.makeCurrent();
867 868 869
          developer.Timeline.instantSync('Rasterized first useful frame');
          developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
        }
870
        SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
871
        firstFrameCallback = null;
872
        _firstFrameCompleter.complete();
873
      };
874 875 876
      // Callback is only invoked when FlutterView.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).
877
      SchedulerBinding.instance.addTimingsCallback(firstFrameCallback!);
878 879
    }

880
    try {
881
      if (renderViewElement != null) {
882
        buildOwner!.buildScope(renderViewElement!);
883
      }
884
      super.drawFrame();
885
      buildOwner!.finalizeTree();
886 887 888 889
    } 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
    if (firstFrameCallback != null && !sendFramesToEngine) {
899 900 901
      // This frame is deferred and not the first frame sent to the engine that
      // should be reported.
      _needToReportFirstFrame = true;
902
      SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
903
    }
904
  }
905 906 907

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

913 914 915 916 917
  bool _readyToProduceFrames = false;

  @override
  bool get framesEnabled => super.framesEnabled && _readyToProduceFrames;

918 919 920 921 922 923 924 925 926 927 928
  /// 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);
    });
  }

929 930 931 932 933
  /// Takes a widget and attaches it to the [renderViewElement], creating it if
  /// necessary.
  ///
  /// This is called by [runApp] to configure the widget tree.
  ///
934 935 936 937
  /// See also:
  ///
  ///  * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
  ///    widget and attaches it to the render tree.
938
  void attachRootWidget(Widget rootWidget) {
939
    final bool isBootstrapFrame = renderViewElement == null;
940
    _readyToProduceFrames = true;
941
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
942
      container: renderView,
943
      debugShortDescription: '[root]',
944
      child: rootWidget,
945
    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
946
    if (isBootstrapFrame) {
947
      SchedulerBinding.instance.ensureVisualUpdate();
948
    }
949
  }
950

951 952 953 954 955 956
  /// 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;

957
  @override
958
  Future<void> performReassemble() {
959 960 961 962 963
    assert(() {
      WidgetInspectorService.instance.performReassemble();
      return true;
    }());

964
    if (renderViewElement != null) {
965
      buildOwner!.reassemble(renderViewElement!, BindingBase.debugReassembleConfig);
966
    }
967
    return super.performReassemble();
968
  }
969 970 971

  /// Computes the locale the current platform would resolve to.
  ///
972 973 974 975 976
  /// This method is meant to be used as part of a
  /// [WidgetsApp.localeListResolutionCallback]. Since this method may return
  /// null, a Flutter/dart algorithm should still be provided as a fallback in
  /// case a native resolved locale cannot be determined or if the native
  /// resolved locale is undesirable.
977 978 979 980
  ///
  /// This method may return a null [Locale] if the platform does not support
  /// native locale resolution, or if the resolution failed.
  ///
981
  /// The first `supportedLocale` is treated as the default locale and will be returned
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
  /// if no better match is found.
  ///
  /// Android and iOS are currently supported.
  ///
  /// On Android, the algorithm described in
  /// https://developer.android.com/guide/topics/resources/multilingual-support
  /// is used to determine the resolved locale. Depending on the android version
  /// of the device, either the modern (>= API 24) or legacy (< API 24) algorithm
  /// will be used.
  ///
  /// On iOS, the result of `preferredLocalizationsFromArray` method of `NSBundle`
  /// is returned. See:
  /// https://developer.apple.com/documentation/foundation/nsbundle/1417249-preferredlocalizationsfromarray?language=objc
  /// for details on the used method.
  ///
  /// iOS treats script code as necessary for a match, so a user preferred locale of
  /// `zh_Hans_CN` will not resolve to a supported locale of `zh_CN`.
  ///
  /// Since implementation may vary by platform and has potential to be heavy,
  /// it is recommended to cache the results of this method if the value is
  /// used multiple times.
  ///
  /// Second-best (and n-best) matching locales should be obtained by calling this
  /// method again with the matched locale of the first call omitted from
1006
  /// `supportedLocales`.
1007
  Locale? computePlatformResolvedLocale(List<Locale> supportedLocales) {
1008
    return platformDispatcher.computePlatformResolvedLocale(supportedLocales);
1009
  }
1010
}
Hixie's avatar
Hixie committed
1011

1012
/// Inflate the given widget and attach it to the screen.
1013
///
1014 1015 1016
/// 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
1017
/// your widget, you can also use the [Center] widget.
1018
///
1019 1020 1021 1022 1023 1024
/// 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].
///
1025
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
1026 1027 1028
///
/// See also:
///
1029 1030 1031 1032 1033 1034
///  * [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
1035
void runApp(Widget app) {
1036
  WidgetsFlutterBinding.ensureInitialized()
1037
    ..scheduleAttachRootWidget(app)
1038
    ..scheduleWarmUpFrame();
Hixie's avatar
Hixie committed
1039 1040
}

1041
String _debugDumpAppString() {
1042
  const String mode = kDebugMode ? 'DEBUG MODE' : kReleaseMode ? 'RELEASE MODE' : 'PROFILE MODE';
1043 1044
  final StringBuffer buffer = StringBuffer();
  buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode');
1045 1046
  if (WidgetsBinding.instance.renderViewElement != null) {
    buffer.writeln(WidgetsBinding.instance.renderViewElement!.toStringDeep());
1047
  } else {
1048
    buffer.writeln('<no tree currently mounted>');
1049
  }
1050 1051 1052 1053 1054
  return buffer.toString();
}

/// Print a string representation of the currently running app.
void debugDumpApp() {
1055
  debugPrint(_debugDumpAppString());
1056 1057
}

1058 1059 1060 1061 1062 1063 1064 1065
/// 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
1066
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
1067 1068 1069
  /// Creates a bridge from a [RenderObject] to an [Element] tree.
  ///
  /// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
1070 1071
  RenderObjectToWidgetAdapter({
    this.child,
1072
    required this.container,
1073
    this.debugShortDescription,
1074
  }) : super(key: GlobalObjectKey(container));
Hixie's avatar
Hixie committed
1075

1076
  /// The widget below this widget in the tree.
1077
  ///
1078
  /// {@macro flutter.widgets.ProxyWidget.child}
1079
  final Widget? child;
1080

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

1084
  /// A short description of this widget used by debugging aids.
1085
  final String? debugShortDescription;
Hixie's avatar
Hixie committed
1086

1087
  @override
1088
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
Hixie's avatar
Hixie committed
1089

1090
  @override
1091
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
Hixie's avatar
Hixie committed
1092

1093
  @override
1094
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }
1095

1096 1097
  /// Inflate this widget and actually set the resulting [RenderObject] as the
  /// child of [container].
1098 1099
  ///
  /// If `element` is null, this function will create a new element. Otherwise,
1100
  /// the given element will have an update scheduled to switch to this widget.
1101 1102
  ///
  /// Used by [runApp] to bootstrap applications.
1103
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
1104 1105
    if (element == null) {
      owner.lockState(() {
1106
        element = createElement();
1107
        assert(element != null);
1108
        element!.assignOwner(owner);
1109
      });
1110 1111
      owner.buildScope(element!, () {
        element!.mount(null, null);
1112 1113
      });
    } else {
1114 1115
      element._newWidget = this;
      element.markNeedsBuild();
1116
    }
1117
    return element!;
1118
  }
1119

1120
  @override
1121
  String toStringShort() => debugShortDescription ?? super.toStringShort();
Hixie's avatar
Hixie committed
1122 1123
}

1124 1125 1126 1127 1128
/// 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
1129
///
1130 1131
/// 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
1132
/// this usage, it is normally instantiated by the bootstrapping logic in the
1133
/// [WidgetsFlutterBinding] singleton created by [runApp].
1134
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
1135 1136 1137 1138 1139
  /// 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].
1140
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> super.widget);
Hixie's avatar
Hixie committed
1141

1142
  Element? _child;
Hixie's avatar
Hixie committed
1143

1144
  static const Object _rootChildSlot = Object();
Hixie's avatar
Hixie committed
1145

1146
  @override
Hixie's avatar
Hixie committed
1147
  void visitChildren(ElementVisitor visitor) {
1148
    if (_child != null) {
1149
      visitor(_child!);
1150
    }
Hixie's avatar
Hixie committed
1151 1152
  }

1153
  @override
1154
  void forgetChild(Element child) {
1155 1156
    assert(child == _child);
    _child = null;
1157
    super.forgetChild(child);
1158 1159
  }

1160
  @override
1161
  void mount(Element? parent, Object? newSlot) {
Hixie's avatar
Hixie committed
1162
    assert(parent == null);
Hixie's avatar
Hixie committed
1163
    super.mount(parent, newSlot);
1164
    _rebuild();
1165
    assert(_child != null);
Hixie's avatar
Hixie committed
1166 1167
  }

1168
  @override
Hixie's avatar
Hixie committed
1169 1170 1171
  void update(RenderObjectToWidgetAdapter<T> newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
1172 1173 1174
    _rebuild();
  }

1175 1176
  // When we are assigned a new widget, we store it here
  // until we are ready to update to it.
1177
  Widget? _newWidget;
1178 1179 1180

  @override
  void performRebuild() {
Ian Hickson's avatar
Ian Hickson committed
1181 1182 1183
    if (_newWidget != null) {
      // _newWidget can be null if, for instance, we were rebuilt
      // due to a reassemble.
1184
      final Widget newWidget = _newWidget!;
Ian Hickson's avatar
Ian Hickson committed
1185
      _newWidget = null;
1186
      update(newWidget as RenderObjectToWidgetAdapter<T>);
Ian Hickson's avatar
Ian Hickson committed
1187
    }
1188 1189 1190 1191
    super.performRebuild();
    assert(_newWidget == null);
  }

1192
  @pragma('vm:notify-debugger-on-exception')
1193 1194
  void _rebuild() {
    try {
1195
      _child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
1196
    } catch (exception, stack) {
1197
      final FlutterErrorDetails details = FlutterErrorDetails(
1198 1199 1200
        exception: exception,
        stack: stack,
        library: 'widgets library',
1201
        context: ErrorDescription('attaching to the render tree'),
1202 1203 1204
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
1205 1206
      _child = updateChild(null, error, _rootChildSlot);
    }
Hixie's avatar
Hixie committed
1207 1208
  }

1209
  @override
1210
  RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>;
Hixie's avatar
Hixie committed
1211

1212
  @override
1213
  void insertRenderObjectChild(RenderObject child, Object? slot) {
Ian Hickson's avatar
Ian Hickson committed
1214
    assert(slot == _rootChildSlot);
1215
    assert(renderObject.debugValidateChild(child));
1216
    renderObject.child = child as T;
Hixie's avatar
Hixie committed
1217 1218
  }

1219
  @override
1220
  void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
Adam Barth's avatar
Adam Barth committed
1221 1222 1223
    assert(false);
  }

1224
  @override
1225
  void removeRenderObjectChild(RenderObject child, Object? slot) {
Hixie's avatar
Hixie committed
1226 1227 1228
    assert(renderObject.child == child);
    renderObject.child = null;
  }
Adam Barth's avatar
Adam Barth committed
1229
}
1230 1231

/// A concrete binding for applications based on the Widgets framework.
1232
///
1233
/// This is the glue that binds the framework to the Flutter engine.
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
///
/// When using the widgets framework, this binding, or one that
/// implements the same interfaces, must be used. The following
/// mixins are used to implement this binding:
///
/// * [GestureBinding], which implements the basics of hit testing.
/// * [SchedulerBinding], which introduces the concepts of frames.
/// * [ServicesBinding], which provides access to the plugin subsystem.
/// * [PaintingBinding], which enables decoding images.
/// * [SemanticsBinding], which supports accessibility.
/// * [RendererBinding], which handles the render tree.
/// * [WidgetsBinding], which handles the widget tree.
1246
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
1247 1248 1249 1250
  /// Returns an instance of the binding that implements
  /// [WidgetsBinding]. If no binding has yet been initialized, the
  /// [WidgetsFlutterBinding] class is used to create and initialize
  /// one.
1251 1252 1253
  ///
  /// You only need to call this method if you need the binding to be
  /// initialized before calling [runApp].
1254 1255 1256
  ///
  /// In the `flutter_test` framework, [testWidgets] initializes the
  /// binding instance to a [TestWidgetsFlutterBinding], not a
1257 1258
  /// [WidgetsFlutterBinding]. See
  /// [TestWidgetsFlutterBinding.ensureInitialized].
1259
  static WidgetsBinding ensureInitialized() {
1260
    if (WidgetsBinding._instance == null) {
1261
      WidgetsFlutterBinding();
1262
    }
1263
    return WidgetsBinding.instance;
1264 1265
  }
}