binding.dart 46.4 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, PlatformDispatcher;
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 'router.dart';
20
import 'widget_inspector.dart';
21

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

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

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

108 109 110 111 112 113 114 115 116 117 118 119 120
  /// 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) {
121
    return didPushRoute(routeInformation.location!);
122 123
  }

124 125
  /// Called when the application's dimensions change. For example,
  /// when a phone is rotated.
126
  ///
127 128
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onMetricsChanged].
129
  ///
130
  /// {@tool snippet}
131 132 133 134 135 136
  ///
  /// This [StatefulWidget] implements the parts of the [State] and
  /// [WidgetsBindingObserver] protocols necessary to react when the device is
  /// rotated (or otherwise changes dimensions).
  ///
  /// ```dart
137
  /// class MetricsReactor extends StatefulWidget {
138
  ///   const MetricsReactor({ Key? key }) : super(key: key);
139 140
  ///
  ///   @override
141
  ///   State<MetricsReactor> createState() => _MetricsReactorState();
142 143
  /// }
  ///
144
  /// class _MetricsReactorState extends State<MetricsReactor> with WidgetsBindingObserver {
145
  ///   late Size _lastSize;
146
  ///
147 148 149
  ///   @override
  ///   void initState() {
  ///     super.initState();
150 151
  ///     _lastSize = WidgetsBinding.instance!.window.physicalSize;
  ///     WidgetsBinding.instance!.addObserver(this);
152 153 154 155
  ///   }
  ///
  ///   @override
  ///   void dispose() {
156
  ///     WidgetsBinding.instance!.removeObserver(this);
157 158 159 160 161
  ///     super.dispose();
  ///   }
  ///
  ///   @override
  ///   void didChangeMetrics() {
162
  ///     setState(() { _lastSize = WidgetsBinding.instance!.window.physicalSize; });
163 164 165 166
  ///   }
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
167
  ///     return Text('Current size: $_lastSize');
168 169 170
  ///   }
  /// }
  /// ```
171
  /// {@end-tool}
172 173 174 175 176 177 178 179 180
  ///
  /// 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.
181
  void didChangeMetrics() { }
182

183 184 185 186 187 188
  /// 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.
  ///
189 190
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onTextScaleFactorChanged].
191
  ///
192
  /// {@tool snippet}
193 194 195
  ///
  /// ```dart
  /// class TextScaleFactorReactor extends StatefulWidget {
196
  ///   const TextScaleFactorReactor({ Key? key }) : super(key: key);
197 198
  ///
  ///   @override
199
  ///   State<TextScaleFactorReactor> createState() => _TextScaleFactorReactorState();
200 201 202 203 204 205
  /// }
  ///
  /// class _TextScaleFactorReactorState extends State<TextScaleFactorReactor> with WidgetsBindingObserver {
  ///   @override
  ///   void initState() {
  ///     super.initState();
206
  ///     WidgetsBinding.instance!.addObserver(this);
207 208 209 210
  ///   }
  ///
  ///   @override
  ///   void dispose() {
211
  ///     WidgetsBinding.instance!.removeObserver(this);
212 213 214
  ///     super.dispose();
  ///   }
  ///
215
  ///   late double _lastTextScaleFactor;
216 217 218
  ///
  ///   @override
  ///   void didChangeTextScaleFactor() {
219
  ///     setState(() { _lastTextScaleFactor = WidgetsBinding.instance!.window.textScaleFactor; });
220 221 222 223
  ///   }
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
224
  ///     return Text('Current scale factor: $_lastTextScaleFactor');
225 226 227
  ///   }
  /// }
  /// ```
228
  /// {@end-tool}
229 230 231 232 233 234 235
  ///
  /// See also:
  ///
  ///  * [MediaQuery.of], which provides a similar service with less
  ///    boilerplate.
  void didChangeTextScaleFactor() { }

236 237
  /// Called when the platform brightness changes.
  ///
238 239
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged].
240 241
  void didChangePlatformBrightness() { }

242 243 244
  /// Called when the system tells the app that the user's locale has
  /// changed. For example, if the user changes the system language
  /// settings.
245
  ///
246 247
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onLocaleChanged].
248
  void didChangeLocales(List<Locale>? locales) { }
249 250 251

  /// Called when the system puts the app in the background or returns
  /// the app to the foreground.
252 253 254
  ///
  /// An example of implementing this method is provided in the class-level
  /// documentation for the [WidgetsBindingObserver] class.
255 256
  ///
  /// This method exposes notifications from [SystemChannels.lifecycle].
257
  void didChangeAppLifecycleState(AppLifecycleState state) { }
258 259

  /// Called when the system is running low on memory.
260 261 262
  ///
  /// This method exposes the `memoryPressure` notification from
  /// [SystemChannels.system].
263
  void didHaveMemoryPressure() { }
264 265 266 267

  /// Called when the system changes the set of currently active accessibility
  /// features.
  ///
268 269
  /// This method exposes notifications from
  /// [dart:ui.PlatformDispatcher.onAccessibilityFeaturesChanged].
270
  void didChangeAccessibilityFeatures() { }
Ian Hickson's avatar
Ian Hickson committed
271
}
272

273
/// The glue between the widgets layer and the Flutter engine.
274
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
275
  @override
276
  void initInstances() {
Ian Hickson's avatar
Ian Hickson committed
277 278
    super.initInstances();
    _instance = this;
279 280 281 282 283 284

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

285 286 287 288
    // Initialization of [_buildOwner] has to be done after
    // [super.initInstances] is called, as it requires [ServicesBinding] to
    // properly setup the [defaultBinaryMessenger] instance.
    _buildOwner = BuildOwner();
289
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
290 291
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
292
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
293 294 295 296
    assert(() {
      FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
      return true;
    }());
297 298
  }

299 300 301 302 303
  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
304
    const PartialStackFrame componentElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: '_firstBuild');
305
    const PartialStackFrame componentElementMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'mount');
Dan Field's avatar
Dan Field committed
306
    const PartialStackFrame statefulElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: '_firstBuild');
307
    const PartialStackFrame singleChildMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'SingleChildRenderObjectElement', method: 'mount');
Dan Field's avatar
Dan Field committed
308
    const PartialStackFrame statefulElementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: 'performRebuild');
309 310 311 312 313 314 315 316 317 318

    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
319
        componentElementFirstBuild,
320 321 322 323 324 325 326 327 328
        componentElementMount,
      ],
      replacement: replacementString,
    ));
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementUpdateChild,
        componentElementPerformRebuild,
        elementRebuild,
Dan Field's avatar
Dan Field committed
329
        componentElementFirstBuild,
330 331 332 333 334 335 336 337 338 339 340
        componentElementMount,
      ],
      replacement: replacementString,
    ));

    // StatefulElement variations
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementInflateWidget,
        elementUpdateChild,
        componentElementPerformRebuild,
Dan Field's avatar
Dan Field committed
341
        statefulElementRebuild,
342
        elementRebuild,
Dan Field's avatar
Dan Field committed
343 344
        componentElementFirstBuild,
        statefulElementFirstBuild,
345 346 347 348 349 350 351 352
        componentElementMount,
      ],
      replacement: replacementString,
    ));
    FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
      frames: <PartialStackFrame>[
        elementUpdateChild,
        componentElementPerformRebuild,
Dan Field's avatar
Dan Field committed
353
        statefulElementRebuild,
354
        elementRebuild,
Dan Field's avatar
Dan Field committed
355 356
        componentElementFirstBuild,
        statefulElementFirstBuild,
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
        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,
    ));
  }

380 381 382 383 384 385 386 387
  /// The current [WidgetsBinding], if one has been created.
  ///
  /// If you need the binding to be constructed before calling [runApp],
  /// you can ensure a Widget binding has been constructed by calling the
  /// `WidgetsFlutterBinding.ensureInitialized()` function.
  static WidgetsBinding? get instance => _instance;
  static WidgetsBinding? _instance;

388 389 390 391
  @override
  void initServiceExtensions() {
    super.initServiceExtensions();

392
    if (!kReleaseMode) {
393
      registerServiceExtension(
394
        name: 'debugDumpApp',
395 396 397 398 399
        callback: (Map<String, String> parameters) async {
          final String data = _debugDumpAppString();
          return <String, Object>{
            'data': data,
          };
400 401
        },
      );
402

403 404 405 406 407 408 409 410 411 412 413 414 415
      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();
          },
        );
      }
416 417 418 419 420

      registerServiceExtension(
        name: 'didSendFirstFrameEvent',
        callback: (_) async {
          return <String, dynamic>{
421 422 423
            // 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.
424
            'enabled': _needToReportFirstFrame ? 'false' : 'true',
425 426 427
          };
        },
      );
428

429 430 431 432 433 434 435 436 437 438 439 440 441 442
      // 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',
          };
        },
      );

443 444 445
      registerServiceExtension(
        name: 'fastReassemble',
        callback: (Map<String, Object> params) async {
446 447 448
          // 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.
449
          final String? className = params['className'] as String?;
450 451 452 453 454
          BindingBase.debugReassembleConfig = DebugReassembleConfig(widgetName: className);
          try {
            await reassembleApplication();
          } finally {
            BindingBase.debugReassembleConfig = null;
455
          }
456
          return <String, String>{'type': 'Success'};
457 458 459
        },
      );

460 461 462 463 464 465 466 467 468
      // 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;
        },
      );
469
    }
470

471
    assert(() {
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
      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();
493
          },
494
      );
495

496 497 498 499
      WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);

      return true;
    }());
500 501
  }

502
  Future<void> _forceRebuild() {
503
    if (renderViewElement != null) {
504
      buildOwner!.reassemble(renderViewElement!, null);
505 506
      return endOfFrame;
    }
507
    return Future<void>.value();
508 509
  }

510 511
  /// The [BuildOwner] in charge of executing the build pipeline for the
  /// widget tree rooted at this binding.
512
  BuildOwner? get buildOwner => _buildOwner;
513 514 515
  // Initialization of [_buildOwner] has to be done within the [initInstances]
  // method, as it requires [ServicesBinding] to properly setup the
  // [defaultBinaryMessenger] instance.
516
  BuildOwner? _buildOwner;
Ian Hickson's avatar
Ian Hickson committed
517

518
  /// The object in charge of the focus tree.
519
  ///
520 521
  /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
  /// the [FocusScopeNode] for a given [BuildContext].
522
  ///
523
  /// See [FocusManager] for more details.
524
  FocusManager get focusManager => _buildOwner!.focusManager;
525

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

528 529 530 531 532 533 534 535 536 537 538 539
  /// 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).
540 541 542 543 544
  ///
  /// See also:
  ///
  ///  * [removeObserver], to release the resources reserved by this method.
  ///  * [WidgetsBindingObserver], which has an example of using this method.
545 546 547 548 549
  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).
550 551 552 553 554
  ///
  /// See also:
  ///
  ///  * [addObserver], for the method that adds observers in the first place.
  ///  * [WidgetsBindingObserver], which has an example of using this method.
555 556
  bool removeObserver(WidgetsBindingObserver observer) => _observers.remove(observer);

557
  @override
Ian Hickson's avatar
Ian Hickson committed
558 559
  void handleMetricsChanged() {
    super.handleMetricsChanged();
560
    for (final WidgetsBindingObserver observer in _observers)
561
      observer.didChangeMetrics();
Ian Hickson's avatar
Ian Hickson committed
562 563
  }

564 565 566
  @override
  void handleTextScaleFactorChanged() {
    super.handleTextScaleFactorChanged();
567
    for (final WidgetsBindingObserver observer in _observers)
568 569 570
      observer.didChangeTextScaleFactor();
  }

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

578 579 580
  @override
  void handleAccessibilityFeaturesChanged() {
    super.handleAccessibilityFeaturesChanged();
581
    for (final WidgetsBindingObserver observer in _observers)
582 583 584
      observer.didChangeAccessibilityFeatures();
  }

585
  /// Called when the system locale changes.
586
  ///
587
  /// Calls [dispatchLocalesChanged] to notify the binding observers.
588
  ///
589
  /// See [dart:ui.PlatformDispatcher.onLocaleChanged].
590 591
  @protected
  @mustCallSuper
Ian Hickson's avatar
Ian Hickson committed
592
  void handleLocaleChanged() {
593
    dispatchLocalesChanged(window.locales);
Ian Hickson's avatar
Ian Hickson committed
594 595
  }

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

609 610 611 612 613
  /// 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
614
  /// [PlatformDispatcher.onAccessibilityFeaturesChanged] notification is received.
615 616 617
  @protected
  @mustCallSuper
  void dispatchAccessibilityFeaturesChanged() {
618
    for (final WidgetsBindingObserver observer in _observers)
619 620 621
      observer.didChangeAccessibilityFeatures();
  }

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

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

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

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

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

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

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
      super.drawFrame();
884
      buildOwner!.finalizeTree();
885 886 887 888
    } finally {
      assert(() {
        debugBuildingDirtyElements = false;
        return true;
889
      }());
890
    }
891
    if (!kReleaseMode) {
892
      if (_needToReportFirstFrame && sendFramesToEngine) {
893
        developer.Timeline.instantSync('Widgets built first useful frame');
894
      }
895
    }
896
    _needToReportFirstFrame = false;
897
    if (firstFrameCallback != null && !sendFramesToEngine) {
898 899 900
      // This frame is deferred and not the first frame sent to the engine that
      // should be reported.
      _needToReportFirstFrame = true;
901
      SchedulerBinding.instance!.removeTimingsCallback(firstFrameCallback!);
902
    }
903
  }
904 905 906

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

912 913 914 915 916
  bool _readyToProduceFrames = false;

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

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

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

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

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

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

  /// Computes the locale the current platform would resolve to.
  ///
971 972 973 974 975
  /// 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.
976 977 978 979
  ///
  /// This method may return a null [Locale] if the platform does not support
  /// native locale resolution, or if the resolution failed.
  ///
980
  /// The first `supportedLocale` is treated as the default locale and will be returned
981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
  /// 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
1005
  /// `supportedLocales`.
1006
  Locale? computePlatformResolvedLocale(List<Locale> supportedLocales) {
1007
    return window.computePlatformResolvedLocale(supportedLocales);
1008
  }
1009
}
Hixie's avatar
Hixie committed
1010

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

1040
String _debugDumpAppString() {
1041 1042
  assert(WidgetsBinding.instance != null);
  const String mode = kDebugMode ? 'DEBUG 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 1056
  final String value = _debugDumpAppString();
  debugPrint(value);
1057 1058
}

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

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

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

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

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

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

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

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

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

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

1143
  @override
1144
  RenderObjectToWidgetAdapter<T> get widget => super.widget as RenderObjectToWidgetAdapter<T>;
1145

1146
  Element? _child;
Hixie's avatar
Hixie committed
1147

1148
  static const Object _rootChildSlot = Object();
Hixie's avatar
Hixie committed
1149

1150
  @override
Hixie's avatar
Hixie committed
1151 1152
  void visitChildren(ElementVisitor visitor) {
    if (_child != null)
1153
      visitor(_child!);
Hixie's avatar
Hixie committed
1154 1155
  }

1156
  @override
1157
  void forgetChild(Element child) {
1158 1159
    assert(child == _child);
    _child = null;
1160
    super.forgetChild(child);
1161 1162
  }

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

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

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

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

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

1212
  @override
1213
  RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>;
Hixie's avatar
Hixie committed
1214

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

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

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

/// A concrete binding for applications based on the Widgets framework.
1235
///
1236
/// This is the glue that binds the framework to the Flutter engine.
1237
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
1238 1239 1240 1241 1242

  /// 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].
1243 1244 1245
  ///
  /// You only need to call this method if you need the binding to be
  /// initialized before calling [runApp].
1246 1247 1248
  ///
  /// In the `flutter_test` framework, [testWidgets] initializes the
  /// binding instance to a [TestWidgetsFlutterBinding], not a
1249
  /// [WidgetsFlutterBinding].
1250
  static WidgetsBinding ensureInitialized() {
1251
    if (WidgetsBinding.instance == null)
1252
      WidgetsFlutterBinding();
1253
    return WidgetsBinding.instance!;
1254 1255
  }
}