binding.dart 46.5 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:async';
6
import 'dart:developer' as developer;
7
import 'dart:ui' show AccessibilityFeatures, AppLifecycleState, FrameTiming, Locale, PlatformDispatcher, TimingsCallback;
8

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

15
import 'app.dart';
16
import 'debug.dart';
17
import 'focus_manager.dart';
18
import 'framework.dart';
19
import 'platform_menu_bar.dart';
20
import 'router.dart';
21
import 'service_extensions.dart';
22
import 'widget_inspector.dart';
23

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

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

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

77 78 79 80 81 82 83 84 85 86 87 88 89
  /// 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) {
90
    return didPushRoute(routeInformation.location!);
91 92
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

400
      registerServiceExtension(
401
        name: WidgetsServiceExtensions.didSendFirstFrameRasterizedEvent.name,
402 403 404 405 406 407 408 409 410 411
        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',
          };
        },
      );

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

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

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

      WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);

      return true;
    }());
467 468
  }

469
  Future<void> _forceRebuild() {
470
    if (renderViewElement != null) {
471
      buildOwner!.reassemble(renderViewElement!, null);
472 473
      return endOfFrame;
    }
474
    return Future<void>.value();
475 476
  }

477 478
  /// The [BuildOwner] in charge of executing the build pipeline for the
  /// widget tree rooted at this binding.
479
  BuildOwner? get buildOwner => _buildOwner;
480 481 482
  // Initialization of [_buildOwner] has to be done within the [initInstances]
  // method, as it requires [ServicesBinding] to properly setup the
  // [defaultBinaryMessenger] instance.
483
  BuildOwner? _buildOwner;
Ian Hickson's avatar
Ian Hickson committed
484

485
  /// The object in charge of the focus tree.
486
  ///
487 488
  /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
  /// the [FocusScopeNode] for a given [BuildContext].
489
  ///
490
  /// See [FocusManager] for more details.
491
  FocusManager get focusManager => _buildOwner!.focusManager;
492

493 494 495 496 497 498 499
  /// A delegate that communicates with a platform plugin for serializing and
  /// managing platform-rendered menu bars created by [PlatformMenuBar].
  ///
  /// This is set by default to a [DefaultPlatformMenuDelegate] instance in
  /// [initInstances].
  late PlatformMenuDelegate platformMenuDelegate;

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

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

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

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

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

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

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

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

588 589 590 591 592
  /// 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
593
  /// [PlatformDispatcher.onAccessibilityFeaturesChanged] notification is received.
594 595 596
  @protected
  @mustCallSuper
  void dispatchAccessibilityFeaturesChanged() {
597
    for (final WidgetsBindingObserver observer in _observers) {
598
      observer.didChangeAccessibilityFeatures();
599
    }
600 601
  }

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

626 627
  /// Called when the host tells the app to push a new route onto the
  /// navigator.
628 629 630 631 632 633 634 635
  ///
  /// 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].
636 637
  @protected
  @mustCallSuper
638
  Future<void> handlePushRoute(String route) async {
639
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
640
      if (await observer.didPushRoute(route)) {
641
        return;
642
      }
643 644 645
    }
  }

646
  Future<void> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async {
647
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
648 649 650 651
      if (
        await observer.didPushRouteInformation(
          RouteInformation(
            location: routeArguments['location'] as String,
652
            state: routeArguments['state'] as Object?,
653
          ),
654
        )
655 656 657
      ) {
        return;
      }
658 659 660
    }
  }

661 662 663 664 665
  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        return handlePopRoute();
      case 'pushRoute':
666
        return handlePushRoute(methodCall.arguments as String);
667 668
      case 'pushRouteInformation':
        return _handlePushRouteInformation(methodCall.arguments as Map<dynamic, dynamic>);
669
    }
670
    return Future<dynamic>.value();
671 672
  }

673
  @override
674
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
675
    super.handleAppLifecycleStateChanged(state);
676
    for (final WidgetsBindingObserver observer in _observers) {
677
      observer.didChangeAppLifecycleState(state);
678
    }
679 680
  }

681
  @override
682
  void handleMemoryPressure() {
683
    super.handleMemoryPressure();
684
    for (final WidgetsBindingObserver observer in _observers) {
685
      observer.didHaveMemoryPressure();
686
    }
687 688
  }

689
  bool _needToReportFirstFrame = true;
690 691 692 693 694

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

  /// Whether the Flutter engine has rasterized the first frame.
  ///
695 696 697
  /// 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.
698 699 700 701 702 703 704 705 706 707
  ///
  /// 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.
  ///
708 709 710
  /// 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.
711 712 713 714 715 716 717
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether this future has completed or not.
  Future<void> get waitUntilFirstFrameRasterized => _firstFrameCompleter.future;

  /// Whether the first frame has finished building.
718
  ///
719 720
  /// This value can also be obtained over the VM service protocol as
  /// `ext.flutter.didSendFirstFrameEvent`.
721 722 723 724
  ///
  /// See also:
  ///
  ///  * [firstFrameRasterized], whether the first frame has finished rendering.
725 726
  bool get debugDidSendFirstFrameEvent => !_needToReportFirstFrame;

727
  void _handleBuildScheduled() {
728 729 730 731
    // If we're in the process of building dirty elements, then changes
    // should not trigger a new frame.
    assert(() {
      if (debugBuildingDirtyElements) {
732 733 734 735
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('Build scheduled during frame.'),
          ErrorDescription(
            'While the widget tree was being built, laid out, and painted, '
736
            'a new frame was scheduled to rebuild the widget tree.',
737 738 739 740 741 742 743 744 745 746 747 748 749 750
          ),
          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.',
751
          ),
752
        ]);
753 754
      }
      return true;
755
    }());
756
    ensureVisualUpdate();
757 758
  }

759 760 761 762 763 764 765 766
  /// 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;
767

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

840
    TimingsCallback? firstFrameCallback;
841
    if (_needToReportFirstFrame) {
842
      assert(!_firstFrameCompleter.isCompleted);
843 844

      firstFrameCallback = (List<FrameTiming> timings) {
845
        assert(sendFramesToEngine);
846
        if (!kReleaseMode) {
847 848 849 850 851
          // 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();
852 853 854
          developer.Timeline.instantSync('Rasterized first useful frame');
          developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
        }
855
        SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
856
        firstFrameCallback = null;
857
        _firstFrameCompleter.complete();
858
      };
859 860 861
      // 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).
862
      SchedulerBinding.instance.addTimingsCallback(firstFrameCallback!);
863 864
    }

865
    try {
866
      if (renderViewElement != null) {
867
        buildOwner!.buildScope(renderViewElement!);
868
      }
869
      super.drawFrame();
870
      buildOwner!.finalizeTree();
871 872 873 874
    } finally {
      assert(() {
        debugBuildingDirtyElements = false;
        return true;
875
      }());
876
    }
877
    if (!kReleaseMode) {
878
      if (_needToReportFirstFrame && sendFramesToEngine) {
879
        developer.Timeline.instantSync('Widgets built first useful frame');
880
      }
881
    }
882
    _needToReportFirstFrame = false;
883
    if (firstFrameCallback != null && !sendFramesToEngine) {
884 885 886
      // This frame is deferred and not the first frame sent to the engine that
      // should be reported.
      _needToReportFirstFrame = true;
887
      SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
888
    }
889
  }
890 891 892

  /// The [Element] that is at the root of the hierarchy (and which wraps the
  /// [RenderView] object at the root of the rendering hierarchy).
893 894
  ///
  /// This is initialized the first time [runApp] is called.
895 896
  Element? get renderViewElement => _renderViewElement;
  Element? _renderViewElement;
897

898 899 900 901 902
  bool _readyToProduceFrames = false;

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

903 904 905 906 907 908 909 910 911 912 913
  /// 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);
    });
  }

914 915 916 917 918
  /// Takes a widget and attaches it to the [renderViewElement], creating it if
  /// necessary.
  ///
  /// This is called by [runApp] to configure the widget tree.
  ///
919 920 921 922
  /// See also:
  ///
  ///  * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
  ///    widget and attaches it to the render tree.
923
  void attachRootWidget(Widget rootWidget) {
924
    final bool isBootstrapFrame = renderViewElement == null;
925
    _readyToProduceFrames = true;
926
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
927
      container: renderView,
928
      debugShortDescription: '[root]',
929
      child: rootWidget,
930
    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
931
    if (isBootstrapFrame) {
932
      SchedulerBinding.instance.ensureVisualUpdate();
933
    }
934
  }
935

936 937 938 939 940 941
  /// 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;

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

949
    if (renderViewElement != null) {
950
      buildOwner!.reassemble(renderViewElement!, BindingBase.debugReassembleConfig);
951
    }
952
    return super.performReassemble();
953
  }
954 955 956

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

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

1026
String _debugDumpAppString() {
1027
  const String mode = kDebugMode ? 'DEBUG MODE' : kReleaseMode ? 'RELEASE MODE' : 'PROFILE MODE';
1028 1029
  final StringBuffer buffer = StringBuffer();
  buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode');
1030 1031
  if (WidgetsBinding.instance.renderViewElement != null) {
    buffer.writeln(WidgetsBinding.instance.renderViewElement!.toStringDeep());
1032
  } else {
1033
    buffer.writeln('<no tree currently mounted>');
1034
  }
1035 1036 1037 1038 1039
  return buffer.toString();
}

/// Print a string representation of the currently running app.
void debugDumpApp() {
1040
  debugPrint(_debugDumpAppString());
1041 1042
}

1043 1044 1045 1046 1047 1048 1049 1050
/// 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
1051
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
1052 1053 1054
  /// Creates a bridge from a [RenderObject] to an [Element] tree.
  ///
  /// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
1055 1056
  RenderObjectToWidgetAdapter({
    this.child,
1057
    required this.container,
1058
    this.debugShortDescription,
1059
  }) : super(key: GlobalObjectKey(container));
Hixie's avatar
Hixie committed
1060

1061
  /// The widget below this widget in the tree.
1062
  ///
1063
  /// {@macro flutter.widgets.ProxyWidget.child}
1064
  final Widget? child;
1065

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

1069
  /// A short description of this widget used by debugging aids.
1070
  final String? debugShortDescription;
Hixie's avatar
Hixie committed
1071

1072
  @override
1073
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
Hixie's avatar
Hixie committed
1074

1075
  @override
1076
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
Hixie's avatar
Hixie committed
1077

1078
  @override
1079
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }
1080

1081 1082
  /// Inflate this widget and actually set the resulting [RenderObject] as the
  /// child of [container].
1083 1084
  ///
  /// If `element` is null, this function will create a new element. Otherwise,
1085
  /// the given element will have an update scheduled to switch to this widget.
1086 1087
  ///
  /// Used by [runApp] to bootstrap applications.
1088
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
1089 1090
    if (element == null) {
      owner.lockState(() {
1091
        element = createElement();
1092
        assert(element != null);
1093
        element!.assignOwner(owner);
1094
      });
1095 1096
      owner.buildScope(element!, () {
        element!.mount(null, null);
1097 1098
      });
    } else {
1099 1100
      element._newWidget = this;
      element.markNeedsBuild();
1101
    }
1102
    return element!;
1103
  }
1104

1105
  @override
1106
  String toStringShort() => debugShortDescription ?? super.toStringShort();
Hixie's avatar
Hixie committed
1107 1108
}

1109 1110 1111 1112 1113
/// 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
1114
///
1115 1116
/// 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
1117
/// this usage, it is normally instantiated by the bootstrapping logic in the
1118
/// [WidgetsFlutterBinding] singleton created by [runApp].
1119
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
1120 1121 1122 1123 1124
  /// 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].
1125
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> super.widget);
Hixie's avatar
Hixie committed
1126

1127
  Element? _child;
Hixie's avatar
Hixie committed
1128

1129
  static const Object _rootChildSlot = Object();
Hixie's avatar
Hixie committed
1130

1131
  @override
Hixie's avatar
Hixie committed
1132
  void visitChildren(ElementVisitor visitor) {
1133
    if (_child != null) {
1134
      visitor(_child!);
1135
    }
Hixie's avatar
Hixie committed
1136 1137
  }

1138
  @override
1139
  void forgetChild(Element child) {
1140 1141
    assert(child == _child);
    _child = null;
1142
    super.forgetChild(child);
1143 1144
  }

1145
  @override
1146
  void mount(Element? parent, Object? newSlot) {
Hixie's avatar
Hixie committed
1147
    assert(parent == null);
Hixie's avatar
Hixie committed
1148
    super.mount(parent, newSlot);
1149
    _rebuild();
1150
    assert(_child != null);
Hixie's avatar
Hixie committed
1151 1152
  }

1153
  @override
Hixie's avatar
Hixie committed
1154 1155 1156
  void update(RenderObjectToWidgetAdapter<T> newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
1157 1158 1159
    _rebuild();
  }

1160 1161
  // When we are assigned a new widget, we store it here
  // until we are ready to update to it.
1162
  Widget? _newWidget;
1163 1164 1165

  @override
  void performRebuild() {
Ian Hickson's avatar
Ian Hickson committed
1166 1167 1168
    if (_newWidget != null) {
      // _newWidget can be null if, for instance, we were rebuilt
      // due to a reassemble.
1169
      final Widget newWidget = _newWidget!;
Ian Hickson's avatar
Ian Hickson committed
1170
      _newWidget = null;
1171
      update(newWidget as RenderObjectToWidgetAdapter<T>);
Ian Hickson's avatar
Ian Hickson committed
1172
    }
1173 1174 1175 1176
    super.performRebuild();
    assert(_newWidget == null);
  }

1177
  @pragma('vm:notify-debugger-on-exception')
1178 1179
  void _rebuild() {
    try {
1180
      _child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
1181
    } catch (exception, stack) {
1182
      final FlutterErrorDetails details = FlutterErrorDetails(
1183 1184 1185
        exception: exception,
        stack: stack,
        library: 'widgets library',
1186
        context: ErrorDescription('attaching to the render tree'),
1187 1188 1189
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
1190 1191
      _child = updateChild(null, error, _rootChildSlot);
    }
Hixie's avatar
Hixie committed
1192 1193
  }

1194
  @override
1195
  RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>;
Hixie's avatar
Hixie committed
1196

1197
  @override
1198
  void insertRenderObjectChild(RenderObject child, Object? slot) {
Ian Hickson's avatar
Ian Hickson committed
1199
    assert(slot == _rootChildSlot);
1200
    assert(renderObject.debugValidateChild(child));
1201
    renderObject.child = child as T;
Hixie's avatar
Hixie committed
1202 1203
  }

1204
  @override
1205
  void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
Adam Barth's avatar
Adam Barth committed
1206 1207 1208
    assert(false);
  }

1209
  @override
1210
  void removeRenderObjectChild(RenderObject child, Object? slot) {
Hixie's avatar
Hixie committed
1211 1212 1213
    assert(renderObject.child == child);
    renderObject.child = null;
  }
Adam Barth's avatar
Adam Barth committed
1214
}
1215 1216

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