1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6import 'dart:developer' as developer;
7import 'dart:ui' show AccessibilityFeatures, AppExitResponse, AppLifecycleState, FrameTiming, Locale, PlatformDispatcher, TimingsCallback;
8
9import 'package:flutter/foundation.dart';
10import 'package:flutter/gestures.dart';
11import 'package:flutter/rendering.dart';
12import 'package:flutter/scheduler.dart';
13import 'package:flutter/services.dart';
14
15import 'app.dart';
16import 'debug.dart';
17import 'focus_manager.dart';
18import 'framework.dart';
19import 'platform_menu_bar.dart';
20import 'router.dart';
21import 'service_extensions.dart';
22import 'view.dart';
23import 'widget_inspector.dart';
24
25export 'dart:ui' show AppLifecycleState, Locale;
26
27/// Interface for classes that register with the Widgets layer binding.
28///
29/// This can be used by any class, not just widgets. It provides an interface
30/// which is used by [WidgetsBinding.addObserver] and
31/// [WidgetsBinding.removeObserver] to notify objects of changes in the
32/// environment, such as changes to the device metrics or accessibility
33/// settings. It is used to implement features such as [MediaQuery].
34///
35/// This class can be extended directly, or mixed in, to get default behaviors
36/// for all of the handlers. Alternatively it can be used with the
37/// `implements` keyword, in which case all the handlers must be implemented
38/// (and the analyzer will list those that have been omitted).
39///
40/// To start receiving notifications, call `WidgetsBinding.instance.addObserver`
41/// with a reference to the object implementing the [WidgetsBindingObserver]
42/// interface. To avoid memory leaks, call
43/// `WidgetsBinding.instance.removeObserver` to unregister the object when it
44/// reaches the end of its lifecycle.
45///
46/// {@tool dartpad}
47/// This sample shows how to implement parts of the [State] and
48/// [WidgetsBindingObserver] protocols necessary to react to application
49/// lifecycle messages. See [didChangeAppLifecycleState].
50///
51/// To respond to other notifications, replace the [didChangeAppLifecycleState]
52/// method in this example with other methods from this class.
53///
54/// ** See code in examples/api/lib/widgets/binding/widget_binding_observer.0.dart **
55/// {@end-tool}
56abstract mixin class WidgetsBindingObserver {
57 /// Called when the system tells the app to pop the current route, such as
58 /// after a system back button press or back gesture.
59 ///
60 /// Observers are notified in registration order until one returns
61 /// true. If none return true, the application quits.
62 ///
63 /// Observers are expected to return true if they were able to
64 /// handle the notification, for example by closing an active dialog
65 /// box, and false otherwise. The [WidgetsApp] widget uses this
66 /// mechanism to notify the [Navigator] widget that it should pop
67 /// its current route if possible.
68 ///
69 /// This method exposes the `popRoute` notification from
70 /// [SystemChannels.navigation].
71 ///
72 /// {@macro flutter.widgets.AndroidPredictiveBack}
73 Future<bool> didPopRoute() => Future<bool>.value(false);
74
75 /// Called when the host tells the application to push a new route onto the
76 /// navigator.
77 ///
78 /// Observers are expected to return true if they were able to
79 /// handle the notification. Observers are notified in registration
80 /// order until one returns true.
81 ///
82 /// This method exposes the `pushRoute` notification from
83 /// [SystemChannels.navigation].
84 @Deprecated(
85 'Use didPushRouteInformation instead. '
86 'This feature was deprecated after v3.8.0-14.0.pre.'
87 )
88 Future<bool> didPushRoute(String route) => Future<bool>.value(false);
89
90 /// Called when the host tells the application to push a new
91 /// [RouteInformation] and a restoration state onto the router.
92 ///
93 /// Observers are expected to return true if they were able to
94 /// handle the notification. Observers are notified in registration
95 /// order until one returns true.
96 ///
97 /// This method exposes the `pushRouteInformation` notification from
98 /// [SystemChannels.navigation].
99 ///
100 /// The default implementation is to call the [didPushRoute] directly with the
101 /// string constructed from [RouteInformation.uri]'s path and query parameters.
102 // TODO(chunhtai): remove the default implementation once `didPushRoute` is
103 // removed.
104 Future<bool> didPushRouteInformation(RouteInformation routeInformation) {
105 final Uri uri = routeInformation.uri;
106 return didPushRoute(
107 Uri.decodeComponent(
108 Uri(
109 path: uri.path.isEmpty ? '/' : uri.path,
110 queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
111 fragment: uri.fragment.isEmpty ? null : uri.fragment,
112 ).toString(),
113 ),
114 );
115 }
116
117 /// Called when the application's dimensions change. For example,
118 /// when a phone is rotated.
119 ///
120 /// This method exposes notifications from
121 /// [dart:ui.PlatformDispatcher.onMetricsChanged].
122 ///
123 /// {@tool snippet}
124 ///
125 /// This [StatefulWidget] implements the parts of the [State] and
126 /// [WidgetsBindingObserver] protocols necessary to react when the device is
127 /// rotated (or otherwise changes dimensions).
128 ///
129 /// ```dart
130 /// class MetricsReactor extends StatefulWidget {
131 /// const MetricsReactor({ super.key });
132 ///
133 /// @override
134 /// State<MetricsReactor> createState() => _MetricsReactorState();
135 /// }
136 ///
137 /// class _MetricsReactorState extends State<MetricsReactor> with WidgetsBindingObserver {
138 /// late Size _lastSize;
139 ///
140 /// @override
141 /// void initState() {
142 /// super.initState();
143 /// WidgetsBinding.instance.addObserver(this);
144 /// }
145 ///
146 /// @override
147 /// void didChangeDependencies() {
148 /// super.didChangeDependencies();
149 /// // [View.of] exposes the view from `WidgetsBinding.instance.platformDispatcher.views`
150 /// // into which this widget is drawn.
151 /// _lastSize = View.of(context).physicalSize;
152 /// }
153 ///
154 /// @override
155 /// void dispose() {
156 /// WidgetsBinding.instance.removeObserver(this);
157 /// super.dispose();
158 /// }
159 ///
160 /// @override
161 /// void didChangeMetrics() {
162 /// setState(() { _lastSize = View.of(context).physicalSize; });
163 /// }
164 ///
165 /// @override
166 /// Widget build(BuildContext context) {
167 /// return Text('Current size: $_lastSize');
168 /// }
169 /// }
170 /// ```
171 /// {@end-tool}
172 ///
173 /// In general, this is unnecessary as the layout system takes care of
174 /// automatically recomputing the application geometry when the application
175 /// size changes.
176 ///
177 /// See also:
178 ///
179 /// * [MediaQuery.sizeOf], which provides a similar service with less
180 /// boilerplate.
181 void didChangeMetrics() { }
182
183 /// Called when the platform's text scale factor changes.
184 ///
185 /// This typically happens as the result of the user changing system
186 /// preferences, and it should affect all of the text sizes in the
187 /// application.
188 ///
189 /// This method exposes notifications from
190 /// [dart:ui.PlatformDispatcher.onTextScaleFactorChanged].
191 ///
192 /// {@tool snippet}
193 ///
194 /// ```dart
195 /// class TextScaleFactorReactor extends StatefulWidget {
196 /// const TextScaleFactorReactor({ super.key });
197 ///
198 /// @override
199 /// State<TextScaleFactorReactor> createState() => _TextScaleFactorReactorState();
200 /// }
201 ///
202 /// class _TextScaleFactorReactorState extends State<TextScaleFactorReactor> with WidgetsBindingObserver {
203 /// @override
204 /// void initState() {
205 /// super.initState();
206 /// WidgetsBinding.instance.addObserver(this);
207 /// }
208 ///
209 /// @override
210 /// void dispose() {
211 /// WidgetsBinding.instance.removeObserver(this);
212 /// super.dispose();
213 /// }
214 ///
215 /// late double _lastTextScaleFactor;
216 ///
217 /// @override
218 /// void didChangeTextScaleFactor() {
219 /// setState(() { _lastTextScaleFactor = WidgetsBinding.instance.platformDispatcher.textScaleFactor; });
220 /// }
221 ///
222 /// @override
223 /// Widget build(BuildContext context) {
224 /// return Text('Current scale factor: $_lastTextScaleFactor');
225 /// }
226 /// }
227 /// ```
228 /// {@end-tool}
229 ///
230 /// See also:
231 ///
232 /// * [MediaQuery.textScaleFactorOf], which provides a similar service with less
233 /// boilerplate.
234 void didChangeTextScaleFactor() { }
235
236 /// Called when the platform brightness changes.
237 ///
238 /// This method exposes notifications from
239 /// [dart:ui.PlatformDispatcher.onPlatformBrightnessChanged].
240 ///
241 /// See also:
242 ///
243 /// * [MediaQuery.platformBrightnessOf], which provides a similar service with
244 /// less boilerplate.
245 void didChangePlatformBrightness() { }
246
247 /// Called when the system tells the app that the user's locale has
248 /// changed. For example, if the user changes the system language
249 /// settings.
250 ///
251 /// This method exposes notifications from
252 /// [dart:ui.PlatformDispatcher.onLocaleChanged].
253 void didChangeLocales(List<Locale>? locales) { }
254
255 /// Called when the system puts the app in the background or returns
256 /// the app to the foreground.
257 ///
258 /// An example of implementing this method is provided in the class-level
259 /// documentation for the [WidgetsBindingObserver] class.
260 ///
261 /// This method exposes notifications from [SystemChannels.lifecycle].
262 ///
263 /// See also:
264 ///
265 /// * [AppLifecycleListener], an alternative API for responding to
266 /// application lifecycle changes.
267 void didChangeAppLifecycleState(AppLifecycleState state) { }
268
269 /// Called when a request is received from the system to exit the application.
270 ///
271 /// If any observer responds with [AppExitResponse.cancel], it will cancel the
272 /// exit. All observers will be asked before exiting.
273 ///
274 /// {@macro flutter.services.binding.ServicesBinding.requestAppExit}
275 ///
276 /// See also:
277 ///
278 /// * [ServicesBinding.exitApplication] for a function to call that will request
279 /// that the application exits.
280 Future<AppExitResponse> didRequestAppExit() async {
281 return AppExitResponse.exit;
282 }
283
284 /// Called when the system is running low on memory.
285 ///
286 /// This method exposes the `memoryPressure` notification from
287 /// [SystemChannels.system].
288 void didHaveMemoryPressure() { }
289
290 /// Called when the system changes the set of currently active accessibility
291 /// features.
292 ///
293 /// This method exposes notifications from
294 /// [dart:ui.PlatformDispatcher.onAccessibilityFeaturesChanged].
295 void didChangeAccessibilityFeatures() { }
296}
297
298/// The glue between the widgets layer and the Flutter engine.
299///
300/// The [WidgetsBinding] manages a single [Element] tree rooted at [rootElement].
301/// Calling [runApp] (which indirectly calls [attachRootWidget]) bootstraps that
302/// element tree.
303///
304/// ## Relationship to render trees
305///
306/// Multiple render trees may be associated with the element tree. Those are
307/// managed by the underlying [RendererBinding].
308///
309/// The element tree is segmented into two types of zones: rendering zones and
310/// non-rendering zones.
311///
312/// A rendering zone is a part of the element tree that is backed by a render
313/// tree and it describes the pixels that are drawn on screen. For elements in
314/// this zone, [Element.renderObject] never returns null because the elements
315/// are all associated with [RenderObject]s. Almost all widgets can be placed in
316/// a rendering zone; notable exceptions are the [View] widget, [ViewCollection]
317/// widget, and [RootWidget].
318///
319/// A non-rendering zone is a part of the element tree that is not backed by a
320/// render tree. For elements in this zone, [Element.renderObject] returns null
321/// because the elements are not associated with any [RenderObject]s. Only
322/// widgets that do not produce a [RenderObject] can be used in this zone
323/// because there is no render tree to attach the render object to. In other
324/// words, [RenderObjectWidget]s cannot be used in this zone. Typically, one
325/// would find [InheritedWidget]s, [View]s, and [ViewCollection]s in this zone
326/// to inject data across rendering zones into the tree and to organize the
327/// rendering zones (and by extension their associated render trees) into a
328/// unified element tree.
329///
330/// The root of the element tree at [rootElement] starts a non-rendering zone.
331/// Within a non-rendering zone, the [View] widget is used to start a rendering
332/// zone by bootstrapping a render tree. Within a rendering zone, the
333/// [ViewAnchor] can be used to start a new non-rendering zone.
334///
335// TODO(goderbauer): Include an example graph showcasing the different zones.
336///
337/// To figure out if an element is in a rendering zone it may walk up the tree
338/// calling [Element.debugExpectsRenderObjectForSlot] on its ancestors. If it
339/// reaches an element that returns false, it is in a non-rendering zone. If it
340/// reaches a [RenderObjectElement] ancestor it is in a rendering zone.
341mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
342 @override
343 void initInstances() {
344 super.initInstances();
345 _instance = this;
346
347 assert(() {
348 _debugAddStackFilters();
349 return true;
350 }());
351
352 // Initialization of [_buildOwner] has to be done after
353 // [super.initInstances] is called, as it requires [ServicesBinding] to
354 // properly setup the [defaultBinaryMessenger] instance.
355 _buildOwner = BuildOwner();
356 buildOwner!.onBuildScheduled = _handleBuildScheduled;
357 platformDispatcher.onLocaleChanged = handleLocaleChanged;
358 SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
359 assert(() {
360 FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
361 return true;
362 }());
363 platformMenuDelegate = DefaultPlatformMenuDelegate();
364 }
365
366 /// The current [WidgetsBinding], if one has been created.
367 ///
368 /// Provides access to the features exposed by this mixin. The binding must
369 /// be initialized before using this getter; this is typically done by calling
370 /// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
371 static WidgetsBinding get instance => BindingBase.checkInstance(_instance);
372 static WidgetsBinding? _instance;
373
374 void _debugAddStackFilters() {
375 const PartialStackFrame elementInflateWidget = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'inflateWidget');
376 const PartialStackFrame elementUpdateChild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'updateChild');
377 const PartialStackFrame elementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'rebuild');
378 const PartialStackFrame componentElementPerformRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'performRebuild');
379 const PartialStackFrame componentElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: '_firstBuild');
380 const PartialStackFrame componentElementMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'mount');
381 const PartialStackFrame statefulElementFirstBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: '_firstBuild');
382 const PartialStackFrame singleChildMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'SingleChildRenderObjectElement', method: 'mount');
383 const PartialStackFrame statefulElementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: 'performRebuild');
384
385 const String replacementString = '... Normal element mounting';
386
387 // ComponentElement variations
388 FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
389 frames: <PartialStackFrame>[
390 elementInflateWidget,
391 elementUpdateChild,
392 componentElementPerformRebuild,
393 elementRebuild,
394 componentElementFirstBuild,
395 componentElementMount,
396 ],
397 replacement: replacementString,
398 ));
399 FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
400 frames: <PartialStackFrame>[
401 elementUpdateChild,
402 componentElementPerformRebuild,
403 elementRebuild,
404 componentElementFirstBuild,
405 componentElementMount,
406 ],
407 replacement: replacementString,
408 ));
409
410 // StatefulElement variations
411 FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
412 frames: <PartialStackFrame>[
413 elementInflateWidget,
414 elementUpdateChild,
415 componentElementPerformRebuild,
416 statefulElementRebuild,
417 elementRebuild,
418 componentElementFirstBuild,
419 statefulElementFirstBuild,
420 componentElementMount,
421 ],
422 replacement: replacementString,
423 ));
424 FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
425 frames: <PartialStackFrame>[
426 elementUpdateChild,
427 componentElementPerformRebuild,
428 statefulElementRebuild,
429 elementRebuild,
430 componentElementFirstBuild,
431 statefulElementFirstBuild,
432 componentElementMount,
433 ],
434 replacement: replacementString,
435 ));
436
437 // SingleChildRenderObjectElement variations
438 FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
439 frames: <PartialStackFrame>[
440 elementInflateWidget,
441 elementUpdateChild,
442 singleChildMount,
443 ],
444 replacement: replacementString,
445 ));
446 FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
447 frames: <PartialStackFrame>[
448 elementUpdateChild,
449 singleChildMount,
450 ],
451 replacement: replacementString,
452 ));
453 }
454
455 @override
456 void initServiceExtensions() {
457 super.initServiceExtensions();
458
459 if (!kReleaseMode) {
460 registerServiceExtension(
461 name: WidgetsServiceExtensions.debugDumpApp.name,
462 callback: (Map<String, String> parameters) async {
463 final String data = _debugDumpAppString();
464 return <String, Object>{
465 'data': data,
466 };
467 },
468 );
469
470 registerServiceExtension(
471 name: WidgetsServiceExtensions.debugDumpFocusTree.name,
472 callback: (Map<String, String> parameters) async {
473 final String data = focusManager.toStringDeep();
474 return <String, Object>{
475 'data': data,
476 };
477 },
478 );
479
480 if (!kIsWeb) {
481 registerBoolServiceExtension(
482 name: WidgetsServiceExtensions.showPerformanceOverlay.name,
483 getter: () =>
484 Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
485 setter: (bool value) {
486 if (WidgetsApp.showPerformanceOverlayOverride == value) {
487 return Future<void>.value();
488 }
489 WidgetsApp.showPerformanceOverlayOverride = value;
490 return _forceRebuild();
491 },
492 );
493 }
494
495 registerServiceExtension(
496 name: WidgetsServiceExtensions.didSendFirstFrameEvent.name,
497 callback: (_) async {
498 return <String, dynamic>{
499 // This is defined to return a STRING, not a boolean.
500 // Devtools, the Intellij plugin, and the flutter tool all depend
501 // on it returning a string and not a boolean.
502 'enabled': _needToReportFirstFrame ? 'false' : 'true',
503 };
504 },
505 );
506
507 registerServiceExtension(
508 name: WidgetsServiceExtensions.didSendFirstFrameRasterizedEvent.name,
509 callback: (_) async {
510 return <String, dynamic>{
511 // This is defined to return a STRING, not a boolean.
512 // Devtools, the Intellij plugin, and the flutter tool all depend
513 // on it returning a string and not a boolean.
514 'enabled': firstFrameRasterized ? 'true' : 'false',
515 };
516 },
517 );
518
519 // Expose the ability to send Widget rebuilds as [Timeline] events.
520 registerBoolServiceExtension(
521 name: WidgetsServiceExtensions.profileWidgetBuilds.name,
522 getter: () async => debugProfileBuildsEnabled,
523 setter: (bool value) async {
524 debugProfileBuildsEnabled = value;
525 }
526 );
527 registerBoolServiceExtension(
528 name: WidgetsServiceExtensions.profileUserWidgetBuilds.name,
529 getter: () async => debugProfileBuildsEnabledUserWidgets,
530 setter: (bool value) async {
531 debugProfileBuildsEnabledUserWidgets = value;
532 }
533 );
534 }
535
536 assert(() {
537 registerBoolServiceExtension(
538 name: WidgetsServiceExtensions.debugAllowBanner.name,
539 getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
540 setter: (bool value) {
541 if (WidgetsApp.debugAllowBannerOverride == value) {
542 return Future<void>.value();
543 }
544 WidgetsApp.debugAllowBannerOverride = value;
545 return _forceRebuild();
546 },
547 );
548
549 WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);
550
551 return true;
552 }());
553 }
554
555 Future<void> _forceRebuild() {
556 if (rootElement != null) {
557 buildOwner!.reassemble(rootElement!);
558 return endOfFrame;
559 }
560 return Future<void>.value();
561 }
562
563 /// The [BuildOwner] in charge of executing the build pipeline for the
564 /// widget tree rooted at this binding.
565 BuildOwner? get buildOwner => _buildOwner;
566 // Initialization of [_buildOwner] has to be done within the [initInstances]
567 // method, as it requires [ServicesBinding] to properly setup the
568 // [defaultBinaryMessenger] instance.
569 BuildOwner? _buildOwner;
570
571 /// The object in charge of the focus tree.
572 ///
573 /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
574 /// the [FocusScopeNode] for a given [BuildContext].
575 ///
576 /// See [FocusManager] for more details.
577 FocusManager get focusManager => _buildOwner!.focusManager;
578
579 /// A delegate that communicates with a platform plugin for serializing and
580 /// managing platform-rendered menu bars created by [PlatformMenuBar].
581 ///
582 /// This is set by default to a [DefaultPlatformMenuDelegate] instance in
583 /// [initInstances].
584 late PlatformMenuDelegate platformMenuDelegate;
585
586 final List<WidgetsBindingObserver> _observers = <WidgetsBindingObserver>[];
587
588 /// Registers the given object as a binding observer. Binding
589 /// observers are notified when various application events occur,
590 /// for example when the system locale changes. Generally, one
591 /// widget in the widget tree registers itself as a binding
592 /// observer, and converts the system state into inherited widgets.
593 ///
594 /// For example, the [WidgetsApp] widget registers as a binding
595 /// observer and passes the screen size to a [MediaQuery] widget
596 /// each time it is built, which enables other widgets to use the
597 /// [MediaQuery.sizeOf] static method and (implicitly) the
598 /// [InheritedWidget] mechanism to be notified whenever the screen
599 /// size changes (e.g. whenever the screen rotates).
600 ///
601 /// See also:
602 ///
603 /// * [removeObserver], to release the resources reserved by this method.
604 /// * [WidgetsBindingObserver], which has an example of using this method.
605 void addObserver(WidgetsBindingObserver observer) => _observers.add(observer);
606
607 /// Unregisters the given observer. This should be used sparingly as
608 /// it is relatively expensive (O(N) in the number of registered
609 /// observers).
610 ///
611 /// See also:
612 ///
613 /// * [addObserver], for the method that adds observers in the first place.
614 /// * [WidgetsBindingObserver], which has an example of using this method.
615 bool removeObserver(WidgetsBindingObserver observer) => _observers.remove(observer);
616
617 @override
618 Future<AppExitResponse> handleRequestAppExit() async {
619 bool didCancel = false;
620 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
621 if ((await observer.didRequestAppExit()) == AppExitResponse.cancel) {
622 didCancel = true;
623 // Don't early return. For the case where someone is just using the
624 // observer to know when exit happens, we want to call all the
625 // observers, even if we already know we're going to cancel.
626 }
627 }
628 return didCancel ? AppExitResponse.cancel : AppExitResponse.exit;
629 }
630
631 @override
632 void handleMetricsChanged() {
633 super.handleMetricsChanged();
634 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
635 observer.didChangeMetrics();
636 }
637 }
638
639 @override
640 void handleTextScaleFactorChanged() {
641 super.handleTextScaleFactorChanged();
642 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
643 observer.didChangeTextScaleFactor();
644 }
645 }
646
647 @override
648 void handlePlatformBrightnessChanged() {
649 super.handlePlatformBrightnessChanged();
650 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
651 observer.didChangePlatformBrightness();
652 }
653 }
654
655 @override
656 void handleAccessibilityFeaturesChanged() {
657 super.handleAccessibilityFeaturesChanged();
658 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
659 observer.didChangeAccessibilityFeatures();
660 }
661 }
662
663 /// Called when the system locale changes.
664 ///
665 /// Calls [dispatchLocalesChanged] to notify the binding observers.
666 ///
667 /// See [dart:ui.PlatformDispatcher.onLocaleChanged].
668 @protected
669 @mustCallSuper
670 @visibleForTesting
671 void handleLocaleChanged() {
672 dispatchLocalesChanged(platformDispatcher.locales);
673 }
674
675 /// Notify all the observers that the locale has changed (using
676 /// [WidgetsBindingObserver.didChangeLocales]), giving them the
677 /// `locales` argument.
678 ///
679 /// This is called by [handleLocaleChanged] when the
680 /// [PlatformDispatcher.onLocaleChanged] notification is received.
681 @protected
682 @mustCallSuper
683 void dispatchLocalesChanged(List<Locale>? locales) {
684 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
685 observer.didChangeLocales(locales);
686 }
687 }
688
689 /// Notify all the observers that the active set of [AccessibilityFeatures]
690 /// has changed (using [WidgetsBindingObserver.didChangeAccessibilityFeatures]),
691 /// giving them the `features` argument.
692 ///
693 /// This is called by [handleAccessibilityFeaturesChanged] when the
694 /// [PlatformDispatcher.onAccessibilityFeaturesChanged] notification is received.
695 @protected
696 @mustCallSuper
697 void dispatchAccessibilityFeaturesChanged() {
698 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
699 observer.didChangeAccessibilityFeatures();
700 }
701 }
702
703 /// Called when the system pops the current route.
704 ///
705 /// This first notifies the binding observers (using
706 /// [WidgetsBindingObserver.didPopRoute]), in registration order, until one
707 /// returns true, meaning that it was able to handle the request (e.g. by
708 /// closing a dialog box). If none return true, then the application is shut
709 /// down by calling [SystemNavigator.pop].
710 ///
711 /// [WidgetsApp] uses this in conjunction with a [Navigator] to
712 /// cause the back button to close dialog boxes, return from modal
713 /// pages, and so forth.
714 ///
715 /// This method exposes the `popRoute` notification from
716 /// [SystemChannels.navigation].
717 ///
718 /// {@template flutter.widgets.AndroidPredictiveBack}
719 /// ## Handling backs ahead of time
720 ///
721 /// Not all system backs will result in a call to this method. Some are
722 /// handled entirely by the system without informing the Flutter framework.
723 ///
724 /// Android API 33+ introduced a feature called predictive back, which allows
725 /// the user to peek behind the current app or route during a back gesture and
726 /// then decide to cancel or commit the back. Flutter enables or disables this
727 /// feature ahead of time, before a back gesture occurs, and back gestures
728 /// that trigger predictive back are handled entirely by the system and do not
729 /// trigger this method here in the framework.
730 ///
731 /// By default, the framework communicates when it would like to handle system
732 /// back gestures using [SystemNavigator.setFrameworkHandlesBack] in
733 /// [WidgetsApp]. This is done automatically based on the status of the
734 /// [Navigator] stack and the state of any [PopScope] widgets present.
735 /// Developers can manually set this by calling the method directly or by
736 /// using [NavigationNotification].
737 /// {@endtemplate}
738 @protected
739 @visibleForTesting
740 Future<void> handlePopRoute() async {
741 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
742 if (await observer.didPopRoute()) {
743 return;
744 }
745 }
746 SystemNavigator.pop();
747 }
748
749 /// Called when the host tells the app to push a new route onto the
750 /// navigator.
751 ///
752 /// This notifies the binding observers (using
753 /// [WidgetsBindingObserver.didPushRoute]), in registration order, until one
754 /// returns true, meaning that it was able to handle the request (e.g. by
755 /// opening a dialog box). If none return true, then nothing happens.
756 ///
757 /// This method exposes the `pushRoute` notification from
758 /// [SystemChannels.navigation].
759 @protected
760 @mustCallSuper
761 @visibleForTesting
762 Future<void> handlePushRoute(String route) async {
763 final RouteInformation routeInformation = RouteInformation(uri: Uri.parse(route));
764 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
765 if (await observer.didPushRouteInformation(routeInformation)) {
766 return;
767 }
768 }
769 }
770
771 Future<void> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async {
772 final RouteInformation routeInformation = RouteInformation(
773 uri: Uri.parse(routeArguments['location'] as String),
774 state: routeArguments['state'] as Object?,
775 );
776 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
777 if (await observer.didPushRouteInformation(routeInformation)) {
778 return;
779 }
780 }
781 }
782
783 Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
784 switch (methodCall.method) {
785 case 'popRoute':
786 return handlePopRoute();
787 case 'pushRoute':
788 return handlePushRoute(methodCall.arguments as String);
789 case 'pushRouteInformation':
790 return _handlePushRouteInformation(methodCall.arguments as Map<dynamic, dynamic>);
791 }
792 return Future<dynamic>.value();
793 }
794
795 @override
796 void handleAppLifecycleStateChanged(AppLifecycleState state) {
797 super.handleAppLifecycleStateChanged(state);
798 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
799 observer.didChangeAppLifecycleState(state);
800 }
801 }
802
803 @override
804 void handleMemoryPressure() {
805 super.handleMemoryPressure();
806 for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
807 observer.didHaveMemoryPressure();
808 }
809 }
810
811 bool _needToReportFirstFrame = true;
812
813 final Completer<void> _firstFrameCompleter = Completer<void>();
814
815 /// Whether the Flutter engine has rasterized the first frame.
816 ///
817 /// Usually, the time that a frame is rasterized is very close to the time that
818 /// it gets presented on the display. Specifically, rasterization is the last
819 /// expensive phase of a frame that's still in Flutter's control.
820 ///
821 /// See also:
822 ///
823 /// * [waitUntilFirstFrameRasterized], the future when [firstFrameRasterized]
824 /// becomes true.
825 bool get firstFrameRasterized => _firstFrameCompleter.isCompleted;
826
827 /// A future that completes when the Flutter engine has rasterized the first
828 /// frame.
829 ///
830 /// Usually, the time that a frame is rasterized is very close to the time that
831 /// it gets presented on the display. Specifically, rasterization is the last
832 /// expensive phase of a frame that's still in Flutter's control.
833 ///
834 /// See also:
835 ///
836 /// * [firstFrameRasterized], whether this future has completed or not.
837 Future<void> get waitUntilFirstFrameRasterized => _firstFrameCompleter.future;
838
839 /// Whether the first frame has finished building.
840 ///
841 /// This value can also be obtained over the VM service protocol as
842 /// `ext.flutter.didSendFirstFrameEvent`.
843 ///
844 /// See also:
845 ///
846 /// * [firstFrameRasterized], whether the first frame has finished rendering.
847 bool get debugDidSendFirstFrameEvent => !_needToReportFirstFrame;
848
849 void _handleBuildScheduled() {
850 // If we're in the process of building dirty elements, then changes
851 // should not trigger a new frame.
852 assert(() {
853 if (debugBuildingDirtyElements) {
854 throw FlutterError.fromParts(<DiagnosticsNode>[
855 ErrorSummary('Build scheduled during frame.'),
856 ErrorDescription(
857 'While the widget tree was being built, laid out, and painted, '
858 'a new frame was scheduled to rebuild the widget tree.',
859 ),
860 ErrorHint(
861 'This might be because setState() was called from a layout or '
862 'paint callback. '
863 'If a change is needed to the widget tree, it should be applied '
864 'as the tree is being built. Scheduling a change for the subsequent '
865 'frame instead results in an interface that lags behind by one frame. '
866 'If this was done to make your build dependent on a size measured at '
867 'layout time, consider using a LayoutBuilder, CustomSingleChildLayout, '
868 'or CustomMultiChildLayout. If, on the other hand, the one frame delay '
869 'is the desired effect, for example because this is an '
870 'animation, consider scheduling the frame in a post-frame callback '
871 'using SchedulerBinding.addPostFrameCallback or '
872 'using an AnimationController to trigger the animation.',
873 ),
874 ]);
875 }
876 return true;
877 }());
878 ensureVisualUpdate();
879 }
880
881 /// Whether we are currently in a frame. This is used to verify
882 /// that frames are not scheduled redundantly.
883 ///
884 /// This is public so that test frameworks can change it.
885 ///
886 /// This flag is not used in release builds.
887 @protected
888 bool debugBuildingDirtyElements = false;
889
890 /// Pump the build and rendering pipeline to generate a frame.
891 ///
892 /// This method is called by [handleDrawFrame], which itself is called
893 /// automatically by the engine when it is time to lay out and paint a
894 /// frame.
895 ///
896 /// Each frame consists of the following phases:
897 ///
898 /// 1. The animation phase: The [handleBeginFrame] method, which is registered
899 /// with [PlatformDispatcher.onBeginFrame], invokes all the transient frame
900 /// callbacks registered with [scheduleFrameCallback], in registration order.
901 /// This includes all the [Ticker] instances that are driving
902 /// [AnimationController] objects, which means all of the active [Animation]
903 /// objects tick at this point.
904 ///
905 /// 2. Microtasks: After [handleBeginFrame] returns, any microtasks that got
906 /// scheduled by transient frame callbacks get to run. This typically includes
907 /// callbacks for futures from [Ticker]s and [AnimationController]s that
908 /// completed this frame.
909 ///
910 /// After [handleBeginFrame], [handleDrawFrame], which is registered with
911 /// [PlatformDispatcher.onDrawFrame], is called, which invokes all the
912 /// persistent frame callbacks, of which the most notable is this method,
913 /// [drawFrame], which proceeds as follows:
914 ///
915 /// 3. The build phase: All the dirty [Element]s in the widget tree are
916 /// rebuilt (see [State.build]). See [State.setState] for further details on
917 /// marking a widget dirty for building. See [BuildOwner] for more information
918 /// on this step.
919 ///
920 /// 4. The layout phase: All the dirty [RenderObject]s in the system are laid
921 /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout]
922 /// for further details on marking an object dirty for layout.
923 ///
924 /// 5. The compositing bits phase: The compositing bits on any dirty
925 /// [RenderObject] objects are updated. See
926 /// [RenderObject.markNeedsCompositingBitsUpdate].
927 ///
928 /// 6. The paint phase: All the dirty [RenderObject]s in the system are
929 /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See
930 /// [RenderObject.markNeedsPaint] for further details on marking an object
931 /// dirty for paint.
932 ///
933 /// 7. The compositing phase: The layer tree is turned into a [Scene] and
934 /// sent to the GPU.
935 ///
936 /// 8. The semantics phase: All the dirty [RenderObject]s in the system have
937 /// their semantics updated (see [RenderObject.assembleSemanticsNode]). This
938 /// generates the [SemanticsNode] tree. See
939 /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an
940 /// object dirty for semantics.
941 ///
942 /// For more details on steps 4-8, see [PipelineOwner].
943 ///
944 /// 9. The finalization phase in the widgets layer: The widgets tree is
945 /// finalized. This causes [State.dispose] to be invoked on any objects that
946 /// were removed from the widgets tree this frame. See
947 /// [BuildOwner.finalizeTree] for more details.
948 ///
949 /// 10. The finalization phase in the scheduler layer: After [drawFrame]
950 /// returns, [handleDrawFrame] then invokes post-frame callbacks (registered
951 /// with [addPostFrameCallback]).
952 //
953 // When editing the above, also update rendering/binding.dart's copy.
954 @override
955 void drawFrame() {
956 assert(!debugBuildingDirtyElements);
957 assert(() {
958 debugBuildingDirtyElements = true;
959 return true;
960 }());
961
962 TimingsCallback? firstFrameCallback;
963 if (_needToReportFirstFrame) {
964 assert(!_firstFrameCompleter.isCompleted);
965
966 firstFrameCallback = (List<FrameTiming> timings) {
967 assert(sendFramesToEngine);
968 if (!kReleaseMode) {
969 // Change the current user tag back to the default tag. At this point,
970 // the user tag should be set to "AppStartUp" (originally set in the
971 // engine), so we need to change it back to the default tag to mark
972 // the end of app start up for CPU profiles.
973 developer.UserTag.defaultTag.makeCurrent();
974 developer.Timeline.instantSync('Rasterized first useful frame');
975 developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
976 }
977 SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
978 firstFrameCallback = null;
979 _firstFrameCompleter.complete();
980 };
981 // Callback is only invoked when FlutterView.render is called. When
982 // sendFramesToEngine is set to false during the frame, it will not be
983 // called and we need to remove the callback (see below).
984 SchedulerBinding.instance.addTimingsCallback(firstFrameCallback!);
985 }
986
987 try {
988 if (rootElement != null) {
989 buildOwner!.buildScope(rootElement!);
990 }
991 super.drawFrame();
992 buildOwner!.finalizeTree();
993 } finally {
994 assert(() {
995 debugBuildingDirtyElements = false;
996 return true;
997 }());
998 }
999 if (!kReleaseMode) {
1000 if (_needToReportFirstFrame && sendFramesToEngine) {
1001 developer.Timeline.instantSync('Widgets built first useful frame');
1002 }
1003 }
1004 _needToReportFirstFrame = false;
1005 if (firstFrameCallback != null && !sendFramesToEngine) {
1006 // This frame is deferred and not the first frame sent to the engine that
1007 // should be reported.
1008 _needToReportFirstFrame = true;
1009 SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
1010 }
1011 }
1012
1013 /// The [Element] that is at the root of the element tree hierarchy.
1014 ///
1015 /// This is initialized the first time [runApp] is called.
1016 Element? get rootElement => _rootElement;
1017 Element? _rootElement;
1018
1019 /// Deprecated. Will be removed in a future version of Flutter.
1020 ///
1021 /// Use [rootElement] instead.
1022 @Deprecated(
1023 'Use rootElement instead. '
1024 'This feature was deprecated after v3.9.0-16.0.pre.'
1025 )
1026 Element? get renderViewElement => rootElement;
1027
1028 bool _readyToProduceFrames = false;
1029
1030 @override
1031 bool get framesEnabled => super.framesEnabled && _readyToProduceFrames;
1032
1033 /// Used by [runApp] to wrap the provided `rootWidget` in the default [View].
1034 ///
1035 /// The [View] determines into what [FlutterView] the app is rendered into.
1036 /// This is currently [PlatformDispatcher.implicitView] from [platformDispatcher].
1037 ///
1038 /// The `rootWidget` widget provided to this method must not already be
1039 /// wrapped in a [View].
1040 Widget wrapWithDefaultView(Widget rootWidget) {
1041 return View(
1042 view: platformDispatcher.implicitView!,
1043 deprecatedDoNotUseWillBeRemovedWithoutNoticePipelineOwner: pipelineOwner,
1044 deprecatedDoNotUseWillBeRemovedWithoutNoticeRenderView: renderView,
1045 child: rootWidget,
1046 );
1047 }
1048
1049 /// Schedules a [Timer] for attaching the root widget.
1050 ///
1051 /// This is called by [runApp] to configure the widget tree. Consider using
1052 /// [attachRootWidget] if you want to build the widget tree synchronously.
1053 @protected
1054 void scheduleAttachRootWidget(Widget rootWidget) {
1055 Timer.run(() {
1056 attachRootWidget(rootWidget);
1057 });
1058 }
1059
1060 /// Takes a widget and attaches it to the [rootElement], creating it if
1061 /// necessary.
1062 ///
1063 /// This is called by [runApp] to configure the widget tree.
1064 ///
1065 /// See also:
1066 ///
1067 /// * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
1068 /// widget and attaches it to the render tree.
1069 void attachRootWidget(Widget rootWidget) {
1070 attachToBuildOwner(RootWidget(
1071 debugShortDescription: '[root]',
1072 child: rootWidget,
1073 ));
1074 }
1075
1076 /// Called by [attachRootWidget] to attach the provided [RootWidget] to the
1077 /// [buildOwner].
1078 ///
1079 /// This creates the [rootElement], if necessary, or re-uses an existing one.
1080 ///
1081 /// This method is rarely called directly, but it can be useful in tests to
1082 /// restore the element tree to a previous version by providing the
1083 /// [RootWidget] of that version (see [WidgetTester.restartAndRestore] for an
1084 /// exemplary use case).
1085 void attachToBuildOwner(RootWidget widget) {
1086 final bool isBootstrapFrame = rootElement == null;
1087 _readyToProduceFrames = true;
1088 _rootElement = widget.attach(buildOwner!, rootElement as RootElement?);
1089 if (isBootstrapFrame) {
1090 SchedulerBinding.instance.ensureVisualUpdate();
1091 }
1092 }
1093
1094 /// Whether the [rootElement] has been initialized.
1095 ///
1096 /// This will be false until [runApp] is called (or [WidgetTester.pumpWidget]
1097 /// is called in the context of a [TestWidgetsFlutterBinding]).
1098 bool get isRootWidgetAttached => _rootElement != null;
1099
1100 @override
1101 Future<void> performReassemble() {
1102 assert(() {
1103 WidgetInspectorService.instance.performReassemble();
1104 return true;
1105 }());
1106
1107 if (rootElement != null) {
1108 buildOwner!.reassemble(rootElement!);
1109 }
1110 return super.performReassemble();
1111 }
1112
1113 /// Computes the locale the current platform would resolve to.
1114 ///
1115 /// This method is meant to be used as part of a
1116 /// [WidgetsApp.localeListResolutionCallback]. Since this method may return
1117 /// null, a Flutter/dart algorithm should still be provided as a fallback in
1118 /// case a native resolved locale cannot be determined or if the native
1119 /// resolved locale is undesirable.
1120 ///
1121 /// This method may return a null [Locale] if the platform does not support
1122 /// native locale resolution, or if the resolution failed.
1123 ///
1124 /// The first `supportedLocale` is treated as the default locale and will be returned
1125 /// if no better match is found.
1126 ///
1127 /// Android and iOS are currently supported.
1128 ///
1129 /// On Android, the algorithm described in
1130 /// https://developer.android.com/guide/topics/resources/multilingual-support
1131 /// is used to determine the resolved locale. Depending on the android version
1132 /// of the device, either the modern (>= API 24) or legacy (< API 24) algorithm
1133 /// will be used.
1134 ///
1135 /// On iOS, the result of `preferredLocalizationsFromArray` method of `NSBundle`
1136 /// is returned. See:
1137 /// https://developer.apple.com/documentation/foundation/nsbundle/1417249-preferredlocalizationsfromarray?language=objc
1138 /// for details on the used method.
1139 ///
1140 /// iOS treats script code as necessary for a match, so a user preferred locale of
1141 /// `zh_Hans_CN` will not resolve to a supported locale of `zh_CN`.
1142 ///
1143 /// Since implementation may vary by platform and has potential to be heavy,
1144 /// it is recommended to cache the results of this method if the value is
1145 /// used multiple times.
1146 ///
1147 /// Second-best (and n-best) matching locales should be obtained by calling this
1148 /// method again with the matched locale of the first call omitted from
1149 /// `supportedLocales`.
1150 Locale? computePlatformResolvedLocale(List<Locale> supportedLocales) {
1151 return platformDispatcher.computePlatformResolvedLocale(supportedLocales);
1152 }
1153}
1154
1155/// Inflate the given widget and attach it to the screen.
1156///
1157/// The widget is given constraints during layout that force it to fill the
1158/// entire screen. If you wish to align your widget to one side of the screen
1159/// (e.g., the top), consider using the [Align] widget. If you wish to center
1160/// your widget, you can also use the [Center] widget.
1161///
1162/// Calling [runApp] again will detach the previous root widget from the screen
1163/// and attach the given widget in its place. The new widget tree is compared
1164/// against the previous widget tree and any differences are applied to the
1165/// underlying render tree, similar to what happens when a [StatefulWidget]
1166/// rebuilds after calling [State.setState].
1167///
1168/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
1169///
1170/// ## Application shutdown
1171///
1172/// This widget tree is not torn down when the application shuts down, because
1173/// there is no way to predict when that will happen. For example, a user could
1174/// physically remove power from their device, or the application could crash
1175/// unexpectedly, or the malware on the device could forcibly terminate the
1176/// process.
1177///
1178/// Applications are responsible for ensuring that they are well-behaved
1179/// even in the face of a rapid unscheduled termination.
1180///
1181/// To artificially cause the entire widget tree to be disposed, consider
1182/// calling [runApp] with a widget such as [SizedBox.shrink].
1183///
1184/// To listen for platform shutdown messages (and other lifecycle changes),
1185/// consider the [AppLifecycleListener] API.
1186///
1187/// ## Dismissing Flutter UI via platform native methods
1188///
1189/// {@template flutter.widgets.runApp.dismissal}
1190/// An application may have both Flutter and non-Flutter UI in it. If the
1191/// application calls non-Flutter methods to remove Flutter based UI such as
1192/// platform native API to manipulate the platform native navigation stack,
1193/// the framework does not know if the developer intends to eagerly free
1194/// resources or not. The widget tree remains mounted and ready to render
1195/// as soon as it is displayed again.
1196///
1197/// To release resources more eagerly, establish a [platform channel](https://flutter.dev/platform-channels/)
1198/// and use it to call [runApp] with a widget such as [SizedBox.shrink] when
1199/// the framework should dispose of the active widget tree.
1200/// {@endtemplate}
1201///
1202/// See also:
1203///
1204/// * [WidgetsBinding.attachRootWidget], which creates the root widget for the
1205/// widget hierarchy.
1206/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root
1207/// element for the element hierarchy.
1208/// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to
1209/// ensure the widget, element, and render trees are all built.
1210void runApp(Widget app) {
1211 final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
1212 assert(binding.debugCheckZone('runApp'));
1213 binding
1214 ..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
1215 ..scheduleWarmUpFrame();
1216}
1217
1218String _debugDumpAppString() {
1219 const String mode = kDebugMode ? 'DEBUG MODE' : kReleaseMode ? 'RELEASE MODE' : 'PROFILE MODE';
1220 final StringBuffer buffer = StringBuffer();
1221 buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode');
1222 if (WidgetsBinding.instance.rootElement != null) {
1223 buffer.writeln(WidgetsBinding.instance.rootElement!.toStringDeep());
1224 } else {
1225 buffer.writeln('<no tree currently mounted>');
1226 }
1227 return buffer.toString();
1228}
1229
1230/// Print a string representation of the currently running app.
1231void debugDumpApp() {
1232 debugPrint(_debugDumpAppString());
1233}
1234
1235/// A widget for the root of the widget tree.
1236///
1237/// Exposes an [attach] method to attach the widget tree to a [BuildOwner]. That
1238/// method also bootstraps the element tree.
1239///
1240/// Used by [WidgetsBinding.attachRootWidget] (which is indirectly called by
1241/// [runApp]) to bootstrap applications.
1242class RootWidget extends Widget {
1243 /// Creates a [RootWidget].
1244 const RootWidget({
1245 super.key,
1246 this.child,
1247 this.debugShortDescription,
1248 });
1249
1250 /// The widget below this widget in the tree.
1251 ///
1252 /// {@macro flutter.widgets.ProxyWidget.child}
1253 final Widget? child;
1254
1255 /// A short description of this widget used by debugging aids.
1256 final String? debugShortDescription;
1257
1258 @override
1259 RootElement createElement() => RootElement(this);
1260
1261 /// Inflate this widget and attaches it to the provided [BuildOwner].
1262 ///
1263 /// If `element` is null, this function will create a new element. Otherwise,
1264 /// the given element will have an update scheduled to switch to this widget.
1265 ///
1266 /// Used by [WidgetsBinding.attachToBuildOwner] (which is indirectly called by
1267 /// [runApp]) to bootstrap applications.
1268 RootElement attach(BuildOwner owner, [ RootElement? element ]) {
1269 if (element == null) {
1270 owner.lockState(() {
1271 element = createElement();
1272 assert(element != null);
1273 element!.assignOwner(owner);
1274 });
1275 owner.buildScope(element!, () {
1276 element!.mount(/* parent */ null, /* slot */ null);
1277 });
1278 } else {
1279 element._newWidget = this;
1280 element.markNeedsBuild();
1281 }
1282 return element!;
1283 }
1284
1285 @override
1286 String toStringShort() => debugShortDescription ?? super.toStringShort();
1287}
1288
1289/// The root of the element tree.
1290///
1291/// This element class is the instantiation of a [RootWidget]. It can be used
1292/// only as the root of an [Element] tree (it cannot be mounted into another
1293/// [Element]; its parent must be null).
1294///
1295/// In typical usage, it will be instantiated for a [RootWidget] by calling
1296/// [RootWidget.attach]. In this usage, it is normally instantiated by the
1297/// bootstrapping logic in the [WidgetsFlutterBinding] singleton created by
1298/// [runApp].
1299class RootElement extends Element with RootElementMixin {
1300 /// Creates a [RootElement] for the provided [RootWidget].
1301 RootElement(RootWidget super.widget);
1302
1303 Element? _child;
1304
1305 @override
1306 void visitChildren(ElementVisitor visitor) {
1307 if (_child != null) {
1308 visitor(_child!);
1309 }
1310 }
1311
1312 @override
1313 void forgetChild(Element child) {
1314 assert(child == _child);
1315 _child = null;
1316 super.forgetChild(child);
1317 }
1318
1319 @override
1320 void mount(Element? parent, Object? newSlot) {
1321 assert(parent == null); // We are the root!
1322 super.mount(parent, newSlot);
1323 _rebuild();
1324 assert(_child != null);
1325 super.performRebuild(); // clears the "dirty" flag
1326 }
1327
1328 @override
1329 void update(RootWidget newWidget) {
1330 super.update(newWidget);
1331 assert(widget == newWidget);
1332 _rebuild();
1333 }
1334
1335 // When we are assigned a new widget, we store it here
1336 // until we are ready to update to it.
1337 RootWidget? _newWidget;
1338
1339 @override
1340 void performRebuild() {
1341 if (_newWidget != null) {
1342 // _newWidget can be null if, for instance, we were rebuilt
1343 // due to a reassemble.
1344 final RootWidget newWidget = _newWidget!;
1345 _newWidget = null;
1346 update(newWidget);
1347 }
1348 super.performRebuild();
1349 assert(_newWidget == null);
1350 }
1351
1352 void _rebuild() {
1353 try {
1354 _child = updateChild(_child, (widget as RootWidget).child, /* slot */ null);
1355 } catch (exception, stack) {
1356 final FlutterErrorDetails details = FlutterErrorDetails(
1357 exception: exception,
1358 stack: stack,
1359 library: 'widgets library',
1360 context: ErrorDescription('attaching to the render tree'),
1361 );
1362 FlutterError.reportError(details);
1363 // No error widget possible here since it wouldn't have a view to render into.
1364 _child = null;
1365 }
1366
1367 }
1368
1369 @override
1370 bool get debugDoingBuild => false; // This element doesn't have a build phase.
1371
1372 @override
1373 // There is no ancestor RenderObjectElement that the render object could be attached to.
1374 bool debugExpectsRenderObjectForSlot(Object? slot) => false;
1375}
1376
1377/// A concrete binding for applications based on the Widgets framework.
1378///
1379/// This is the glue that binds the framework to the Flutter engine.
1380///
1381/// When using the widgets framework, this binding, or one that
1382/// implements the same interfaces, must be used. The following
1383/// mixins are used to implement this binding:
1384///
1385/// * [GestureBinding], which implements the basics of hit testing.
1386/// * [SchedulerBinding], which introduces the concepts of frames.
1387/// * [ServicesBinding], which provides access to the plugin subsystem.
1388/// * [PaintingBinding], which enables decoding images.
1389/// * [SemanticsBinding], which supports accessibility.
1390/// * [RendererBinding], which handles the render tree.
1391/// * [WidgetsBinding], which handles the widget tree.
1392class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
1393 /// Returns an instance of the binding that implements
1394 /// [WidgetsBinding]. If no binding has yet been initialized, the
1395 /// [WidgetsFlutterBinding] class is used to create and initialize
1396 /// one.
1397 ///
1398 /// You only need to call this method if you need the binding to be
1399 /// initialized before calling [runApp].
1400 ///
1401 /// In the `flutter_test` framework, [testWidgets] initializes the
1402 /// binding instance to a [TestWidgetsFlutterBinding], not a
1403 /// [WidgetsFlutterBinding]. See
1404 /// [TestWidgetsFlutterBinding.ensureInitialized].
1405 static WidgetsBinding ensureInitialized() {
1406 if (WidgetsBinding._instance == null) {
1407 WidgetsFlutterBinding();
1408 }
1409 return WidgetsBinding.instance;
1410 }
1411}
1412