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