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

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
          final String? className = params['className'] as String?;
          void markElementsDirty(Element element) {
            if (element.widget.runtimeType.toString() == className) {
449 450 451 452
              element.markNeedsBuild();
            }
            element.visitChildElements(markElementsDirty);
          }
453 454 455
          if (renderViewElement != null) {
            markElementsDirty(renderViewElement!);
          }
456 457
          await endOfFrame;
          return <String, String>{'type': 'Success'};
458 459 460
        },
      );

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

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

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

      return true;
    }());
501 502
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

665 666 667 668 669 670
  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,
671
            state: routeArguments['state'] as Object?,
672
          ),
673 674 675 676 677 678
        )
      )
      return;
    }
  }

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

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

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

705
  bool _needToReportFirstFrame = true;
706 707 708 709 710

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

  /// Whether the Flutter engine has rasterized the first frame.
  ///
711 712 713
  /// 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.
714 715 716 717 718 719 720 721 722 723
  ///
  /// 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.
  ///
724 725 726
  /// 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.
727 728 729 730 731 732 733
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether this future has completed or not.
  Future<void> get waitUntilFirstFrameRasterized => _firstFrameCompleter.future;

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

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

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

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

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

      firstFrameCallback = (List<FrameTiming> timings) {
861
        assert(sendFramesToEngine);
862 863 864 865
        if (!kReleaseMode) {
          developer.Timeline.instantSync('Rasterized first useful frame');
          developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
        }
866
        SchedulerBinding.instance!.removeTimingsCallback(firstFrameCallback!);
867
        firstFrameCallback = null;
868
        _firstFrameCompleter.complete();
869
      };
870 871 872
      // 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).
873
      SchedulerBinding.instance!.addTimingsCallback(firstFrameCallback!);
874 875
    }

876
    try {
877
      if (renderViewElement != null)
878
        buildOwner!.buildScope(renderViewElement!);
879
      super.drawFrame();
880
      buildOwner!.finalizeTree();
881 882 883 884
    } finally {
      assert(() {
        debugBuildingDirtyElements = false;
        return true;
885
      }());
886
    }
887
    if (!kReleaseMode) {
888
      if (_needToReportFirstFrame && sendFramesToEngine) {
889
        developer.Timeline.instantSync('Widgets built first useful frame');
890
      }
891
    }
892
    _needToReportFirstFrame = false;
893
    if (firstFrameCallback != null && !sendFramesToEngine) {
894 895 896
      // This frame is deferred and not the first frame sent to the engine that
      // should be reported.
      _needToReportFirstFrame = true;
897
      SchedulerBinding.instance!.removeTimingsCallback(firstFrameCallback!);
898
    }
899
  }
900 901 902

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

908 909 910 911 912
  bool _readyToProduceFrames = false;

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

913 914 915 916 917 918 919 920 921 922 923
  /// 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);
    });
  }

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

946 947 948 949 950 951
  /// 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;

952
  @override
953
  Future<void> performReassemble() {
954 955 956 957 958
    assert(() {
      WidgetInspectorService.instance.performReassemble();
      return true;
    }());

959
    if (renderViewElement != null)
960
      buildOwner!.reassemble(renderViewElement!);
961
    return super.performReassemble();
962
  }
963 964 965

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

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

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

/// Print a string representation of the currently running app.
void debugDumpApp() {
  final String value = _debugDumpAppString();
  debugPrint(value);
1052 1053
}

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

1072
  /// The widget below this widget in the tree.
1073
  ///
1074
  /// {@macro flutter.widgets.ProxyWidget.child}
1075
  final Widget? child;
1076

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

1080
  /// A short description of this widget used by debugging aids.
1081
  final String? debugShortDescription;
Hixie's avatar
Hixie committed
1082

1083
  @override
1084
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
Hixie's avatar
Hixie committed
1085

1086
  @override
1087
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
Hixie's avatar
Hixie committed
1088

1089
  @override
1090
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }
1091

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

1116
  @override
1117
  String toStringShort() => debugShortDescription ?? super.toStringShort();
Hixie's avatar
Hixie committed
1118 1119
}

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

1138
  @override
1139
  RenderObjectToWidgetAdapter<T> get widget => super.widget as RenderObjectToWidgetAdapter<T>;
1140

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

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

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

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

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

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

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

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

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

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

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

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

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

/// A concrete binding for applications based on the Widgets framework.
1230
///
1231
/// This is the glue that binds the framework to the Flutter engine.
1232
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
1233 1234 1235 1236 1237

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