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'; |
17 | library; |
18 | |
19 | import 'dart:async'; |
20 | import 'dart:developer' as developer; |
21 | import 'dart:ui' |
22 | show |
23 | AccessibilityFeatures, |
24 | AppExitResponse, |
25 | AppLifecycleState, |
26 | FrameTiming, |
27 | Locale, |
28 | PlatformDispatcher, |
29 | TimingsCallback, |
30 | ViewFocusEvent; |
31 | |
32 | import 'package:flutter/foundation.dart'; |
33 | import 'package:flutter/gestures.dart'; |
34 | import 'package:flutter/rendering.dart'; |
35 | import 'package:flutter/scheduler.dart'; |
36 | import 'package:flutter/services.dart'; |
37 | |
38 | import 'app.dart'; |
39 | import 'debug.dart'; |
40 | import 'focus_manager.dart'; |
41 | import 'framework.dart'; |
42 | import 'platform_menu_bar.dart'; |
43 | import 'router.dart'; |
44 | import 'service_extensions.dart'; |
45 | import 'view.dart'; |
46 | import 'widget_inspector.dart'; |
47 | |
48 | export '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} |
83 | abstract 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. |
431 | mixin 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 | @visibleForTesting |
505 | @override |
506 | void resetInternalState() { |
507 | // ignore: invalid_use_of_visible_for_testing_member, https://github.com/dart-lang/sdk/issues/41998 |
508 | super.resetInternalState(); |
509 | _debugShowWidgetInspectorOverrideNotifierObject?.dispose(); |
510 | _debugShowWidgetInspectorOverrideNotifierObject = null; |
511 | _debugWidgetInspectorSelectionOnTapEnabledNotifierObject?.dispose(); |
512 | _debugWidgetInspectorSelectionOnTapEnabledNotifierObject = null; |
513 | } |
514 | |
515 | void _debugAddStackFilters() { |
516 | const PartialStackFrame elementInflateWidget = PartialStackFrame( |
517 | package: 'package:flutter/src/widgets/framework.dart', |
518 | className: 'Element', |
519 | method: 'inflateWidget', |
520 | ); |
521 | const PartialStackFrame elementUpdateChild = PartialStackFrame( |
522 | package: 'package:flutter/src/widgets/framework.dart', |
523 | className: 'Element', |
524 | method: 'updateChild', |
525 | ); |
526 | const PartialStackFrame elementRebuild = PartialStackFrame( |
527 | package: 'package:flutter/src/widgets/framework.dart', |
528 | className: 'Element', |
529 | method: 'rebuild', |
530 | ); |
531 | const PartialStackFrame componentElementPerformRebuild = PartialStackFrame( |
532 | package: 'package:flutter/src/widgets/framework.dart', |
533 | className: 'ComponentElement', |
534 | method: 'performRebuild', |
535 | ); |
536 | const PartialStackFrame componentElementFirstBuild = PartialStackFrame( |
537 | package: 'package:flutter/src/widgets/framework.dart', |
538 | className: 'ComponentElement', |
539 | method: '_firstBuild', |
540 | ); |
541 | const PartialStackFrame componentElementMount = PartialStackFrame( |
542 | package: 'package:flutter/src/widgets/framework.dart', |
543 | className: 'ComponentElement', |
544 | method: 'mount', |
545 | ); |
546 | const PartialStackFrame statefulElementFirstBuild = PartialStackFrame( |
547 | package: 'package:flutter/src/widgets/framework.dart', |
548 | className: 'StatefulElement', |
549 | method: '_firstBuild', |
550 | ); |
551 | const PartialStackFrame singleChildMount = PartialStackFrame( |
552 | package: 'package:flutter/src/widgets/framework.dart', |
553 | className: 'SingleChildRenderObjectElement', |
554 | method: 'mount', |
555 | ); |
556 | const PartialStackFrame statefulElementRebuild = PartialStackFrame( |
557 | package: 'package:flutter/src/widgets/framework.dart', |
558 | className: 'StatefulElement', |
559 | method: 'performRebuild', |
560 | ); |
561 | |
562 | const String replacementString = '... Normal element mounting'; |
563 | |
564 | // ComponentElement variations |
565 | FlutterError.addDefaultStackFilter( |
566 | const RepetitiveStackFrameFilter( |
567 | frames: <PartialStackFrame>[ |
568 | elementInflateWidget, |
569 | elementUpdateChild, |
570 | componentElementPerformRebuild, |
571 | elementRebuild, |
572 | componentElementFirstBuild, |
573 | componentElementMount, |
574 | ], |
575 | replacement: replacementString, |
576 | ), |
577 | ); |
578 | FlutterError.addDefaultStackFilter( |
579 | const RepetitiveStackFrameFilter( |
580 | frames: <PartialStackFrame>[ |
581 | elementUpdateChild, |
582 | componentElementPerformRebuild, |
583 | elementRebuild, |
584 | componentElementFirstBuild, |
585 | componentElementMount, |
586 | ], |
587 | replacement: replacementString, |
588 | ), |
589 | ); |
590 | |
591 | // StatefulElement variations |
592 | FlutterError.addDefaultStackFilter( |
593 | const RepetitiveStackFrameFilter( |
594 | frames: <PartialStackFrame>[ |
595 | elementInflateWidget, |
596 | elementUpdateChild, |
597 | componentElementPerformRebuild, |
598 | statefulElementRebuild, |
599 | elementRebuild, |
600 | componentElementFirstBuild, |
601 | statefulElementFirstBuild, |
602 | componentElementMount, |
603 | ], |
604 | replacement: replacementString, |
605 | ), |
606 | ); |
607 | FlutterError.addDefaultStackFilter( |
608 | const RepetitiveStackFrameFilter( |
609 | frames: <PartialStackFrame>[ |
610 | elementUpdateChild, |
611 | componentElementPerformRebuild, |
612 | statefulElementRebuild, |
613 | elementRebuild, |
614 | componentElementFirstBuild, |
615 | statefulElementFirstBuild, |
616 | componentElementMount, |
617 | ], |
618 | replacement: replacementString, |
619 | ), |
620 | ); |
621 | |
622 | // SingleChildRenderObjectElement variations |
623 | FlutterError.addDefaultStackFilter( |
624 | const RepetitiveStackFrameFilter( |
625 | frames: <PartialStackFrame>[elementInflateWidget, elementUpdateChild, singleChildMount], |
626 | replacement: replacementString, |
627 | ), |
628 | ); |
629 | FlutterError.addDefaultStackFilter( |
630 | const RepetitiveStackFrameFilter( |
631 | frames: <PartialStackFrame>[elementUpdateChild, singleChildMount], |
632 | replacement: replacementString, |
633 | ), |
634 | ); |
635 | } |
636 | |
637 | @override |
638 | void initServiceExtensions() { |
639 | super.initServiceExtensions(); |
640 | |
641 | if (!kReleaseMode) { |
642 | registerServiceExtension( |
643 | name: WidgetsServiceExtensions.debugDumpApp.name, |
644 | callback: (Map<String, String> parameters) async { |
645 | final String data = _debugDumpAppString(); |
646 | return <String, Object>{'data': data}; |
647 | }, |
648 | ); |
649 | |
650 | registerServiceExtension( |
651 | name: WidgetsServiceExtensions.debugDumpFocusTree.name, |
652 | callback: (Map<String, String> parameters) async { |
653 | final String data = focusManager.toStringDeep(); |
654 | return <String, Object>{'data': data}; |
655 | }, |
656 | ); |
657 | |
658 | if (!kIsWeb) { |
659 | registerBoolServiceExtension( |
660 | name: WidgetsServiceExtensions.showPerformanceOverlay.name, |
661 | getter: () => Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride), |
662 | setter: (bool value) { |
663 | if (WidgetsApp.showPerformanceOverlayOverride == value) { |
664 | return Future<void>.value(); |
665 | } |
666 | WidgetsApp.showPerformanceOverlayOverride = value; |
667 | return _forceRebuild(); |
668 | }, |
669 | ); |
670 | } |
671 | |
672 | registerServiceExtension( |
673 | name: WidgetsServiceExtensions.didSendFirstFrameEvent.name, |
674 | callback: (_) async { |
675 | return <String, dynamic>{ |
676 | // This is defined to return a STRING, not a boolean. |
677 | // Devtools, the Intellij plugin, and the flutter tool all depend |
678 | // on it returning a string and not a boolean. |
679 | 'enabled': _needToReportFirstFrame ? 'false': 'true', |
680 | }; |
681 | }, |
682 | ); |
683 | |
684 | registerServiceExtension( |
685 | name: WidgetsServiceExtensions.didSendFirstFrameRasterizedEvent.name, |
686 | callback: (_) async { |
687 | return <String, dynamic>{ |
688 | // This is defined to return a STRING, not a boolean. |
689 | // Devtools, the Intellij plugin, and the flutter tool all depend |
690 | // on it returning a string and not a boolean. |
691 | 'enabled': firstFrameRasterized ? 'true': 'false', |
692 | }; |
693 | }, |
694 | ); |
695 | |
696 | // Expose the ability to send Widget rebuilds as [Timeline] events. |
697 | registerBoolServiceExtension( |
698 | name: WidgetsServiceExtensions.profileWidgetBuilds.name, |
699 | getter: () async => debugProfileBuildsEnabled, |
700 | setter: (bool value) async { |
701 | debugProfileBuildsEnabled = value; |
702 | }, |
703 | ); |
704 | registerBoolServiceExtension( |
705 | name: WidgetsServiceExtensions.profileUserWidgetBuilds.name, |
706 | getter: () async => debugProfileBuildsEnabledUserWidgets, |
707 | setter: (bool value) async { |
708 | debugProfileBuildsEnabledUserWidgets = value; |
709 | }, |
710 | ); |
711 | } |
712 | |
713 | assert(() { |
714 | registerBoolServiceExtension( |
715 | name: WidgetsServiceExtensions.debugAllowBanner.name, |
716 | getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride), |
717 | setter: (bool value) { |
718 | if (WidgetsApp.debugAllowBannerOverride == value) { |
719 | return Future<void>.value(); |
720 | } |
721 | WidgetsApp.debugAllowBannerOverride = value; |
722 | return _forceRebuild(); |
723 | }, |
724 | ); |
725 | |
726 | WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension); |
727 | |
728 | return true; |
729 | }()); |
730 | } |
731 | |
732 | Future<void> _forceRebuild() { |
733 | if (rootElement != null) { |
734 | buildOwner!.reassemble(rootElement!); |
735 | return endOfFrame; |
736 | } |
737 | return Future<void>.value(); |
738 | } |
739 | |
740 | /// The [BuildOwner] in charge of executing the build pipeline for the |
741 | /// widget tree rooted at this binding. |
742 | BuildOwner? get buildOwner => _buildOwner; |
743 | // Initialization of [_buildOwner] has to be done within the [initInstances] |
744 | // method, as it requires [ServicesBinding] to properly setup the |
745 | // [defaultBinaryMessenger] instance. |
746 | BuildOwner? _buildOwner; |
747 | |
748 | /// The object in charge of the focus tree. |
749 | /// |
750 | /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain |
751 | /// the [FocusScopeNode] for a given [BuildContext]. |
752 | /// |
753 | /// See [FocusManager] for more details. |
754 | FocusManager get focusManager => _buildOwner!.focusManager; |
755 | |
756 | /// A delegate that communicates with a platform plugin for serializing and |
757 | /// managing platform-rendered menu bars created by [PlatformMenuBar]. |
758 | /// |
759 | /// This is set by default to a [DefaultPlatformMenuDelegate] instance in |
760 | /// [initInstances]. |
761 | late PlatformMenuDelegate platformMenuDelegate; |
762 | |
763 | final List<WidgetsBindingObserver> _observers = <WidgetsBindingObserver>[]; |
764 | |
765 | /// Registers the given object as a binding observer. Binding |
766 | /// observers are notified when various application events occur, |
767 | /// for example when the system locale changes. Generally, one |
768 | /// widget in the widget tree registers itself as a binding |
769 | /// observer, and converts the system state into inherited widgets. |
770 | /// |
771 | /// For example, the [WidgetsApp] widget registers as a binding |
772 | /// observer and passes the screen size to a [MediaQuery] widget |
773 | /// each time it is built, which enables other widgets to use the |
774 | /// [MediaQuery.sizeOf] static method and (implicitly) the |
775 | /// [InheritedWidget] mechanism to be notified whenever the screen |
776 | /// size changes (e.g. whenever the screen rotates). |
777 | /// |
778 | /// See also: |
779 | /// |
780 | /// * [removeObserver], to release the resources reserved by this method. |
781 | /// * [WidgetsBindingObserver], which has an example of using this method. |
782 | void addObserver(WidgetsBindingObserver observer) => _observers.add(observer); |
783 | |
784 | /// Unregisters the given observer. This should be used sparingly as |
785 | /// it is relatively expensive (O(N) in the number of registered |
786 | /// observers). |
787 | /// |
788 | /// See also: |
789 | /// |
790 | /// * [addObserver], for the method that adds observers in the first place. |
791 | /// * [WidgetsBindingObserver], which has an example of using this method. |
792 | bool removeObserver(WidgetsBindingObserver observer) { |
793 | if (observer == _backGestureObserver) { |
794 | _backGestureObserver = null; |
795 | } |
796 | return _observers.remove(observer); |
797 | } |
798 | |
799 | @override |
800 | Future<AppExitResponse> handleRequestAppExit() async { |
801 | bool didCancel = false; |
802 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
803 | if ((await observer.didRequestAppExit()) == AppExitResponse.cancel) { |
804 | didCancel = true; |
805 | // Don't early return. For the case where someone is just using the |
806 | // observer to know when exit happens, we want to call all the |
807 | // observers, even if we already know we're going to cancel. |
808 | } |
809 | } |
810 | return didCancel ? AppExitResponse.cancel : AppExitResponse.exit; |
811 | } |
812 | |
813 | @override |
814 | void handleMetricsChanged() { |
815 | super.handleMetricsChanged(); |
816 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
817 | observer.didChangeMetrics(); |
818 | } |
819 | } |
820 | |
821 | @override |
822 | void handleTextScaleFactorChanged() { |
823 | super.handleTextScaleFactorChanged(); |
824 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
825 | observer.didChangeTextScaleFactor(); |
826 | } |
827 | } |
828 | |
829 | @override |
830 | void handlePlatformBrightnessChanged() { |
831 | super.handlePlatformBrightnessChanged(); |
832 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
833 | observer.didChangePlatformBrightness(); |
834 | } |
835 | } |
836 | |
837 | @override |
838 | void handleAccessibilityFeaturesChanged() { |
839 | super.handleAccessibilityFeaturesChanged(); |
840 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
841 | observer.didChangeAccessibilityFeatures(); |
842 | } |
843 | } |
844 | |
845 | /// Called when the system locale changes. |
846 | /// |
847 | /// Calls [dispatchLocalesChanged] to notify the binding observers. |
848 | /// |
849 | /// See [dart:ui.PlatformDispatcher.onLocaleChanged]. |
850 | @protected |
851 | @mustCallSuper |
852 | @visibleForTesting |
853 | void handleLocaleChanged() { |
854 | dispatchLocalesChanged(platformDispatcher.locales); |
855 | } |
856 | |
857 | /// Notify all the observers that the locale has changed (using |
858 | /// [WidgetsBindingObserver.didChangeLocales]), giving them the |
859 | /// `locales` argument. |
860 | /// |
861 | /// This is called by [handleLocaleChanged] when the |
862 | /// [PlatformDispatcher.onLocaleChanged] notification is received. |
863 | @protected |
864 | @mustCallSuper |
865 | void dispatchLocalesChanged(List<Locale>? locales) { |
866 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
867 | observer.didChangeLocales(locales); |
868 | } |
869 | } |
870 | |
871 | /// Notify all the observers that the active set of [AccessibilityFeatures] |
872 | /// has changed (using [WidgetsBindingObserver.didChangeAccessibilityFeatures]), |
873 | /// giving them the `features` argument. |
874 | /// |
875 | /// This is called by [handleAccessibilityFeaturesChanged] when the |
876 | /// [PlatformDispatcher.onAccessibilityFeaturesChanged] notification is received. |
877 | @protected |
878 | @mustCallSuper |
879 | void dispatchAccessibilityFeaturesChanged() { |
880 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
881 | observer.didChangeAccessibilityFeatures(); |
882 | } |
883 | } |
884 | |
885 | /// Called when the system pops the current route. |
886 | /// |
887 | /// This first notifies the binding observers (using |
888 | /// [WidgetsBindingObserver.didPopRoute]), in registration order, until one |
889 | /// returns true, meaning that it was able to handle the request (e.g. by |
890 | /// closing a dialog box). If none return true, then the application is shut |
891 | /// down by calling [SystemNavigator.pop]. |
892 | /// |
893 | /// [WidgetsApp] uses this in conjunction with a [Navigator] to |
894 | /// cause the back button to close dialog boxes, return from modal |
895 | /// pages, and so forth. |
896 | /// |
897 | /// This method exposes the `popRoute` notification from |
898 | /// [SystemChannels.navigation]. |
899 | /// |
900 | /// {@template flutter.widgets.AndroidPredictiveBack} |
901 | /// ## Handling backs ahead of time |
902 | /// |
903 | /// Not all system backs will result in a call to this method. Some are |
904 | /// handled entirely by the system without informing the Flutter framework. |
905 | /// |
906 | /// Android API 33+ introduced a feature called predictive back, which allows |
907 | /// the user to peek behind the current app or route during a back gesture and |
908 | /// then decide to cancel or commit the back. Flutter enables or disables this |
909 | /// feature ahead of time, before a back gesture occurs, and back gestures |
910 | /// that trigger predictive back are handled entirely by the system and do not |
911 | /// trigger this method here in the framework. |
912 | /// |
913 | /// By default, the framework communicates when it would like to handle system |
914 | /// back gestures using [SystemNavigator.setFrameworkHandlesBack] in |
915 | /// [WidgetsApp]. This is done automatically based on the status of the |
916 | /// [Navigator] stack and the state of any [PopScope] widgets present. |
917 | /// Developers can manually set this by calling the method directly or by |
918 | /// using [NavigationNotification]. |
919 | /// {@endtemplate} |
920 | @protected |
921 | @visibleForTesting |
922 | Future<bool> handlePopRoute() async { |
923 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
924 | if (await observer.didPopRoute()) { |
925 | return true; |
926 | } |
927 | } |
928 | SystemNavigator.pop(); |
929 | return false; |
930 | } |
931 | |
932 | // The observer that is currently handling an active predictive back gesture. |
933 | WidgetsBindingObserver? _backGestureObserver; |
934 | |
935 | Future<bool> _handleStartBackGesture(Map<String?, Object?> arguments) { |
936 | _backGestureObserver = null; |
937 | final PredictiveBackEvent backEvent = PredictiveBackEvent.fromMap(arguments); |
938 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
939 | if (observer.handleStartBackGesture(backEvent)) { |
940 | _backGestureObserver = observer; |
941 | return Future<bool>.value(true); |
942 | } |
943 | } |
944 | return Future<bool>.value(false); |
945 | } |
946 | |
947 | Future<void> _handleUpdateBackGestureProgress(Map<String?, Object?> arguments) async { |
948 | if (_backGestureObserver == null) { |
949 | return; |
950 | } |
951 | |
952 | final PredictiveBackEvent backEvent = PredictiveBackEvent.fromMap(arguments); |
953 | _backGestureObserver!.handleUpdateBackGestureProgress(backEvent); |
954 | } |
955 | |
956 | Future<void> _handleCommitBackGesture() async { |
957 | if (_backGestureObserver == null) { |
958 | // If the predictive back was not handled, then the route should be popped |
959 | // like a normal, non-predictive back. For example, this will happen if a |
960 | // back gesture occurs but no predictive back route transition exists to |
961 | // handle it. The back gesture should still cause normal pop even if it |
962 | // doesn't cause a predictive transition. |
963 | await handlePopRoute(); |
964 | return; |
965 | } |
966 | _backGestureObserver?.handleCommitBackGesture(); |
967 | } |
968 | |
969 | Future<void> _handleCancelBackGesture() async { |
970 | if (_backGestureObserver == null) { |
971 | return; |
972 | } |
973 | |
974 | _backGestureObserver!.handleCancelBackGesture(); |
975 | } |
976 | |
977 | /// Called when the host tells the app to push a new route onto the |
978 | /// navigator. |
979 | /// |
980 | /// This notifies the binding observers (using |
981 | /// [WidgetsBindingObserver.didPushRoute]), in registration order, until one |
982 | /// returns true, meaning that it was able to handle the request (e.g. by |
983 | /// opening a dialog box). If none return true, then nothing happens. |
984 | /// |
985 | /// This method exposes the `pushRoute` notification from |
986 | /// [SystemChannels.navigation]. |
987 | @protected |
988 | @mustCallSuper |
989 | @visibleForTesting |
990 | Future<bool> handlePushRoute(String route) async { |
991 | final RouteInformation routeInformation = RouteInformation(uri: Uri.parse(route)); |
992 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
993 | if (await observer.didPushRouteInformation(routeInformation)) { |
994 | return true; |
995 | } |
996 | } |
997 | return false; |
998 | } |
999 | |
1000 | Future<bool> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async { |
1001 | final RouteInformation routeInformation = RouteInformation( |
1002 | uri: Uri.parse(routeArguments['location'] as String), |
1003 | state: routeArguments['state'] as Object?, |
1004 | ); |
1005 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
1006 | if (await observer.didPushRouteInformation(routeInformation)) { |
1007 | return true; |
1008 | } |
1009 | } |
1010 | return false; |
1011 | } |
1012 | |
1013 | Future<bool> _handleNavigationInvocation(MethodCall methodCall) { |
1014 | return switch (methodCall.method) { |
1015 | 'popRoute'=> handlePopRoute(), |
1016 | 'pushRoute'=> handlePushRoute(methodCall.arguments as String), |
1017 | 'pushRouteInformation'=> _handlePushRouteInformation( |
1018 | methodCall.arguments as Map<dynamic, dynamic>, |
1019 | ), |
1020 | // Return false for unhandled method. |
1021 | _ => Future<bool>.value(false), |
1022 | }; |
1023 | } |
1024 | |
1025 | Future<dynamic> _handleBackGestureInvocation(MethodCall methodCall) { |
1026 | final Map<String?, Object?>? arguments = |
1027 | (methodCall.arguments as Map<Object?, Object?>?)?.cast<String?, Object?>(); |
1028 | return switch (methodCall.method) { |
1029 | 'startBackGesture'=> _handleStartBackGesture(arguments!), |
1030 | 'updateBackGestureProgress'=> _handleUpdateBackGestureProgress(arguments!), |
1031 | 'commitBackGesture'=> _handleCommitBackGesture(), |
1032 | 'cancelBackGesture'=> _handleCancelBackGesture(), |
1033 | _ => throw MissingPluginException(), |
1034 | }; |
1035 | } |
1036 | |
1037 | @override |
1038 | void handleAppLifecycleStateChanged(AppLifecycleState state) { |
1039 | super.handleAppLifecycleStateChanged(state); |
1040 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
1041 | observer.didChangeAppLifecycleState(state); |
1042 | } |
1043 | } |
1044 | |
1045 | @override |
1046 | void handleViewFocusChanged(ViewFocusEvent event) { |
1047 | super.handleViewFocusChanged(event); |
1048 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
1049 | observer.didChangeViewFocus(event); |
1050 | } |
1051 | } |
1052 | |
1053 | @override |
1054 | void handleMemoryPressure() { |
1055 | super.handleMemoryPressure(); |
1056 | for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) { |
1057 | observer.didHaveMemoryPressure(); |
1058 | } |
1059 | } |
1060 | |
1061 | bool _needToReportFirstFrame = true; |
1062 | |
1063 | final Completer<void> _firstFrameCompleter = Completer<void>(); |
1064 | |
1065 | /// Whether the Flutter engine has rasterized the first frame. |
1066 | /// |
1067 | /// Usually, the time that a frame is rasterized is very close to the time that |
1068 | /// it gets presented on the display. Specifically, rasterization is the last |
1069 | /// expensive phase of a frame that's still in Flutter's control. |
1070 | /// |
1071 | /// See also: |
1072 | /// |
1073 | /// * [waitUntilFirstFrameRasterized], the future when [firstFrameRasterized] |
1074 | /// becomes true. |
1075 | bool get firstFrameRasterized => _firstFrameCompleter.isCompleted; |
1076 | |
1077 | /// A future that completes when the Flutter engine has rasterized the first |
1078 | /// frame. |
1079 | /// |
1080 | /// Usually, the time that a frame is rasterized is very close to the time that |
1081 | /// it gets presented on the display. Specifically, rasterization is the last |
1082 | /// expensive phase of a frame that's still in Flutter's control. |
1083 | /// |
1084 | /// See also: |
1085 | /// |
1086 | /// * [firstFrameRasterized], whether this future has completed or not. |
1087 | Future<void> get waitUntilFirstFrameRasterized => _firstFrameCompleter.future; |
1088 | |
1089 | /// Whether the first frame has finished building. |
1090 | /// |
1091 | /// This value can also be obtained over the VM service protocol as |
1092 | /// `ext.flutter.didSendFirstFrameEvent`. |
1093 | /// |
1094 | /// See also: |
1095 | /// |
1096 | /// * [firstFrameRasterized], whether the first frame has finished rendering. |
1097 | bool get debugDidSendFirstFrameEvent => !_needToReportFirstFrame; |
1098 | |
1099 | void _handleBuildScheduled() { |
1100 | // If we're in the process of building dirty elements, then changes |
1101 | // should not trigger a new frame. |
1102 | assert(() { |
1103 | if (debugBuildingDirtyElements) { |
1104 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
1105 | ErrorSummary('Build scheduled during frame.'), |
1106 | ErrorDescription( |
1107 | 'While the widget tree was being built, laid out, and painted, ' |
1108 | 'a new frame was scheduled to rebuild the widget tree.', |
1109 | ), |
1110 | ErrorHint( |
1111 | 'This might be because setState() was called from a layout or ' |
1112 | 'paint callback. ' |
1113 | 'If a change is needed to the widget tree, it should be applied ' |
1114 | 'as the tree is being built. Scheduling a change for the subsequent ' |
1115 | 'frame instead results in an interface that lags behind by one frame. ' |
1116 | 'If this was done to make your build dependent on a size measured at ' |
1117 | 'layout time, consider using a LayoutBuilder, CustomSingleChildLayout, ' |
1118 | 'or CustomMultiChildLayout. If, on the other hand, the one frame delay ' |
1119 | 'is the desired effect, for example because this is an ' |
1120 | 'animation, consider scheduling the frame in a post-frame callback ' |
1121 | 'using SchedulerBinding.addPostFrameCallback or ' |
1122 | 'using an AnimationController to trigger the animation.', |
1123 | ), |
1124 | ]); |
1125 | } |
1126 | return true; |
1127 | }()); |
1128 | ensureVisualUpdate(); |
1129 | } |
1130 | |
1131 | /// Whether we are currently in a frame. This is used to verify |
1132 | /// that frames are not scheduled redundantly. |
1133 | /// |
1134 | /// This is public so that test frameworks can change it. |
1135 | /// |
1136 | /// This flag is not used in release builds. |
1137 | @protected |
1138 | bool debugBuildingDirtyElements = false; |
1139 | |
1140 | /// Pump the build and rendering pipeline to generate a frame. |
1141 | /// |
1142 | /// This method is called by [handleDrawFrame], which itself is called |
1143 | /// automatically by the engine when it is time to lay out and paint a |
1144 | /// frame. |
1145 | /// |
1146 | /// Each frame consists of the following phases: |
1147 | /// |
1148 | /// 1. The animation phase: The [handleBeginFrame] method, which is registered |
1149 | /// with [PlatformDispatcher.onBeginFrame], invokes all the transient frame |
1150 | /// callbacks registered with [scheduleFrameCallback], in registration order. |
1151 | /// This includes all the [Ticker] instances that are driving |
1152 | /// [AnimationController] objects, which means all of the active [Animation] |
1153 | /// objects tick at this point. |
1154 | /// |
1155 | /// 2. Microtasks: After [handleBeginFrame] returns, any microtasks that got |
1156 | /// scheduled by transient frame callbacks get to run. This typically includes |
1157 | /// callbacks for futures from [Ticker]s and [AnimationController]s that |
1158 | /// completed this frame. |
1159 | /// |
1160 | /// After [handleBeginFrame], [handleDrawFrame], which is registered with |
1161 | /// [PlatformDispatcher.onDrawFrame], is called, which invokes all the |
1162 | /// persistent frame callbacks, of which the most notable is this method, |
1163 | /// [drawFrame], which proceeds as follows: |
1164 | /// |
1165 | /// 3. The build phase: All the dirty [Element]s in the widget tree are |
1166 | /// rebuilt (see [State.build]). See [State.setState] for further details on |
1167 | /// marking a widget dirty for building. See [BuildOwner] for more information |
1168 | /// on this step. |
1169 | /// |
1170 | /// 4. The layout phase: All the dirty [RenderObject]s in the system are laid |
1171 | /// out (see [RenderObject.performLayout]). See [RenderObject.markNeedsLayout] |
1172 | /// for further details on marking an object dirty for layout. |
1173 | /// |
1174 | /// 5. The compositing bits phase: The compositing bits on any dirty |
1175 | /// [RenderObject] objects are updated. See |
1176 | /// [RenderObject.markNeedsCompositingBitsUpdate]. |
1177 | /// |
1178 | /// 6. The paint phase: All the dirty [RenderObject]s in the system are |
1179 | /// repainted (see [RenderObject.paint]). This generates the [Layer] tree. See |
1180 | /// [RenderObject.markNeedsPaint] for further details on marking an object |
1181 | /// dirty for paint. |
1182 | /// |
1183 | /// 7. The compositing phase: The layer tree is turned into a [Scene] and |
1184 | /// sent to the GPU. |
1185 | /// |
1186 | /// 8. The semantics phase: All the dirty [RenderObject]s in the system have |
1187 | /// their semantics updated (see [RenderObject.assembleSemanticsNode]). This |
1188 | /// generates the [SemanticsNode] tree. See |
1189 | /// [RenderObject.markNeedsSemanticsUpdate] for further details on marking an |
1190 | /// object dirty for semantics. |
1191 | /// |
1192 | /// For more details on steps 4-8, see [PipelineOwner]. |
1193 | /// |
1194 | /// 9. The finalization phase in the widgets layer: The widgets tree is |
1195 | /// finalized. This causes [State.dispose] to be invoked on any objects that |
1196 | /// were removed from the widgets tree this frame. See |
1197 | /// [BuildOwner.finalizeTree] for more details. |
1198 | /// |
1199 | /// 10. The finalization phase in the scheduler layer: After [drawFrame] |
1200 | /// returns, [handleDrawFrame] then invokes post-frame callbacks (registered |
1201 | /// with [addPostFrameCallback]). |
1202 | // |
1203 | // When editing the above, also update rendering/binding.dart's copy. |
1204 | @override |
1205 | void drawFrame() { |
1206 | assert(!debugBuildingDirtyElements); |
1207 | assert(() { |
1208 | debugBuildingDirtyElements = true; |
1209 | return true; |
1210 | }()); |
1211 | |
1212 | TimingsCallback? firstFrameCallback; |
1213 | bool debugFrameWasSentToEngine = false; |
1214 | if (_needToReportFirstFrame) { |
1215 | assert(!_firstFrameCompleter.isCompleted); |
1216 | |
1217 | firstFrameCallback = (List<FrameTiming> timings) { |
1218 | assert(debugFrameWasSentToEngine); |
1219 | if (!kReleaseMode) { |
1220 | // Change the current user tag back to the default tag. At this point, |
1221 | // the user tag should be set to "AppStartUp" (originally set in the |
1222 | // engine), so we need to change it back to the default tag to mark |
1223 | // the end of app start up for CPU profiles. |
1224 | developer.UserTag.defaultTag.makeCurrent(); |
1225 | developer.Timeline.instantSync('Rasterized first useful frame'); |
1226 | developer.postEvent('Flutter.FirstFrame', <String, dynamic>{}); |
1227 | } |
1228 | SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!); |
1229 | firstFrameCallback = null; |
1230 | _firstFrameCompleter.complete(); |
1231 | }; |
1232 | // Callback is only invoked when FlutterView.render is called. When |
1233 | // sendFramesToEngine is set to false during the frame, it will not be |
1234 | // called and we need to remove the callback (see below). |
1235 | SchedulerBinding.instance.addTimingsCallback(firstFrameCallback!); |
1236 | } |
1237 | |
1238 | try { |
1239 | if (rootElement != null) { |
1240 | buildOwner!.buildScope(rootElement!); |
1241 | } |
1242 | super.drawFrame(); |
1243 | assert(() { |
1244 | debugFrameWasSentToEngine = sendFramesToEngine; |
1245 | return true; |
1246 | }()); |
1247 | buildOwner!.finalizeTree(); |
1248 | } finally { |
1249 | assert(() { |
1250 | debugBuildingDirtyElements = false; |
1251 | return true; |
1252 | }()); |
1253 | } |
1254 | if (!kReleaseMode) { |
1255 | if (_needToReportFirstFrame && sendFramesToEngine) { |
1256 | developer.Timeline.instantSync('Widgets built first useful frame'); |
1257 | } |
1258 | } |
1259 | _needToReportFirstFrame = false; |
1260 | if (firstFrameCallback != null && !sendFramesToEngine) { |
1261 | // This frame is deferred and not the first frame sent to the engine that |
1262 | // should be reported. |
1263 | _needToReportFirstFrame = true; |
1264 | SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!); |
1265 | } |
1266 | } |
1267 | |
1268 | /// The [Element] that is at the root of the element tree hierarchy. |
1269 | /// |
1270 | /// This is initialized the first time [runApp] is called. |
1271 | Element? get rootElement => _rootElement; |
1272 | Element? _rootElement; |
1273 | |
1274 | /// Deprecated. Will be removed in a future version of Flutter. |
1275 | /// |
1276 | /// Use [rootElement] instead. |
1277 | @Deprecated( |
1278 | 'Use rootElement instead. ' |
1279 | 'This feature was deprecated after v3.9.0-16.0.pre.', |
1280 | ) |
1281 | Element? get renderViewElement => rootElement; |
1282 | |
1283 | bool _readyToProduceFrames = false; |
1284 | |
1285 | @override |
1286 | bool get framesEnabled => super.framesEnabled && _readyToProduceFrames; |
1287 | |
1288 | /// Used by [runApp] to wrap the provided `rootWidget` in the default [View]. |
1289 | /// |
1290 | /// The [View] determines into what [FlutterView] the app is rendered into. |
1291 | /// This is currently [PlatformDispatcher.implicitView] from [platformDispatcher]. |
1292 | /// This method will throw a [StateError] if the [PlatformDispatcher.implicitView] |
1293 | /// is null. |
1294 | /// |
1295 | /// The `rootWidget` widget provided to this method must not already be |
1296 | /// wrapped in a [View]. |
1297 | Widget wrapWithDefaultView(Widget rootWidget) { |
1298 | if (platformDispatcher.implicitView == null) { |
1299 | throw StateError( |
1300 | 'The app requested a view, but the platform did not provide one.\n' |
1301 | 'This is likely because the app called `runApp` to render its root ' |
1302 | 'widget, which expects the platform to provide a default view to ' |
1303 | 'render into (the "implicit" view).\n' |
1304 | 'However, the platform likely has multi-view mode enabled, which does ' |
1305 | 'not create this default "implicit" view.\n' |
1306 | 'Try using `runWidget` instead of `runApp` to start your app.\n' |
1307 | '`runWidget` allows you to provide a `View` widget, without requiring ' |
1308 | 'a default view.' |
1309 | '${kIsWeb ? "\nSee: https://flutter.dev/to/web-multiview-runwidget" : ""}', |
1310 | ); |
1311 | } |
1312 | |
1313 | return View( |
1314 | view: platformDispatcher.implicitView!, |
1315 | deprecatedDoNotUseWillBeRemovedWithoutNoticePipelineOwner: pipelineOwner, |
1316 | deprecatedDoNotUseWillBeRemovedWithoutNoticeRenderView: renderView, |
1317 | child: rootWidget, |
1318 | ); |
1319 | } |
1320 | |
1321 | /// Schedules a [Timer] for attaching the root widget. |
1322 | /// |
1323 | /// This is called by [runApp] to configure the widget tree. Consider using |
1324 | /// [attachRootWidget] if you want to build the widget tree synchronously. |
1325 | @protected |
1326 | void scheduleAttachRootWidget(Widget rootWidget) { |
1327 | Timer.run(() { |
1328 | attachRootWidget(rootWidget); |
1329 | }); |
1330 | } |
1331 | |
1332 | /// Takes a widget and attaches it to the [rootElement], creating it if |
1333 | /// necessary. |
1334 | /// |
1335 | /// This is called by [runApp] to configure the widget tree. |
1336 | /// |
1337 | /// See also: |
1338 | /// |
1339 | /// * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a |
1340 | /// widget and attaches it to the render tree. |
1341 | void attachRootWidget(Widget rootWidget) { |
1342 | attachToBuildOwner(RootWidget(debugShortDescription: '[root]', child: rootWidget)); |
1343 | } |
1344 | |
1345 | /// Called by [attachRootWidget] to attach the provided [RootWidget] to the |
1346 | /// [buildOwner]. |
1347 | /// |
1348 | /// This creates the [rootElement], if necessary, or re-uses an existing one. |
1349 | /// |
1350 | /// This method is rarely called directly, but it can be useful in tests to |
1351 | /// restore the element tree to a previous version by providing the |
1352 | /// [RootWidget] of that version (see [WidgetTester.restartAndRestore] for an |
1353 | /// exemplary use case). |
1354 | void attachToBuildOwner(RootWidget widget) { |
1355 | final bool isBootstrapFrame = rootElement == null; |
1356 | _readyToProduceFrames = true; |
1357 | _rootElement = widget.attach(buildOwner!, rootElement as RootElement?); |
1358 | if (isBootstrapFrame) { |
1359 | SchedulerBinding.instance.ensureVisualUpdate(); |
1360 | } |
1361 | } |
1362 | |
1363 | /// Whether the [rootElement] has been initialized. |
1364 | /// |
1365 | /// This will be false until [runApp] is called (or [WidgetTester.pumpWidget] |
1366 | /// is called in the context of a [TestWidgetsFlutterBinding]). |
1367 | bool get isRootWidgetAttached => _rootElement != null; |
1368 | |
1369 | @override |
1370 | Future<void> performReassemble() { |
1371 | assert(() { |
1372 | WidgetInspectorService.instance.performReassemble(); |
1373 | return true; |
1374 | }()); |
1375 | |
1376 | if (rootElement != null) { |
1377 | buildOwner!.reassemble(rootElement!); |
1378 | } |
1379 | return super.performReassemble(); |
1380 | } |
1381 | |
1382 | /// Computes the locale the current platform would resolve to. |
1383 | /// |
1384 | /// This method is meant to be used as part of a |
1385 | /// [WidgetsApp.localeListResolutionCallback]. Since this method may return |
1386 | /// null, a Flutter/dart algorithm should still be provided as a fallback in |
1387 | /// case a native resolved locale cannot be determined or if the native |
1388 | /// resolved locale is undesirable. |
1389 | /// |
1390 | /// This method may return a null [Locale] if the platform does not support |
1391 | /// native locale resolution, or if the resolution failed. |
1392 | /// |
1393 | /// The first `supportedLocale` is treated as the default locale and will be returned |
1394 | /// if no better match is found. |
1395 | /// |
1396 | /// Android and iOS are currently supported. |
1397 | /// |
1398 | /// On Android, the algorithm described in |
1399 | /// https://developer.android.com/guide/topics/resources/multilingual-support |
1400 | /// is used to determine the resolved locale. Depending on the android version |
1401 | /// of the device, either the modern (>= API 24) or legacy (< API 24) algorithm |
1402 | /// will be used. |
1403 | /// |
1404 | /// On iOS, the result of `preferredLocalizationsFromArray` method of `NSBundle` |
1405 | /// is returned. See: |
1406 | /// https://developer.apple.com/documentation/foundation/nsbundle/1417249-preferredlocalizationsfromarray?language=objc |
1407 | /// for details on the used method. |
1408 | /// |
1409 | /// iOS treats script code as necessary for a match, so a user preferred locale of |
1410 | /// `zh_Hans_CN` will not resolve to a supported locale of `zh_CN`. |
1411 | /// |
1412 | /// Since implementation may vary by platform and has potential to be heavy, |
1413 | /// it is recommended to cache the results of this method if the value is |
1414 | /// used multiple times. |
1415 | /// |
1416 | /// Second-best (and n-best) matching locales should be obtained by calling this |
1417 | /// method again with the matched locale of the first call omitted from |
1418 | /// `supportedLocales`. |
1419 | Locale? computePlatformResolvedLocale(List<Locale> supportedLocales) { |
1420 | return platformDispatcher.computePlatformResolvedLocale(supportedLocales); |
1421 | } |
1422 | } |
1423 | |
1424 | /// Inflate the given widget and attach it to the view. |
1425 | /// |
1426 | // TODO(goderbauer): Update the paragraph below to include the Window widget once that exists. |
1427 | /// The [runApp] method renders the provided `app` widget into the |
1428 | /// [PlatformDispatcher.implicitView] by wrapping it in a [View] widget, which |
1429 | /// will bootstrap the render tree for the app. Apps that want to control which |
1430 | /// [FlutterView] they render into can use [runWidget] instead. |
1431 | /// |
1432 | /// The widget is given constraints during layout that force it to fill the |
1433 | /// entire view. If you wish to align your widget to one side of the view |
1434 | /// (e.g., the top), consider using the [Align] widget. If you wish to center |
1435 | /// your widget, you can also use the [Center] widget. |
1436 | /// |
1437 | /// Calling [runApp] again will detach the previous root widget from the view |
1438 | /// and attach the given widget in its place. The new widget tree is compared |
1439 | /// against the previous widget tree and any differences are applied to the |
1440 | /// underlying render tree, similar to what happens when a [StatefulWidget] |
1441 | /// rebuilds after calling [State.setState]. |
1442 | /// |
1443 | /// Initializes the binding using [WidgetsFlutterBinding] if necessary. |
1444 | /// |
1445 | /// {@template flutter.widgets.runApp.shutdown} |
1446 | /// ## Application shutdown |
1447 | /// |
1448 | /// This widget tree is not torn down when the application shuts down, because |
1449 | /// there is no way to predict when that will happen. For example, a user could |
1450 | /// physically remove power from their device, or the application could crash |
1451 | /// unexpectedly, or the malware on the device could forcibly terminate the |
1452 | /// process. |
1453 | /// |
1454 | /// Applications are responsible for ensuring that they are well-behaved |
1455 | /// even in the face of a rapid unscheduled termination. |
1456 | /// |
1457 | /// To listen for platform shutdown messages (and other lifecycle changes), |
1458 | /// consider the [AppLifecycleListener] API. |
1459 | /// {@endtemplate} |
1460 | /// |
1461 | /// To artificially cause the entire widget tree to be disposed, consider |
1462 | /// calling [runApp] with a widget such as [SizedBox.shrink]. |
1463 | /// |
1464 | /// {@template flutter.widgets.runApp.dismissal} |
1465 | /// ## Dismissing Flutter UI via platform native methods |
1466 | /// |
1467 | /// An application may have both Flutter and non-Flutter UI in it. If the |
1468 | /// application calls non-Flutter methods to remove Flutter based UI such as |
1469 | /// platform native API to manipulate the platform native navigation stack, |
1470 | /// the framework does not know if the developer intends to eagerly free |
1471 | /// resources or not. The widget tree remains mounted and ready to render |
1472 | /// as soon as it is displayed again. |
1473 | /// {@endtemplate} |
1474 | /// |
1475 | /// To release resources more eagerly, establish a [platform channel](https://flutter.dev/to/platform-channels) |
1476 | /// and use it to call [runApp] with a widget such as [SizedBox.shrink] when |
1477 | /// the framework should dispose of the active widget tree. |
1478 | /// |
1479 | /// See also: |
1480 | /// |
1481 | /// * [runWidget], which bootstraps a widget tree without assuming the |
1482 | /// [FlutterView] into which it will be rendered. |
1483 | /// * [WidgetsBinding.attachRootWidget], which creates the root widget for the |
1484 | /// widget hierarchy. |
1485 | /// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root |
1486 | /// element for the element hierarchy. |
1487 | /// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to |
1488 | /// ensure the widget, element, and render trees are all built. |
1489 | void runApp(Widget app) { |
1490 | final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized(); |
1491 | _runWidget(binding.wrapWithDefaultView(app), binding, 'runApp'); |
1492 | } |
1493 | |
1494 | /// Inflate the given widget and bootstrap the widget tree. |
1495 | /// |
1496 | // TODO(goderbauer): Update the paragraph below to include the Window widget once that exists. |
1497 | /// Unlike [runApp], this method does not define a [FlutterView] into which the |
1498 | /// provided `app` widget is rendered into. It is up to the caller to include at |
1499 | /// least one [View] widget in the provided `app` widget that will bootstrap a |
1500 | /// render tree and define the [FlutterView] into which content is rendered. |
1501 | /// [RenderObjectWidget]s without an ancestor [View] widget will result in an |
1502 | /// exception. Apps that want to render into the default view without dealing |
1503 | /// with view management should consider calling [runApp] instead. |
1504 | /// |
1505 | /// {@tool snippet} |
1506 | /// The sample shows how to utilize [runWidget] to specify the [FlutterView] |
1507 | /// into which the `MyApp` widget will be drawn: |
1508 | /// |
1509 | /// ```dart |
1510 | /// runWidget( |
1511 | /// View( |
1512 | /// view: myFlutterView, |
1513 | /// child: const MyApp(), |
1514 | /// ), |
1515 | /// ); |
1516 | /// ``` |
1517 | /// {@end-tool} |
1518 | /// |
1519 | /// Calling [runWidget] again will detach the previous root widget and attach |
1520 | /// the given widget in its place. The new widget tree is compared against the |
1521 | /// previous widget tree and any differences are applied to the underlying |
1522 | /// render tree, similar to what happens when a [StatefulWidget] rebuilds after |
1523 | /// calling [State.setState]. |
1524 | /// |
1525 | /// Initializes the binding using [WidgetsFlutterBinding] if necessary. |
1526 | /// |
1527 | /// {@macro flutter.widgets.runApp.shutdown} |
1528 | /// |
1529 | /// To artificially cause the entire widget tree to be disposed, consider |
1530 | /// calling [runWidget] with a [ViewCollection] that does not specify any |
1531 | /// [ViewCollection.views]. |
1532 | /// |
1533 | /// {@macro flutter.widgets.runApp.dismissal} |
1534 | /// |
1535 | /// To release resources more eagerly, establish a [platform channel](https://flutter.dev/to/platform-channels) |
1536 | /// and use it to remove the [View] whose widget resources should be released |
1537 | /// from the `app` widget tree provided to [runWidget]. |
1538 | /// |
1539 | /// See also: |
1540 | /// |
1541 | /// * [runApp], which bootstraps a widget tree and renders it into a default |
1542 | /// [FlutterView]. |
1543 | /// * [WidgetsBinding.attachRootWidget], which creates the root widget for the |
1544 | /// widget hierarchy. |
1545 | /// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root |
1546 | /// element for the element hierarchy. |
1547 | /// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to |
1548 | /// ensure the widget, element, and render trees are all built. |
1549 | void runWidget(Widget app) { |
1550 | final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized(); |
1551 | _runWidget(app, binding, 'runWidget'); |
1552 | } |
1553 | |
1554 | void _runWidget(Widget app, WidgetsBinding binding, String debugEntryPoint) { |
1555 | assert(binding.debugCheckZone(debugEntryPoint)); |
1556 | binding |
1557 | ..scheduleAttachRootWidget(app) |
1558 | ..scheduleWarmUpFrame(); |
1559 | } |
1560 | |
1561 | String _debugDumpAppString() { |
1562 | const String mode = |
1563 | kDebugMode |
1564 | ? 'DEBUG MODE' |
1565 | : kReleaseMode |
1566 | ? 'RELEASE MODE' |
1567 | : 'PROFILE MODE'; |
1568 | final StringBuffer buffer = StringBuffer(); |
1569 | buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode'); |
1570 | if (WidgetsBinding.instance.rootElement != null) { |
1571 | buffer.writeln(WidgetsBinding.instance.rootElement!.toStringDeep()); |
1572 | } else { |
1573 | buffer.writeln('<no tree currently mounted>'); |
1574 | } |
1575 | return buffer.toString(); |
1576 | } |
1577 | |
1578 | /// Print a string representation of the currently running app. |
1579 | void debugDumpApp() { |
1580 | debugPrint(_debugDumpAppString()); |
1581 | } |
1582 | |
1583 | /// A widget for the root of the widget tree. |
1584 | /// |
1585 | /// Exposes an [attach] method to attach the widget tree to a [BuildOwner]. That |
1586 | /// method also bootstraps the element tree. |
1587 | /// |
1588 | /// Used by [WidgetsBinding.attachRootWidget] (which is indirectly called by |
1589 | /// [runApp]) to bootstrap applications. |
1590 | class RootWidget extends Widget { |
1591 | /// Creates a [RootWidget]. |
1592 | const RootWidget({super.key, this.child, this.debugShortDescription}); |
1593 | |
1594 | /// The widget below this widget in the tree. |
1595 | /// |
1596 | /// {@macro flutter.widgets.ProxyWidget.child} |
1597 | final Widget? child; |
1598 | |
1599 | /// A short description of this widget used by debugging aids. |
1600 | final String? debugShortDescription; |
1601 | |
1602 | @override |
1603 | RootElement createElement() => RootElement(this); |
1604 | |
1605 | /// Inflate this widget and attaches it to the provided [BuildOwner]. |
1606 | /// |
1607 | /// If `element` is null, this function will create a new element. Otherwise, |
1608 | /// the given element will have an update scheduled to switch to this widget. |
1609 | /// |
1610 | /// Used by [WidgetsBinding.attachToBuildOwner] (which is indirectly called by |
1611 | /// [runApp]) to bootstrap applications. |
1612 | RootElement attach(BuildOwner owner, [RootElement? element]) { |
1613 | if (element == null) { |
1614 | owner.lockState(() { |
1615 | element = createElement(); |
1616 | assert(element != null); |
1617 | element!.assignOwner(owner); |
1618 | }); |
1619 | owner.buildScope(element!, () { |
1620 | element!.mount(/* parent */ null, /* slot */ null); |
1621 | }); |
1622 | } else { |
1623 | element._newWidget = this; |
1624 | element.markNeedsBuild(); |
1625 | } |
1626 | return element!; |
1627 | } |
1628 | |
1629 | @override |
1630 | String toStringShort() => debugShortDescription ?? super.toStringShort(); |
1631 | } |
1632 | |
1633 | /// The root of the element tree. |
1634 | /// |
1635 | /// This element class is the instantiation of a [RootWidget]. It can be used |
1636 | /// only as the root of an [Element] tree (it cannot be mounted into another |
1637 | /// [Element]; its parent must be null). |
1638 | /// |
1639 | /// In typical usage, it will be instantiated for a [RootWidget] by calling |
1640 | /// [RootWidget.attach]. In this usage, it is normally instantiated by the |
1641 | /// bootstrapping logic in the [WidgetsFlutterBinding] singleton created by |
1642 | /// [runApp]. |
1643 | class RootElement extends Element with RootElementMixin { |
1644 | /// Creates a [RootElement] for the provided [RootWidget]. |
1645 | RootElement(RootWidget super.widget); |
1646 | |
1647 | Element? _child; |
1648 | |
1649 | @override |
1650 | void visitChildren(ElementVisitor visitor) { |
1651 | if (_child != null) { |
1652 | visitor(_child!); |
1653 | } |
1654 | } |
1655 | |
1656 | @override |
1657 | void forgetChild(Element child) { |
1658 | assert(child == _child); |
1659 | _child = null; |
1660 | super.forgetChild(child); |
1661 | } |
1662 | |
1663 | @override |
1664 | void mount(Element? parent, Object? newSlot) { |
1665 | assert(parent == null); // We are the root! |
1666 | super.mount(parent, newSlot); |
1667 | _rebuild(); |
1668 | assert(_child != null); |
1669 | super.performRebuild(); // clears the "dirty" flag |
1670 | } |
1671 | |
1672 | @override |
1673 | void update(RootWidget newWidget) { |
1674 | super.update(newWidget); |
1675 | assert(widget == newWidget); |
1676 | _rebuild(); |
1677 | } |
1678 | |
1679 | // When we are assigned a new widget, we store it here |
1680 | // until we are ready to update to it. |
1681 | RootWidget? _newWidget; |
1682 | |
1683 | @override |
1684 | void performRebuild() { |
1685 | if (_newWidget != null) { |
1686 | // _newWidget can be null if, for instance, we were rebuilt |
1687 | // due to a reassemble. |
1688 | final RootWidget newWidget = _newWidget!; |
1689 | _newWidget = null; |
1690 | update(newWidget); |
1691 | } |
1692 | super.performRebuild(); |
1693 | assert(_newWidget == null); |
1694 | } |
1695 | |
1696 | void _rebuild() { |
1697 | try { |
1698 | _child = updateChild(_child, (widget as RootWidget).child, /* slot */ null); |
1699 | } catch (exception, stack) { |
1700 | final FlutterErrorDetails details = FlutterErrorDetails( |
1701 | exception: exception, |
1702 | stack: stack, |
1703 | library: 'widgets library', |
1704 | context: ErrorDescription('attaching to the render tree'), |
1705 | ); |
1706 | FlutterError.reportError(details); |
1707 | // No error widget possible here since it wouldn't have a view to render into. |
1708 | _child = null; |
1709 | } |
1710 | } |
1711 | |
1712 | @override |
1713 | bool get debugDoingBuild => false; // This element doesn't have a build phase. |
1714 | |
1715 | @override |
1716 | // There is no ancestor RenderObjectElement that the render object could be attached to. |
1717 | bool debugExpectsRenderObjectForSlot(Object? slot) => false; |
1718 | } |
1719 | |
1720 | /// A concrete binding for applications based on the Widgets framework. |
1721 | /// |
1722 | /// This is the glue that binds the framework to the Flutter engine. |
1723 | /// |
1724 | /// When using the widgets framework, this binding, or one that |
1725 | /// implements the same interfaces, must be used. The following |
1726 | /// mixins are used to implement this binding: |
1727 | /// |
1728 | /// * [GestureBinding], which implements the basics of hit testing. |
1729 | /// * [SchedulerBinding], which introduces the concepts of frames. |
1730 | /// * [ServicesBinding], which provides access to the plugin subsystem. |
1731 | /// * [PaintingBinding], which enables decoding images. |
1732 | /// * [SemanticsBinding], which supports accessibility. |
1733 | /// * [RendererBinding], which handles the render tree. |
1734 | /// * [WidgetsBinding], which handles the widget tree. |
1735 | class WidgetsFlutterBinding extends BindingBase |
1736 | with |
1737 | GestureBinding, |
1738 | SchedulerBinding, |
1739 | ServicesBinding, |
1740 | PaintingBinding, |
1741 | SemanticsBinding, |
1742 | RendererBinding, |
1743 | WidgetsBinding { |
1744 | /// Returns an instance of the binding that implements |
1745 | /// [WidgetsBinding]. If no binding has yet been initialized, the |
1746 | /// [WidgetsFlutterBinding] class is used to create and initialize |
1747 | /// one. |
1748 | /// |
1749 | /// You only need to call this method if you need the binding to be |
1750 | /// initialized before calling [runApp]. |
1751 | /// |
1752 | /// In the `flutter_test` framework, [testWidgets] initializes the |
1753 | /// binding instance to a [TestWidgetsFlutterBinding], not a |
1754 | /// [WidgetsFlutterBinding]. See |
1755 | /// [TestWidgetsFlutterBinding.ensureInitialized]. |
1756 | static WidgetsBinding ensureInitialized() { |
1757 | if (WidgetsBinding._instance == null) { |
1758 | WidgetsFlutterBinding(); |
1759 | } |
1760 | return WidgetsBinding.instance; |
1761 | } |
1762 | } |
1763 |
Definitions
- WidgetsBindingObserver
- didPopRoute
- handleStartBackGesture
- handleUpdateBackGestureProgress
- handleCommitBackGesture
- handleCancelBackGesture
- didPushRoute
- didPushRouteInformation
- didChangeMetrics
- didChangeTextScaleFactor
- didChangePlatformBrightness
- didChangeLocales
- didChangeAppLifecycleState
- didChangeViewFocus
- didRequestAppExit
- didHaveMemoryPressure
- didChangeAccessibilityFeatures
- WidgetsBinding
- initInstances
- instance
- debugShowWidgetInspectorOverride
- debugShowWidgetInspectorOverride
- debugShowWidgetInspectorOverrideNotifier
- debugWidgetInspectorSelectionOnTapEnabled
- resetInternalState
- _debugAddStackFilters
- initServiceExtensions
- _forceRebuild
- buildOwner
- focusManager
- addObserver
- removeObserver
- handleRequestAppExit
- handleMetricsChanged
- handleTextScaleFactorChanged
- handlePlatformBrightnessChanged
- handleAccessibilityFeaturesChanged
- handleLocaleChanged
- dispatchLocalesChanged
- dispatchAccessibilityFeaturesChanged
- handlePopRoute
- _handleStartBackGesture
- _handleUpdateBackGestureProgress
- _handleCommitBackGesture
- _handleCancelBackGesture
- handlePushRoute
- _handlePushRouteInformation
- _handleNavigationInvocation
- _handleBackGestureInvocation
- handleAppLifecycleStateChanged
- handleViewFocusChanged
- handleMemoryPressure
- firstFrameRasterized
- waitUntilFirstFrameRasterized
- debugDidSendFirstFrameEvent
- _handleBuildScheduled
- drawFrame
- rootElement
- renderViewElement
- framesEnabled
Learn more about Flutter for embedded and desktop on industrialflutter.com