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 'package:flutter/cupertino.dart';
6/// @docImport 'package:flutter/material.dart';
7///
8/// @docImport 'heroes.dart';
9/// @docImport 'overlay.dart';
10/// @docImport 'view.dart';
11library;
12
13import 'dart:collection' show HashMap;
14
15import 'package:flutter/foundation.dart';
16import 'package:flutter/rendering.dart';
17import 'package:flutter/services.dart';
18
19import 'actions.dart';
20import 'banner.dart';
21import 'basic.dart';
22import 'binding.dart';
23import 'default_text_editing_shortcuts.dart';
24import 'focus_scope.dart';
25import 'focus_traversal.dart';
26import 'framework.dart';
27import 'localizations.dart';
28import 'media_query.dart';
29import 'navigator.dart';
30import 'notification_listener.dart';
31import 'pages.dart';
32import 'performance_overlay.dart';
33import 'restoration.dart';
34import 'router.dart';
35import 'scrollable_helpers.dart';
36import 'semantics_debugger.dart';
37import 'shared_app_data.dart';
38import 'shortcuts.dart';
39import 'tap_region.dart';
40import 'text.dart';
41import 'title.dart';
42import 'value_listenable_builder.dart';
43import 'widget_inspector.dart';
44
45export 'dart:ui' show Locale;
46
47// Examples can assume:
48// late Widget myWidget;
49
50/// The signature of [WidgetsApp.localeListResolutionCallback].
51///
52/// A [LocaleListResolutionCallback] is responsible for computing the locale of the app's
53/// [Localizations] object when the app starts and when user changes the list of
54/// locales for the device.
55///
56/// The [locales] list is the device's preferred locales when the app started, or the
57/// device's preferred locales the user selected after the app was started. This list
58/// is in order of preference. If this list is null or empty, then Flutter has not yet
59/// received the locale information from the platform. The [supportedLocales] parameter
60/// is just the value of [WidgetsApp.supportedLocales].
61///
62/// See also:
63///
64/// * [LocaleResolutionCallback], which takes only one default locale (instead of a list)
65/// and is attempted only after this callback fails or is null. [LocaleListResolutionCallback]
66/// is recommended over [LocaleResolutionCallback].
67typedef LocaleListResolutionCallback = Locale? Function(List<Locale>? locales, Iterable<Locale> supportedLocales);
68
69/// {@template flutter.widgets.LocaleResolutionCallback}
70/// The signature of [WidgetsApp.localeResolutionCallback].
71///
72/// It is recommended to provide a [LocaleListResolutionCallback] instead of a
73/// [LocaleResolutionCallback] when possible, as [LocaleResolutionCallback] only
74/// receives a subset of the information provided in [LocaleListResolutionCallback].
75///
76/// A [LocaleResolutionCallback] is responsible for computing the locale of the app's
77/// [Localizations] object when the app starts and when user changes the default
78/// locale for the device after [LocaleListResolutionCallback] fails or is not provided.
79///
80/// This callback is also used if the app is created with a specific locale using
81/// the [WidgetsApp.new] `locale` parameter.
82///
83/// The [locale] is either the value of [WidgetsApp.locale], or the device's default
84/// locale when the app started, or the device locale the user selected after the app
85/// was started. The default locale is the first locale in the list of preferred
86/// locales. If [locale] is null, then Flutter has not yet received the locale
87/// information from the platform. The [supportedLocales] parameter is just the value of
88/// [WidgetsApp.supportedLocales].
89///
90/// See also:
91///
92/// * [LocaleListResolutionCallback], which takes a list of preferred locales (instead of one locale).
93/// Resolutions by [LocaleListResolutionCallback] take precedence over [LocaleResolutionCallback].
94/// {@endtemplate}
95typedef LocaleResolutionCallback = Locale? Function(Locale? locale, Iterable<Locale> supportedLocales);
96
97/// The default locale resolution algorithm.
98///
99/// Custom resolution algorithms can be provided through
100/// [WidgetsApp.localeListResolutionCallback] or
101/// [WidgetsApp.localeResolutionCallback].
102///
103/// When no custom locale resolution algorithms are provided or if both fail
104/// to resolve, Flutter will default to calling this algorithm.
105///
106/// This algorithm prioritizes speed at the cost of slightly less appropriate
107/// resolutions for edge cases.
108///
109/// This algorithm will resolve to the earliest preferred locale that
110/// matches the most fields, prioritizing in the order of perfect match,
111/// languageCode+countryCode, languageCode+scriptCode, languageCode-only.
112///
113/// In the case where a locale is matched by languageCode-only and is not the
114/// default (first) locale, the next preferred locale with a
115/// perfect match can supersede the languageCode-only match if it exists.
116///
117/// When a preferredLocale matches more than one supported locale, it will
118/// resolve to the first matching locale listed in the supportedLocales.
119///
120/// When all preferred locales have been exhausted without a match, the first
121/// countryCode only match will be returned.
122///
123/// When no match at all is found, the first (default) locale in
124/// [supportedLocales] will be returned.
125///
126/// To summarize, the main matching priority is:
127///
128/// 1. [Locale.languageCode], [Locale.scriptCode], and [Locale.countryCode]
129/// 2. [Locale.languageCode] and [Locale.scriptCode] only
130/// 3. [Locale.languageCode] and [Locale.countryCode] only
131/// 4. [Locale.languageCode] only (with caveats, see above)
132/// 5. [Locale.countryCode] only when all [preferredLocales] fail to match
133/// 6. Returns the first element of [supportedLocales] as a fallback
134///
135/// This algorithm does not take language distance (how similar languages are to each other)
136/// into account, and will not handle edge cases such as resolving `de` to `fr` rather than `zh`
137/// when `de` is not supported and `zh` is listed before `fr` (German is closer to French
138/// than Chinese).
139Locale basicLocaleListResolution(List<Locale>? preferredLocales, Iterable<Locale> supportedLocales) {
140 // preferredLocales can be null when called before the platform has had a chance to
141 // initialize the locales. Platforms without locale passing support will provide an empty list.
142 // We default to the first supported locale in these cases.
143 if (preferredLocales == null || preferredLocales.isEmpty) {
144 return supportedLocales.first;
145 }
146 // Hash the supported locales because apps can support many locales and would
147 // be expensive to search through them many times.
148 final Map<String, Locale> allSupportedLocales = HashMap<String, Locale>();
149 final Map<String, Locale> languageAndCountryLocales = HashMap<String, Locale>();
150 final Map<String, Locale> languageAndScriptLocales = HashMap<String, Locale>();
151 final Map<String, Locale> languageLocales = HashMap<String, Locale>();
152 final Map<String?, Locale> countryLocales = HashMap<String?, Locale>();
153 for (final Locale locale in supportedLocales) {
154 allSupportedLocales['${locale.languageCode}_${locale.scriptCode}_${locale.countryCode}'] ??= locale;
155 languageAndScriptLocales['${locale.languageCode}_${locale.scriptCode}'] ??= locale;
156 languageAndCountryLocales['${locale.languageCode}_${locale.countryCode}'] ??= locale;
157 languageLocales[locale.languageCode] ??= locale;
158 countryLocales[locale.countryCode] ??= locale;
159 }
160
161 // Since languageCode-only matches are possibly low quality, we don't return
162 // it instantly when we find such a match. We check to see if the next
163 // preferred locale in the list has a high accuracy match, and only return
164 // the languageCode-only match when a higher accuracy match in the next
165 // preferred locale cannot be found.
166 Locale? matchesLanguageCode;
167 Locale? matchesCountryCode;
168 // Loop over user's preferred locales
169 for (int localeIndex = 0; localeIndex < preferredLocales.length; localeIndex += 1) {
170 final Locale userLocale = preferredLocales[localeIndex];
171 // Look for perfect match.
172 if (allSupportedLocales.containsKey('${userLocale.languageCode}_${userLocale.scriptCode}_${userLocale.countryCode}')) {
173 return userLocale;
174 }
175 // Look for language+script match.
176 if (userLocale.scriptCode != null) {
177 final Locale? match = languageAndScriptLocales['${userLocale.languageCode}_${userLocale.scriptCode}'];
178 if (match != null) {
179 return match;
180 }
181 }
182 // Look for language+country match.
183 if (userLocale.countryCode != null) {
184 final Locale? match = languageAndCountryLocales['${userLocale.languageCode}_${userLocale.countryCode}'];
185 if (match != null) {
186 return match;
187 }
188 }
189 // If there was a languageCode-only match in the previous iteration's higher
190 // ranked preferred locale, we return it if the current userLocale does not
191 // have a better match.
192 if (matchesLanguageCode != null) {
193 return matchesLanguageCode;
194 }
195 // Look and store language-only match.
196 Locale? match = languageLocales[userLocale.languageCode];
197 if (match != null) {
198 matchesLanguageCode = match;
199 // Since first (default) locale is usually highly preferred, we will allow
200 // a languageCode-only match to be instantly matched. If the next preferred
201 // languageCode is the same, we defer hastily returning until the next iteration
202 // since at worst it is the same and at best an improved match.
203 if (localeIndex == 0 &&
204 !(localeIndex + 1 < preferredLocales.length && preferredLocales[localeIndex + 1].languageCode == userLocale.languageCode)) {
205 return matchesLanguageCode;
206 }
207 }
208 // countryCode-only match. When all else except default supported locale fails,
209 // attempt to match by country only, as a user is likely to be familiar with a
210 // language from their listed country.
211 if (matchesCountryCode == null && userLocale.countryCode != null) {
212 match = countryLocales[userLocale.countryCode];
213 if (match != null) {
214 matchesCountryCode = match;
215 }
216 }
217 }
218 // When there is no languageCode-only match. Fallback to matching countryCode only. Country
219 // fallback only applies on iOS. When there is no countryCode-only match, we return first
220 // supported locale.
221 final Locale resolvedLocale = matchesLanguageCode ?? matchesCountryCode ?? supportedLocales.first;
222 return resolvedLocale;
223}
224
225/// The signature of [WidgetsApp.onGenerateTitle].
226///
227/// Used to generate a value for the app's [Title.title], which the device uses
228/// to identify the app for the user. The `context` includes the [WidgetsApp]'s
229/// [Localizations] widget so that this method can be used to produce a
230/// localized title.
231///
232/// This function must not return null.
233typedef GenerateAppTitle = String Function(BuildContext context);
234
235/// The signature of [WidgetsApp.pageRouteBuilder].
236///
237/// Creates a [PageRoute] using the given [RouteSettings] and [WidgetBuilder].
238typedef PageRouteFactory = PageRoute<T> Function<T>(RouteSettings settings, WidgetBuilder builder);
239
240/// The signature of [WidgetsApp.onGenerateInitialRoutes].
241///
242/// Creates a series of one or more initial routes.
243typedef InitialRouteListFactory = List<Route<dynamic>> Function(String initialRoute);
244
245/// A convenience widget that wraps a number of widgets that are commonly
246/// required for an application.
247///
248/// One of the primary roles that [WidgetsApp] provides is binding the system
249/// back button to popping the [Navigator] or quitting the application.
250///
251/// It is used by both [MaterialApp] and [CupertinoApp] to implement base
252/// functionality for an app.
253///
254/// Find references to many of the widgets that [WidgetsApp] wraps in the "See
255/// also" section.
256///
257/// See also:
258///
259/// * [CheckedModeBanner], which displays a [Banner] saying "DEBUG" when
260/// running in debug mode.
261/// * [DefaultTextStyle], the text style to apply to descendant [Text] widgets
262/// without an explicit style.
263/// * [MediaQuery], which establishes a subtree in which media queries resolve
264/// to a [MediaQueryData].
265/// * [Localizations], which defines the [Locale] for its `child`.
266/// * [Title], a widget that describes this app in the operating system.
267/// * [Navigator], a widget that manages a set of child widgets with a stack
268/// discipline.
269/// * [Overlay], a widget that manages a [Stack] of entries that can be managed
270/// independently.
271/// * [SemanticsDebugger], a widget that visualizes the semantics for the child.
272class WidgetsApp extends StatefulWidget {
273 /// Creates a widget that wraps a number of widgets that are commonly
274 /// required for an application.
275 ///
276 /// Most callers will want to use the [home] or [routes] parameters, or both.
277 /// The [home] parameter is a convenience for the following [routes] map:
278 ///
279 /// ```dart
280 /// <String, WidgetBuilder>{ '/': (BuildContext context) => myWidget }
281 /// ```
282 ///
283 /// It is possible to specify both [home] and [routes], but only if [routes] does
284 /// _not_ contain an entry for `'/'`. Conversely, if [home] is omitted, [routes]
285 /// _must_ contain an entry for `'/'`.
286 ///
287 /// If [home] or [routes] are not null, the routing implementation needs to know how
288 /// appropriately build [PageRoute]s. This can be achieved by supplying the
289 /// [pageRouteBuilder] parameter. The [pageRouteBuilder] is used by [MaterialApp]
290 /// and [CupertinoApp] to create [MaterialPageRoute]s and [CupertinoPageRoute],
291 /// respectively.
292 ///
293 /// The [builder] parameter is designed to provide the ability to wrap the visible
294 /// content of the app in some other widget. It is recommended that you use [home]
295 /// rather than [builder] if you intend to only display a single route in your app.
296 ///
297 /// [WidgetsApp] is also possible to provide a custom implementation of routing via the
298 /// [onGenerateRoute] and [onUnknownRoute] parameters. These parameters correspond
299 /// to [Navigator.onGenerateRoute] and [Navigator.onUnknownRoute]. If [home], [routes],
300 /// and [builder] are null, or if they fail to create a requested route,
301 /// [onGenerateRoute] will be invoked. If that fails, [onUnknownRoute] will be invoked.
302 ///
303 /// The [pageRouteBuilder] is called to create a [PageRoute] that wraps newly built routes.
304 /// If the [builder] is non-null and the [onGenerateRoute] argument is null, then the
305 /// [builder] will be provided only with the context and the child widget, whereas
306 /// the [pageRouteBuilder] will be provided with [RouteSettings]; in that configuration,
307 /// the [navigatorKey], [onUnknownRoute], [navigatorObservers], and
308 /// [initialRoute] properties must have their default values, as they will have no effect.
309 ///
310 /// The `supportedLocales` argument must be a list of one or more elements.
311 /// By default supportedLocales is `[const Locale('en', 'US')]`.
312 ///
313 /// {@tool dartpad}
314 /// This sample shows a basic Flutter application using [WidgetsApp].
315 ///
316 /// ** See code in examples/api/lib/widgets/app/widgets_app.widgets_app.0.dart **
317 /// {@end-tool}
318 WidgetsApp({ // can't be const because the asserts use methods on Iterable :-(
319 super.key,
320 this.navigatorKey,
321 this.onGenerateRoute,
322 this.onGenerateInitialRoutes,
323 this.onUnknownRoute,
324 this.onNavigationNotification,
325 List<NavigatorObserver> this.navigatorObservers = const <NavigatorObserver>[],
326 this.initialRoute,
327 this.pageRouteBuilder,
328 this.home,
329 Map<String, WidgetBuilder> this.routes = const <String, WidgetBuilder>{},
330 this.builder,
331 this.title,
332 this.onGenerateTitle,
333 this.textStyle,
334 required this.color,
335 this.locale,
336 this.localizationsDelegates,
337 this.localeListResolutionCallback,
338 this.localeResolutionCallback,
339 this.supportedLocales = const <Locale>[Locale('en', 'US')],
340 this.showPerformanceOverlay = false,
341 this.showSemanticsDebugger = false,
342 this.debugShowWidgetInspector = false,
343 this.debugShowCheckedModeBanner = true,
344 this.inspectorSelectButtonBuilder,
345 this.shortcuts,
346 this.actions,
347 this.restorationScopeId,
348 @Deprecated(
349 'Remove this parameter as it is now ignored. '
350 'WidgetsApp never introduces its own MediaQuery; the View widget takes care of that. '
351 'This feature was deprecated after v3.7.0-29.0.pre.'
352 )
353 this.useInheritedMediaQuery = false,
354 }) : assert(
355 home == null ||
356 onGenerateInitialRoutes == null,
357 'If onGenerateInitialRoutes is specified, the home argument will be '
358 'redundant.',
359 ),
360 assert(
361 home == null ||
362 !routes.containsKey(Navigator.defaultRouteName),
363 'If the home property is specified, the routes table '
364 'cannot include an entry for "/", since it would be redundant.',
365 ),
366 assert(
367 builder != null ||
368 home != null ||
369 routes.containsKey(Navigator.defaultRouteName) ||
370 onGenerateRoute != null ||
371 onUnknownRoute != null,
372 'Either the home property must be specified, '
373 'or the routes table must include an entry for "/", '
374 'or there must be on onGenerateRoute callback specified, '
375 'or there must be an onUnknownRoute callback specified, '
376 'or the builder property must be specified, '
377 'because otherwise there is nothing to fall back on if the '
378 'app is started with an intent that specifies an unknown route.',
379 ),
380 assert(
381 (home != null ||
382 routes.isNotEmpty ||
383 onGenerateRoute != null ||
384 onUnknownRoute != null)
385 ||
386 (builder != null &&
387 navigatorKey == null &&
388 initialRoute == null &&
389 navigatorObservers.isEmpty),
390 'If no route is provided using '
391 'home, routes, onGenerateRoute, or onUnknownRoute, '
392 'a non-null callback for the builder property must be provided, '
393 'and the other navigator-related properties, '
394 'navigatorKey, initialRoute, and navigatorObservers, '
395 'must have their initial values '
396 '(null, null, and the empty list, respectively).',
397 ),
398 assert(
399 builder != null ||
400 onGenerateRoute != null ||
401 pageRouteBuilder != null,
402 'If neither builder nor onGenerateRoute are provided, the '
403 'pageRouteBuilder must be specified so that the default handler '
404 'will know what kind of PageRoute transition to build.',
405 ),
406 assert(supportedLocales.isNotEmpty),
407 routeInformationProvider = null,
408 routeInformationParser = null,
409 routerDelegate = null,
410 backButtonDispatcher = null,
411 routerConfig = null;
412
413 /// Creates a [WidgetsApp] that uses the [Router] instead of a [Navigator].
414 ///
415 /// {@template flutter.widgets.WidgetsApp.router}
416 /// If the [routerConfig] is provided, the other router related delegates,
417 /// [routeInformationParser], [routeInformationProvider], [routerDelegate],
418 /// and [backButtonDispatcher], must all be null.
419 /// {@endtemplate}
420 WidgetsApp.router({
421 super.key,
422 this.routeInformationProvider,
423 this.routeInformationParser,
424 this.routerDelegate,
425 this.routerConfig,
426 this.backButtonDispatcher,
427 this.builder,
428 this.title,
429 this.onGenerateTitle,
430 this.onNavigationNotification,
431 this.textStyle,
432 required this.color,
433 this.locale,
434 this.localizationsDelegates,
435 this.localeListResolutionCallback,
436 this.localeResolutionCallback,
437 this.supportedLocales = const <Locale>[Locale('en', 'US')],
438 this.showPerformanceOverlay = false,
439 this.showSemanticsDebugger = false,
440 this.debugShowWidgetInspector = false,
441 this.debugShowCheckedModeBanner = true,
442 this.inspectorSelectButtonBuilder,
443 this.shortcuts,
444 this.actions,
445 this.restorationScopeId,
446 @Deprecated(
447 'Remove this parameter as it is now ignored. '
448 'WidgetsApp never introduces its own MediaQuery; the View widget takes care of that. '
449 'This feature was deprecated after v3.7.0-29.0.pre.'
450 )
451 this.useInheritedMediaQuery = false,
452 }) : assert((){
453 if (routerConfig != null) {
454 assert(
455 (routeInformationProvider ?? routeInformationParser ?? routerDelegate ?? backButtonDispatcher) == null,
456 'If the routerConfig is provided, all the other router delegates must not be provided',
457 );
458 return true;
459 }
460 assert(routerDelegate != null, 'Either one of routerDelegate or routerConfig must be provided');
461 assert(
462 routeInformationProvider == null || routeInformationParser != null,
463 'If routeInformationProvider is provided, routeInformationParser must also be provided',
464 );
465 return true;
466 }()),
467 assert(supportedLocales.isNotEmpty),
468 navigatorObservers = null,
469 navigatorKey = null,
470 onGenerateRoute = null,
471 pageRouteBuilder = null,
472 home = null,
473 onGenerateInitialRoutes = null,
474 onUnknownRoute = null,
475 routes = null,
476 initialRoute = null;
477
478 /// {@template flutter.widgets.widgetsApp.navigatorKey}
479 /// A key to use when building the [Navigator].
480 ///
481 /// If a [navigatorKey] is specified, the [Navigator] can be directly
482 /// manipulated without first obtaining it from a [BuildContext] via
483 /// [Navigator.of]: from the [navigatorKey], use the [GlobalKey.currentState]
484 /// getter.
485 ///
486 /// If this is changed, a new [Navigator] will be created, losing all the
487 /// application state in the process; in that case, the [navigatorObservers]
488 /// must also be changed, since the previous observers will be attached to the
489 /// previous navigator.
490 ///
491 /// The [Navigator] is only built if [onGenerateRoute] is not null; if it is
492 /// null, [navigatorKey] must also be null.
493 /// {@endtemplate}
494 final GlobalKey<NavigatorState>? navigatorKey;
495
496 /// {@template flutter.widgets.widgetsApp.onGenerateRoute}
497 /// The route generator callback used when the app is navigated to a
498 /// named route.
499 ///
500 /// If this returns null when building the routes to handle the specified
501 /// [initialRoute], then all the routes are discarded and
502 /// [Navigator.defaultRouteName] is used instead (`/`). See [initialRoute].
503 ///
504 /// During normal app operation, the [onGenerateRoute] callback will only be
505 /// applied to route names pushed by the application, and so should never
506 /// return null.
507 ///
508 /// This is used if [routes] does not contain the requested route.
509 ///
510 /// The [Navigator] is only built if routes are provided (either via [home],
511 /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
512 /// [builder] must not be null.
513 /// {@endtemplate}
514 ///
515 /// If this property is not set, either the [routes] or [home] properties must
516 /// be set, and the [pageRouteBuilder] must also be set so that the
517 /// default handler will know what routes and [PageRoute]s to build.
518 final RouteFactory? onGenerateRoute;
519
520 /// {@template flutter.widgets.widgetsApp.onGenerateInitialRoutes}
521 /// The routes generator callback used for generating initial routes if
522 /// [initialRoute] is provided.
523 ///
524 /// If this property is not set, the underlying
525 /// [Navigator.onGenerateInitialRoutes] will default to
526 /// [Navigator.defaultGenerateInitialRoutes].
527 /// {@endtemplate}
528 final InitialRouteListFactory? onGenerateInitialRoutes;
529
530 /// The [PageRoute] generator callback used when the app is navigated to a
531 /// named route.
532 ///
533 /// A [PageRoute] represents the page in a [Navigator], so that it can
534 /// correctly animate between pages, and to represent the "return value" of
535 /// a route (e.g. which button a user selected in a modal dialog).
536 ///
537 /// This callback can be used, for example, to specify that a [MaterialPageRoute]
538 /// or a [CupertinoPageRoute] should be used for building page transitions.
539 ///
540 /// The [PageRouteFactory] type is generic, meaning the provided function must
541 /// itself be generic. For example (with special emphasis on the `<T>` at the
542 /// start of the closure):
543 ///
544 /// ```dart
545 /// pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) => PageRouteBuilder<T>(
546 /// settings: settings,
547 /// pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => builder(context),
548 /// ),
549 /// ```
550 final PageRouteFactory? pageRouteBuilder;
551
552 /// {@template flutter.widgets.widgetsApp.routeInformationParser}
553 /// A delegate to parse the route information from the
554 /// [routeInformationProvider] into a generic data type to be processed by
555 /// the [routerDelegate] at a later stage.
556 ///
557 /// This object will be used by the underlying [Router].
558 ///
559 /// The generic type `T` must match the generic type of the [routerDelegate].
560 ///
561 /// See also:
562 ///
563 /// * [Router.routeInformationParser], which receives this object when this
564 /// widget builds the [Router].
565 /// {@endtemplate}
566 final RouteInformationParser<Object>? routeInformationParser;
567
568 /// {@template flutter.widgets.widgetsApp.routerDelegate}
569 /// A delegate that configures a widget, typically a [Navigator], with
570 /// parsed result from the [routeInformationParser].
571 ///
572 /// This object will be used by the underlying [Router].
573 ///
574 /// The generic type `T` must match the generic type of the
575 /// [routeInformationParser].
576 ///
577 /// See also:
578 ///
579 /// * [Router.routerDelegate], which receives this object when this widget
580 /// builds the [Router].
581 /// {@endtemplate}
582 final RouterDelegate<Object>? routerDelegate;
583
584 /// {@template flutter.widgets.widgetsApp.backButtonDispatcher}
585 /// A delegate that decide whether to handle the Android back button intent.
586 ///
587 /// This object will be used by the underlying [Router].
588 ///
589 /// If this is not provided, the widgets app will create a
590 /// [RootBackButtonDispatcher] by default.
591 ///
592 /// See also:
593 ///
594 /// * [Router.backButtonDispatcher], which receives this object when this
595 /// widget builds the [Router].
596 /// {@endtemplate}
597 final BackButtonDispatcher? backButtonDispatcher;
598
599 /// {@template flutter.widgets.widgetsApp.routeInformationProvider}
600 /// A object that provides route information through the
601 /// [RouteInformationProvider.value] and notifies its listener when its value
602 /// changes.
603 ///
604 /// This object will be used by the underlying [Router].
605 ///
606 /// If this is not provided, the widgets app will create a
607 /// [PlatformRouteInformationProvider] with initial route name equal to the
608 /// [dart:ui.PlatformDispatcher.defaultRouteName] by default.
609 ///
610 /// See also:
611 ///
612 /// * [Router.routeInformationProvider], which receives this object when this
613 /// widget builds the [Router].
614 /// {@endtemplate}
615 final RouteInformationProvider? routeInformationProvider;
616
617 /// {@template flutter.widgets.widgetsApp.routerConfig}
618 /// An object to configure the underlying [Router].
619 ///
620 /// If the [routerConfig] is provided, the other router related delegates,
621 /// [routeInformationParser], [routeInformationProvider], [routerDelegate],
622 /// and [backButtonDispatcher], must all be null.
623 ///
624 /// See also:
625 ///
626 /// * [Router.withConfig], which receives this object when this
627 /// widget builds the [Router].
628 /// {@endtemplate}
629 final RouterConfig<Object>? routerConfig;
630
631 /// {@template flutter.widgets.widgetsApp.home}
632 /// The widget for the default route of the app ([Navigator.defaultRouteName],
633 /// which is `/`).
634 ///
635 /// This is the route that is displayed first when the application is started
636 /// normally, unless [initialRoute] is specified. It's also the route that's
637 /// displayed if the [initialRoute] can't be displayed.
638 ///
639 /// To be able to directly call [Theme.of], [MediaQuery.of], etc, in the code
640 /// that sets the [home] argument in the constructor, you can use a [Builder]
641 /// widget to get a [BuildContext].
642 ///
643 /// If [home] is specified, then [routes] must not include an entry for `/`,
644 /// as [home] takes its place.
645 ///
646 /// The [Navigator] is only built if routes are provided (either via [home],
647 /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
648 /// [builder] must not be null.
649 ///
650 /// The difference between using [home] and using [builder] is that the [home]
651 /// subtree is inserted into the application below a [Navigator] (and thus
652 /// below an [Overlay], which [Navigator] uses). With [home], therefore,
653 /// dialog boxes will work automatically, the [routes] table will be used, and
654 /// APIs such as [Navigator.push] and [Navigator.pop] will work as expected.
655 /// In contrast, the widget returned from [builder] is inserted _above_ the
656 /// app's [Navigator] (if any).
657 /// {@endtemplate}
658 ///
659 /// If this property is set, the [pageRouteBuilder] property must also be set
660 /// so that the default route handler will know what kind of [PageRoute]s to
661 /// build.
662 final Widget? home;
663
664 /// The application's top-level routing table.
665 ///
666 /// When a named route is pushed with [Navigator.pushNamed], the route name is
667 /// looked up in this map. If the name is present, the associated
668 /// [WidgetBuilder] is used to construct a [PageRoute] specified by
669 /// [pageRouteBuilder] to perform an appropriate transition, including [Hero]
670 /// animations, to the new route.
671 ///
672 /// {@template flutter.widgets.widgetsApp.routes}
673 /// If the app only has one page, then you can specify it using [home] instead.
674 ///
675 /// If [home] is specified, then it implies an entry in this table for the
676 /// [Navigator.defaultRouteName] route (`/`), and it is an error to
677 /// redundantly provide such a route in the [routes] table.
678 ///
679 /// If a route is requested that is not specified in this table (or by
680 /// [home]), then the [onGenerateRoute] callback is called to build the page
681 /// instead.
682 ///
683 /// The [Navigator] is only built if routes are provided (either via [home],
684 /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
685 /// [builder] must not be null.
686 /// {@endtemplate}
687 ///
688 /// If the routes map is not empty, the [pageRouteBuilder] property must be set
689 /// so that the default route handler will know what kind of [PageRoute]s to
690 /// build.
691 final Map<String, WidgetBuilder>? routes;
692
693 /// {@template flutter.widgets.widgetsApp.onUnknownRoute}
694 /// Called when [onGenerateRoute] fails to generate a route, except for the
695 /// [initialRoute].
696 ///
697 /// This callback is typically used for error handling. For example, this
698 /// callback might always generate a "not found" page that describes the route
699 /// that wasn't found.
700 ///
701 /// Unknown routes can arise either from errors in the app or from external
702 /// requests to push routes, such as from Android intents.
703 ///
704 /// The [Navigator] is only built if routes are provided (either via [home],
705 /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
706 /// [builder] must not be null.
707 /// {@endtemplate}
708 final RouteFactory? onUnknownRoute;
709
710 /// {@template flutter.widgets.widgetsApp.onNavigationNotification}
711 /// The callback to use when receiving a [NavigationNotification].
712 ///
713 /// By default this updates the engine with the navigation status and stops
714 /// bubbling the notification.
715 ///
716 /// See also:
717 ///
718 /// * [NotificationListener.onNotification], which uses this callback.
719 /// {@endtemplate}
720 final NotificationListenerCallback<NavigationNotification>? onNavigationNotification;
721
722 /// {@template flutter.widgets.widgetsApp.initialRoute}
723 /// The name of the first route to show, if a [Navigator] is built.
724 ///
725 /// Defaults to [dart:ui.PlatformDispatcher.defaultRouteName], which may be
726 /// overridden by the code that launched the application.
727 ///
728 /// If the route name starts with a slash, then it is treated as a "deep link",
729 /// and before this route is pushed, the routes leading to this one are pushed
730 /// also. For example, if the route was `/a/b/c`, then the app would start
731 /// with the four routes `/`, `/a`, `/a/b`, and `/a/b/c` loaded, in that order.
732 /// Even if the route was just `/a`, the app would start with `/` and `/a`
733 /// loaded. You can use the [onGenerateInitialRoutes] property to override
734 /// this behavior.
735 ///
736 /// Intermediate routes aren't required to exist. In the example above, `/a`
737 /// and `/a/b` could be skipped if they have no matching route. But `/a/b/c` is
738 /// required to have a route, else [initialRoute] is ignored and
739 /// [Navigator.defaultRouteName] is used instead (`/`). This can happen if the
740 /// app is started with an intent that specifies a non-existent route.
741 ///
742 /// The [Navigator] is only built if routes are provided (either via [home],
743 /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
744 /// [initialRoute] must be null and [builder] must not be null.
745 ///
746 /// Changing the [initialRoute] will have no effect, as it only controls the
747 /// _initial_ route. To change the route while the application is running, use
748 /// the [Navigator] or [Router] APIs.
749 ///
750 /// See also:
751 ///
752 /// * [Navigator.initialRoute], which is used to implement this property.
753 /// * [Navigator.push], for pushing additional routes.
754 /// * [Navigator.pop], for removing a route from the stack.
755 ///
756 /// {@endtemplate}
757 final String? initialRoute;
758
759 /// {@template flutter.widgets.widgetsApp.navigatorObservers}
760 /// The list of observers for the [Navigator] created for this app.
761 ///
762 /// This list must be replaced by a list of newly-created observers if the
763 /// [navigatorKey] is changed.
764 ///
765 /// The [Navigator] is only built if routes are provided (either via [home],
766 /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not,
767 /// [navigatorObservers] must be the empty list and [builder] must not be null.
768 /// {@endtemplate}
769 final List<NavigatorObserver>? navigatorObservers;
770
771 /// {@template flutter.widgets.widgetsApp.builder}
772 /// A builder for inserting widgets above the [Navigator] or - when the
773 /// [WidgetsApp.router] constructor is used - above the [Router] but below the
774 /// other widgets created by the [WidgetsApp] widget, or for replacing the
775 /// [Navigator]/[Router] entirely.
776 ///
777 /// For example, from the [BuildContext] passed to this method, the
778 /// [Directionality], [Localizations], [DefaultTextStyle], [MediaQuery], etc,
779 /// are all available. They can also be overridden in a way that impacts all
780 /// the routes in the [Navigator] or [Router].
781 ///
782 /// This is rarely useful, but can be used in applications that wish to
783 /// override those defaults, e.g. to force the application into right-to-left
784 /// mode despite being in English, or to override the [MediaQuery] metrics
785 /// (e.g. to leave a gap for advertisements shown by a plugin from OEM code).
786 ///
787 /// For specifically overriding the [title] with a value based on the
788 /// [Localizations], consider [onGenerateTitle] instead.
789 ///
790 /// The [builder] callback is passed two arguments, the [BuildContext] (as
791 /// `context`) and a [Navigator] or [Router] widget (as `child`).
792 ///
793 /// If no routes are provided to the regular [WidgetsApp] constructor using
794 /// [home], [routes], [onGenerateRoute], or [onUnknownRoute], the `child` will
795 /// be null, and it is the responsibility of the [builder] to provide the
796 /// application's routing machinery.
797 ///
798 /// If routes _are_ provided to the regular [WidgetsApp] constructor using one
799 /// or more of those properties or if the [WidgetsApp.router] constructor is
800 /// used, then `child` is not null, and the returned value should include the
801 /// `child` in the widget subtree; if it does not, then the application will
802 /// have no [Navigator] or [Router] and the routing related properties (i.e.
803 /// [navigatorKey], [home], [routes], [onGenerateRoute], [onUnknownRoute],
804 /// [initialRoute], [navigatorObservers], [routeInformationProvider],
805 /// [backButtonDispatcher], [routerDelegate], and [routeInformationParser])
806 /// are ignored.
807 ///
808 /// If [builder] is null, it is as if a builder was specified that returned
809 /// the `child` directly. If it is null, routes must be provided using one of
810 /// the other properties listed above.
811 ///
812 /// Unless a [Navigator] is provided, either implicitly from [builder] being
813 /// null, or by a [builder] including its `child` argument, or by a [builder]
814 /// explicitly providing a [Navigator] of its own, or by the [routerDelegate]
815 /// building one, widgets and APIs such as [Hero], [Navigator.push] and
816 /// [Navigator.pop], will not function.
817 /// {@endtemplate}
818 final TransitionBuilder? builder;
819
820 /// {@template flutter.widgets.widgetsApp.title}
821 /// A one-line description used by the device to identify the app for the user.
822 ///
823 /// On Android the titles appear above the task manager's app snapshots which are
824 /// displayed when the user presses the "recent apps" button. On iOS this
825 /// value cannot be used. `CFBundleDisplayName` from the app's `Info.plist` is
826 /// referred to instead whenever present, `CFBundleName` otherwise.
827 /// On the web it is used as the page title, which shows up in the browser's list of open tabs.
828 ///
829 /// To provide a localized title instead, use [onGenerateTitle].
830 /// {@endtemplate}
831 final String? title;
832
833 /// {@template flutter.widgets.widgetsApp.onGenerateTitle}
834 /// If non-null this callback function is called to produce the app's
835 /// title string, otherwise [title] is used.
836 ///
837 /// The [onGenerateTitle] `context` parameter includes the [WidgetsApp]'s
838 /// [Localizations] widget so that this callback can be used to produce a
839 /// localized title.
840 ///
841 /// This callback function must not return null.
842 ///
843 /// The [onGenerateTitle] callback is called each time the [WidgetsApp]
844 /// rebuilds.
845 /// {@endtemplate}
846 final GenerateAppTitle? onGenerateTitle;
847
848 /// The default text style for [Text] in the application.
849 final TextStyle? textStyle;
850
851 /// {@template flutter.widgets.widgetsApp.color}
852 /// The primary color to use for the application in the operating system
853 /// interface.
854 ///
855 /// For example, on Android this is the color used for the application in the
856 /// application switcher.
857 /// {@endtemplate}
858 final Color color;
859
860 /// {@template flutter.widgets.widgetsApp.locale}
861 /// The initial locale for this app's [Localizations] widget is based
862 /// on this value.
863 ///
864 /// If the 'locale' is null then the system's locale value is used.
865 ///
866 /// The value of [Localizations.locale] will equal this locale if
867 /// it matches one of the [supportedLocales]. Otherwise it will be
868 /// the first element of [supportedLocales].
869 /// {@endtemplate}
870 ///
871 /// See also:
872 ///
873 /// * [localeResolutionCallback], which can override the default
874 /// [supportedLocales] matching algorithm.
875 /// * [localizationsDelegates], which collectively define all of the localized
876 /// resources used by this app.
877 final Locale? locale;
878
879 /// {@template flutter.widgets.widgetsApp.localizationsDelegates}
880 /// The delegates for this app's [Localizations] widget.
881 ///
882 /// The delegates collectively define all of the localized resources
883 /// for this application's [Localizations] widget.
884 /// {@endtemplate}
885 final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
886
887 /// {@template flutter.widgets.widgetsApp.localeListResolutionCallback}
888 /// This callback is responsible for choosing the app's locale
889 /// when the app is started, and when the user changes the
890 /// device's locale.
891 ///
892 /// When a [localeListResolutionCallback] is provided, Flutter will first
893 /// attempt to resolve the locale with the provided
894 /// [localeListResolutionCallback]. If the callback or result is null, it will
895 /// fallback to trying the [localeResolutionCallback]. If both
896 /// [localeResolutionCallback] and [localeListResolutionCallback] are left
897 /// null or fail to resolve (return null), basic fallback algorithm will
898 /// be used.
899 ///
900 /// The priority of each available fallback is:
901 ///
902 /// 1. [localeListResolutionCallback] is attempted.
903 /// 2. [localeResolutionCallback] is attempted.
904 /// 3. Flutter's basic resolution algorithm, as described in
905 /// [supportedLocales], is attempted last.
906 ///
907 /// Properly localized projects should provide a more advanced algorithm than
908 /// the basic method from [supportedLocales], as it does not implement a
909 /// complete algorithm (such as the one defined in
910 /// [Unicode TR35](https://unicode.org/reports/tr35/#LanguageMatching))
911 /// and is optimized for speed at the detriment of some uncommon edge-cases.
912 /// {@endtemplate}
913 ///
914 /// This callback considers the entire list of preferred locales.
915 ///
916 /// This algorithm should be able to handle a null or empty list of preferred locales,
917 /// which indicates Flutter has not yet received locale information from the platform.
918 ///
919 /// See also:
920 ///
921 /// * [MaterialApp.localeListResolutionCallback], which sets the callback of the
922 /// [WidgetsApp] it creates.
923 /// * [basicLocaleListResolution], the default locale resolution algorithm.
924 final LocaleListResolutionCallback? localeListResolutionCallback;
925
926 /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
927 ///
928 /// This callback considers only the default locale, which is the first locale
929 /// in the preferred locales list. It is preferred to set [localeListResolutionCallback]
930 /// over [localeResolutionCallback] as it provides the full preferred locales list.
931 ///
932 /// This algorithm should be able to handle a null locale, which indicates
933 /// Flutter has not yet received locale information from the platform.
934 ///
935 /// See also:
936 ///
937 /// * [MaterialApp.localeResolutionCallback], which sets the callback of the
938 /// [WidgetsApp] it creates.
939 /// * [basicLocaleListResolution], the default locale resolution algorithm.
940 final LocaleResolutionCallback? localeResolutionCallback;
941
942 /// {@template flutter.widgets.widgetsApp.supportedLocales}
943 /// The list of locales that this app has been localized for.
944 ///
945 /// By default only the American English locale is supported. Apps should
946 /// configure this list to match the locales they support.
947 ///
948 /// This list must not null. Its default value is just
949 /// `[const Locale('en', 'US')]`.
950 ///
951 /// The order of the list matters. The default locale resolution algorithm,
952 /// [basicLocaleListResolution], attempts to match by the following priority:
953 ///
954 /// 1. [Locale.languageCode], [Locale.scriptCode], and [Locale.countryCode]
955 /// 2. [Locale.languageCode] and [Locale.scriptCode] only
956 /// 3. [Locale.languageCode] and [Locale.countryCode] only
957 /// 4. [Locale.languageCode] only
958 /// 5. [Locale.countryCode] only when all preferred locales fail to match
959 /// 6. Returns the first element of [supportedLocales] as a fallback
960 ///
961 /// When more than one supported locale matches one of these criteria, only
962 /// the first matching locale is returned.
963 ///
964 /// The default locale resolution algorithm can be overridden by providing a
965 /// value for [localeListResolutionCallback]. The provided
966 /// [basicLocaleListResolution] is optimized for speed and does not implement
967 /// a full algorithm (such as the one defined in
968 /// [Unicode TR35](https://unicode.org/reports/tr35/#LanguageMatching)) that
969 /// takes distances between languages into account.
970 ///
971 /// When supporting languages with more than one script, it is recommended
972 /// to specify the [Locale.scriptCode] explicitly. Locales may also be defined without
973 /// [Locale.countryCode] to specify a generic fallback for a particular script.
974 ///
975 /// A fully supported language with multiple scripts should define a generic language-only
976 /// locale (e.g. 'zh'), language+script only locales (e.g. 'zh_Hans' and 'zh_Hant'),
977 /// and any language+script+country locales (e.g. 'zh_Hans_CN'). Fully defining all of
978 /// these locales as supported is not strictly required but allows for proper locale resolution in
979 /// the most number of cases. These locales can be specified with the [Locale.fromSubtags]
980 /// constructor:
981 ///
982 /// ```dart
983 /// // Full Chinese support for CN, TW, and HK
984 /// supportedLocales: <Locale>[
985 /// const Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
986 /// const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'
987 /// const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'
988 /// const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'), // 'zh_Hans_CN'
989 /// const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'), // 'zh_Hant_TW'
990 /// const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'), // 'zh_Hant_HK'
991 /// ],
992 /// ```
993 ///
994 /// Omitting some these fallbacks may result in improperly resolved
995 /// edge-cases, for example, a simplified Chinese user in Taiwan ('zh_Hans_TW')
996 /// may resolve to traditional Chinese if 'zh_Hans' and 'zh_Hans_CN' are
997 /// omitted.
998 /// {@endtemplate}
999 ///
1000 /// See also:
1001 ///
1002 /// * [MaterialApp.supportedLocales], which sets the `supportedLocales`
1003 /// of the [WidgetsApp] it creates.
1004 /// * [localeResolutionCallback], an app callback that resolves the app's locale
1005 /// when the device's locale changes.
1006 /// * [localizationsDelegates], which collectively define all of the localized
1007 /// resources used by this app.
1008 /// * [basicLocaleListResolution], the default locale resolution algorithm.
1009 final Iterable<Locale> supportedLocales;
1010
1011 /// Turns on a performance overlay.
1012 ///
1013 /// See also:
1014 ///
1015 /// * <https://flutter.dev/to/performance-overlay>
1016 final bool showPerformanceOverlay;
1017
1018 /// Turns on an overlay that shows the accessibility information
1019 /// reported by the framework.
1020 final bool showSemanticsDebugger;
1021
1022 /// Turns on an overlay that enables inspecting the widget tree.
1023 ///
1024 /// The inspector is only available in debug mode as it depends on
1025 /// [RenderObject.debugDescribeChildren] which should not be called outside of
1026 /// debug mode.
1027 final bool debugShowWidgetInspector;
1028
1029 /// Builds the widget the [WidgetInspector] uses to switch between view and
1030 /// inspect modes.
1031 ///
1032 /// This lets [MaterialApp] to use a Material Design button to toggle the
1033 /// inspector select mode without requiring [WidgetInspector] to depend on the
1034 /// material package.
1035 final InspectorSelectButtonBuilder? inspectorSelectButtonBuilder;
1036
1037 /// {@template flutter.widgets.widgetsApp.debugShowCheckedModeBanner}
1038 /// Turns on a little "DEBUG" banner in debug mode to indicate
1039 /// that the app is in debug mode. This is on by default (in
1040 /// debug mode), to turn it off, set the constructor argument to
1041 /// false. In release mode this has no effect.
1042 ///
1043 /// To get this banner in your application if you're not using
1044 /// WidgetsApp, include a [CheckedModeBanner] widget in your app.
1045 ///
1046 /// This banner is intended to deter people from complaining that your
1047 /// app is slow when it's in debug mode. In debug mode, Flutter
1048 /// enables a large number of expensive diagnostics to aid in
1049 /// development, and so performance in debug mode is not
1050 /// representative of what will happen in release mode.
1051 /// {@endtemplate}
1052 final bool debugShowCheckedModeBanner;
1053
1054 /// {@template flutter.widgets.widgetsApp.shortcuts}
1055 /// The default map of keyboard shortcuts to intents for the application.
1056 ///
1057 /// By default, this is set to [WidgetsApp.defaultShortcuts].
1058 ///
1059 /// Passing this will not replace [DefaultTextEditingShortcuts]. These can be
1060 /// overridden by using a [Shortcuts] widget lower in the widget tree.
1061 /// {@endtemplate}
1062 ///
1063 /// {@tool snippet}
1064 /// This example shows how to add a single shortcut for
1065 /// [LogicalKeyboardKey.select] to the default shortcuts without needing to
1066 /// add your own [Shortcuts] widget.
1067 ///
1068 /// Alternatively, you could insert a [Shortcuts] widget with just the mapping
1069 /// you want to add between the [WidgetsApp] and its child and get the same
1070 /// effect.
1071 ///
1072 /// ```dart
1073 /// Widget build(BuildContext context) {
1074 /// return WidgetsApp(
1075 /// shortcuts: <ShortcutActivator, Intent>{
1076 /// ... WidgetsApp.defaultShortcuts,
1077 /// const SingleActivator(LogicalKeyboardKey.select): const ActivateIntent(),
1078 /// },
1079 /// color: const Color(0xFFFF0000),
1080 /// builder: (BuildContext context, Widget? child) {
1081 /// return const Placeholder();
1082 /// },
1083 /// );
1084 /// }
1085 /// ```
1086 /// {@end-tool}
1087 ///
1088 /// {@template flutter.widgets.widgetsApp.shortcuts.seeAlso}
1089 /// See also:
1090 ///
1091 /// * [SingleActivator], which defines shortcut key combination of a single
1092 /// key and modifiers, such as "Delete" or "Control+C".
1093 /// * The [Shortcuts] widget, which defines a keyboard mapping.
1094 /// * The [Actions] widget, which defines the mapping from intent to action.
1095 /// * The [Intent] and [Action] classes, which allow definition of new
1096 /// actions.
1097 /// {@endtemplate}
1098 final Map<ShortcutActivator, Intent>? shortcuts;
1099
1100 /// {@template flutter.widgets.widgetsApp.actions}
1101 /// The default map of intent keys to actions for the application.
1102 ///
1103 /// By default, this is the output of [WidgetsApp.defaultActions], called with
1104 /// [defaultTargetPlatform]. Specifying [actions] for an app overrides the
1105 /// default, so if you wish to modify the default [actions], you can call
1106 /// [WidgetsApp.defaultActions] and modify the resulting map, passing it as
1107 /// the [actions] for this app. You may also add to the bindings, or override
1108 /// specific bindings for a widget subtree, by adding your own [Actions]
1109 /// widget.
1110 /// {@endtemplate}
1111 ///
1112 /// {@tool snippet}
1113 /// This example shows how to add a single action handling an
1114 /// [ActivateAction] to the default actions without needing to
1115 /// add your own [Actions] widget.
1116 ///
1117 /// Alternatively, you could insert a [Actions] widget with just the mapping
1118 /// you want to add between the [WidgetsApp] and its child and get the same
1119 /// effect.
1120 ///
1121 /// ```dart
1122 /// Widget build(BuildContext context) {
1123 /// return WidgetsApp(
1124 /// actions: <Type, Action<Intent>>{
1125 /// ... WidgetsApp.defaultActions,
1126 /// ActivateAction: CallbackAction<Intent>(
1127 /// onInvoke: (Intent intent) {
1128 /// // Do something here...
1129 /// return null;
1130 /// },
1131 /// ),
1132 /// },
1133 /// color: const Color(0xFFFF0000),
1134 /// builder: (BuildContext context, Widget? child) {
1135 /// return const Placeholder();
1136 /// },
1137 /// );
1138 /// }
1139 /// ```
1140 /// {@end-tool}
1141 ///
1142 /// {@template flutter.widgets.widgetsApp.actions.seeAlso}
1143 /// See also:
1144 ///
1145 /// * The [shortcuts] parameter, which defines the default set of shortcuts
1146 /// for the application.
1147 /// * The [Shortcuts] widget, which defines a keyboard mapping.
1148 /// * The [Actions] widget, which defines the mapping from intent to action.
1149 /// * The [Intent] and [Action] classes, which allow definition of new
1150 /// actions.
1151 /// {@endtemplate}
1152 final Map<Type, Action<Intent>>? actions;
1153
1154 /// {@template flutter.widgets.widgetsApp.restorationScopeId}
1155 /// The identifier to use for state restoration of this app.
1156 ///
1157 /// Providing a restoration ID inserts a [RootRestorationScope] into the
1158 /// widget hierarchy, which enables state restoration for descendant widgets.
1159 ///
1160 /// Providing a restoration ID also enables the [Navigator] or [Router] built
1161 /// by the [WidgetsApp] to restore its state (i.e. to restore the history
1162 /// stack of active [Route]s). See the documentation on [Navigator] for more
1163 /// details around state restoration of [Route]s.
1164 ///
1165 /// See also:
1166 ///
1167 /// * [RestorationManager], which explains how state restoration works in
1168 /// Flutter.
1169 /// {@endtemplate}
1170 final String? restorationScopeId;
1171
1172 /// {@template flutter.widgets.widgetsApp.useInheritedMediaQuery}
1173 /// Deprecated. This setting is now ignored.
1174 ///
1175 /// The widget never introduces its own [MediaQuery]; the [View] widget takes
1176 /// care of that.
1177 /// {@endtemplate}
1178 @Deprecated(
1179 'This setting is now ignored. '
1180 'WidgetsApp never introduces its own MediaQuery; the View widget takes care of that. '
1181 'This feature was deprecated after v3.7.0-29.0.pre.'
1182 )
1183 final bool useInheritedMediaQuery;
1184
1185 /// If true, forces the performance overlay to be visible in all instances.
1186 ///
1187 /// Used by the `showPerformanceOverlay` VM service extension.
1188 static bool showPerformanceOverlayOverride = false;
1189
1190 /// If true, forces the widget inspector to be visible.
1191 ///
1192 /// Deprecated.
1193 /// Use WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier.value
1194 /// instead.
1195 ///
1196 /// Overrides the `debugShowWidgetInspector` value set in [WidgetsApp].
1197 ///
1198 /// Used by the `debugShowWidgetInspector` debugging extension.
1199 ///
1200 /// The inspector allows the selection of a location on your device or emulator
1201 /// and view what widgets and render objects associated with it. An outline of
1202 /// the selected widget and some summary information is shown on device and
1203 /// more detailed information is shown in the IDE or DevTools.
1204 @Deprecated(
1205 'Use WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier.value instead. '
1206 'This feature was deprecated after v3.20.0-14.0.pre.',
1207 )
1208 static bool get debugShowWidgetInspectorOverride {
1209 return WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier.value;
1210 }
1211
1212 @Deprecated(
1213 'Use WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier.value instead. '
1214 'This feature was deprecated after v3.20.0-14.0.pre.',
1215 )
1216 static set debugShowWidgetInspectorOverride(bool value) {
1217 WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier.value = value;
1218 }
1219
1220 /// If false, prevents the debug banner from being visible.
1221 ///
1222 /// Used by the `debugAllowBanner` VM service extension.
1223 ///
1224 /// This is how `flutter run` turns off the banner when you take a screen shot
1225 /// with "s".
1226 static bool debugAllowBannerOverride = true;
1227
1228 static const Map<ShortcutActivator, Intent> _defaultShortcuts = <ShortcutActivator, Intent>{
1229 // Activation
1230 SingleActivator(LogicalKeyboardKey.enter): ActivateIntent(),
1231 SingleActivator(LogicalKeyboardKey.numpadEnter): ActivateIntent(),
1232 SingleActivator(LogicalKeyboardKey.space): ActivateIntent(),
1233 SingleActivator(LogicalKeyboardKey.gameButtonA): ActivateIntent(),
1234 SingleActivator(LogicalKeyboardKey.select): ActivateIntent(),
1235
1236 // Dismissal
1237 SingleActivator(LogicalKeyboardKey.escape): DismissIntent(),
1238
1239 // Keyboard traversal.
1240 SingleActivator(LogicalKeyboardKey.tab): NextFocusIntent(),
1241 SingleActivator(LogicalKeyboardKey.tab, shift: true): PreviousFocusIntent(),
1242 SingleActivator(LogicalKeyboardKey.arrowLeft): DirectionalFocusIntent(TraversalDirection.left),
1243 SingleActivator(LogicalKeyboardKey.arrowRight): DirectionalFocusIntent(TraversalDirection.right),
1244 SingleActivator(LogicalKeyboardKey.arrowDown): DirectionalFocusIntent(TraversalDirection.down),
1245 SingleActivator(LogicalKeyboardKey.arrowUp): DirectionalFocusIntent(TraversalDirection.up),
1246
1247 // Scrolling
1248 SingleActivator(LogicalKeyboardKey.arrowUp, control: true): ScrollIntent(direction: AxisDirection.up),
1249 SingleActivator(LogicalKeyboardKey.arrowDown, control: true): ScrollIntent(direction: AxisDirection.down),
1250 SingleActivator(LogicalKeyboardKey.arrowLeft, control: true): ScrollIntent(direction: AxisDirection.left),
1251 SingleActivator(LogicalKeyboardKey.arrowRight, control: true): ScrollIntent(direction: AxisDirection.right),
1252 SingleActivator(LogicalKeyboardKey.pageUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page),
1253 SingleActivator(LogicalKeyboardKey.pageDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page),
1254 };
1255
1256 // Default shortcuts for the web platform.
1257 static const Map<ShortcutActivator, Intent> _defaultWebShortcuts = <ShortcutActivator, Intent>{
1258 // Activation
1259 SingleActivator(LogicalKeyboardKey.space): PrioritizedIntents(
1260 orderedIntents: <Intent>[
1261 ActivateIntent(),
1262 ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page),
1263 ],
1264 ),
1265 // On the web, enter activates buttons, but not other controls.
1266 SingleActivator(LogicalKeyboardKey.enter): ButtonActivateIntent(),
1267 SingleActivator(LogicalKeyboardKey.numpadEnter): ButtonActivateIntent(),
1268
1269 // Dismissal
1270 SingleActivator(LogicalKeyboardKey.escape): DismissIntent(),
1271
1272 // Keyboard traversal.
1273 SingleActivator(LogicalKeyboardKey.tab): NextFocusIntent(),
1274 SingleActivator(LogicalKeyboardKey.tab, shift: true): PreviousFocusIntent(),
1275
1276 // Scrolling
1277 SingleActivator(LogicalKeyboardKey.arrowUp): ScrollIntent(direction: AxisDirection.up),
1278 SingleActivator(LogicalKeyboardKey.arrowDown): ScrollIntent(direction: AxisDirection.down),
1279 SingleActivator(LogicalKeyboardKey.arrowLeft): ScrollIntent(direction: AxisDirection.left),
1280 SingleActivator(LogicalKeyboardKey.arrowRight): ScrollIntent(direction: AxisDirection.right),
1281 SingleActivator(LogicalKeyboardKey.pageUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page),
1282 SingleActivator(LogicalKeyboardKey.pageDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page),
1283 };
1284
1285 // Default shortcuts for the macOS platform.
1286 static const Map<ShortcutActivator, Intent> _defaultAppleOsShortcuts = <ShortcutActivator, Intent>{
1287 // Activation
1288 SingleActivator(LogicalKeyboardKey.enter): ActivateIntent(),
1289 SingleActivator(LogicalKeyboardKey.numpadEnter): ActivateIntent(),
1290 SingleActivator(LogicalKeyboardKey.space): ActivateIntent(),
1291
1292 // Dismissal
1293 SingleActivator(LogicalKeyboardKey.escape): DismissIntent(),
1294
1295 // Keyboard traversal
1296 SingleActivator(LogicalKeyboardKey.tab): NextFocusIntent(),
1297 SingleActivator(LogicalKeyboardKey.tab, shift: true): PreviousFocusIntent(),
1298 SingleActivator(LogicalKeyboardKey.arrowLeft): DirectionalFocusIntent(TraversalDirection.left),
1299 SingleActivator(LogicalKeyboardKey.arrowRight): DirectionalFocusIntent(TraversalDirection.right),
1300 SingleActivator(LogicalKeyboardKey.arrowDown): DirectionalFocusIntent(TraversalDirection.down),
1301 SingleActivator(LogicalKeyboardKey.arrowUp): DirectionalFocusIntent(TraversalDirection.up),
1302
1303 // Scrolling
1304 SingleActivator(LogicalKeyboardKey.arrowUp, meta: true): ScrollIntent(direction: AxisDirection.up),
1305 SingleActivator(LogicalKeyboardKey.arrowDown, meta: true): ScrollIntent(direction: AxisDirection.down),
1306 SingleActivator(LogicalKeyboardKey.arrowLeft, meta: true): ScrollIntent(direction: AxisDirection.left),
1307 SingleActivator(LogicalKeyboardKey.arrowRight, meta: true): ScrollIntent(direction: AxisDirection.right),
1308 SingleActivator(LogicalKeyboardKey.pageUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page),
1309 SingleActivator(LogicalKeyboardKey.pageDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page),
1310 };
1311
1312 /// Generates the default shortcut key bindings based on the
1313 /// [defaultTargetPlatform].
1314 ///
1315 /// Used by [WidgetsApp] to assign a default value to [WidgetsApp.shortcuts].
1316 static Map<ShortcutActivator, Intent> get defaultShortcuts {
1317 if (kIsWeb) {
1318 return _defaultWebShortcuts;
1319 }
1320
1321 switch (defaultTargetPlatform) {
1322 case TargetPlatform.android:
1323 case TargetPlatform.fuchsia:
1324 case TargetPlatform.linux:
1325 case TargetPlatform.windows:
1326 return _defaultShortcuts;
1327 case TargetPlatform.iOS:
1328 case TargetPlatform.macOS:
1329 return _defaultAppleOsShortcuts;
1330 }
1331 }
1332
1333 /// The default value of [WidgetsApp.actions].
1334 static Map<Type, Action<Intent>> defaultActions = <Type, Action<Intent>>{
1335 DoNothingIntent: DoNothingAction(),
1336 DoNothingAndStopPropagationIntent: DoNothingAction(consumesKey: false),
1337 RequestFocusIntent: RequestFocusAction(),
1338 NextFocusIntent: NextFocusAction(),
1339 PreviousFocusIntent: PreviousFocusAction(),
1340 DirectionalFocusIntent: DirectionalFocusAction(),
1341 ScrollIntent: ScrollAction(),
1342 PrioritizedIntents: PrioritizedAction(),
1343 VoidCallbackIntent: VoidCallbackAction(),
1344 };
1345
1346 @override
1347 State<WidgetsApp> createState() => _WidgetsAppState();
1348}
1349
1350class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
1351 // STATE LIFECYCLE
1352
1353 // If window.defaultRouteName isn't '/', we should assume it was set
1354 // intentionally via `setInitialRoute`, and should override whatever is in
1355 // [widget.initialRoute].
1356 String get _initialRouteName => WidgetsBinding.instance.platformDispatcher.defaultRouteName != Navigator.defaultRouteName
1357 ? WidgetsBinding.instance.platformDispatcher.defaultRouteName
1358 : widget.initialRoute ?? WidgetsBinding.instance.platformDispatcher.defaultRouteName;
1359
1360 AppLifecycleState? _appLifecycleState;
1361
1362 /// The default value for [WidgetsApp.onNavigationNotification].
1363 ///
1364 /// Does nothing and stops bubbling if the app is detached. Otherwise, updates
1365 /// the platform with [NavigationNotification.canHandlePop] and stops
1366 /// bubbling.
1367 bool _defaultOnNavigationNotification(NavigationNotification notification) {
1368 switch (_appLifecycleState) {
1369 case null:
1370 case AppLifecycleState.detached:
1371 // Avoid updating the engine when the app isn't ready.
1372 return true;
1373 case AppLifecycleState.inactive:
1374 case AppLifecycleState.resumed:
1375 case AppLifecycleState.hidden:
1376 case AppLifecycleState.paused:
1377 SystemNavigator.setFrameworkHandlesBack(notification.canHandlePop);
1378 return true;
1379 }
1380 }
1381
1382 @override
1383 void didChangeAppLifecycleState(AppLifecycleState state) {
1384 _appLifecycleState = state;
1385 super.didChangeAppLifecycleState(state);
1386 }
1387
1388 @override
1389 void initState() {
1390 super.initState();
1391 _updateRouting();
1392 _locale = _resolveLocales(WidgetsBinding.instance.platformDispatcher.locales, widget.supportedLocales);
1393 WidgetsBinding.instance.addObserver(this);
1394 _appLifecycleState = WidgetsBinding.instance.lifecycleState;
1395 }
1396
1397 @override
1398 void didUpdateWidget(WidgetsApp oldWidget) {
1399 super.didUpdateWidget(oldWidget);
1400 _updateRouting(oldWidget: oldWidget);
1401 }
1402
1403 @override
1404 void dispose() {
1405 WidgetsBinding.instance.removeObserver(this);
1406 _defaultRouteInformationProvider?.dispose();
1407 super.dispose();
1408 }
1409
1410 void _clearRouterResource() {
1411 _defaultRouteInformationProvider?.dispose();
1412 _defaultRouteInformationProvider = null;
1413 _defaultBackButtonDispatcher = null;
1414 }
1415
1416 void _clearNavigatorResource() {
1417 _navigator = null;
1418 }
1419
1420 void _updateRouting({WidgetsApp? oldWidget}) {
1421 if (_usesRouterWithDelegates) {
1422 assert(!_usesNavigator && !_usesRouterWithConfig);
1423 _clearNavigatorResource();
1424 if (widget.routeInformationProvider == null && widget.routeInformationParser != null) {
1425 _defaultRouteInformationProvider ??= PlatformRouteInformationProvider(
1426 initialRouteInformation: RouteInformation(
1427 uri: Uri.parse(_initialRouteName),
1428 ),
1429 );
1430 } else {
1431 _defaultRouteInformationProvider?.dispose();
1432 _defaultRouteInformationProvider = null;
1433 }
1434 if (widget.backButtonDispatcher == null) {
1435 _defaultBackButtonDispatcher ??= RootBackButtonDispatcher();
1436 }
1437
1438 } else if (_usesNavigator) {
1439 assert(!_usesRouterWithDelegates && !_usesRouterWithConfig);
1440 _clearRouterResource();
1441 if (_navigator == null || widget.navigatorKey != oldWidget!.navigatorKey) {
1442 _navigator = widget.navigatorKey ?? GlobalObjectKey<NavigatorState>(this);
1443 }
1444 assert(_navigator != null);
1445 } else {
1446 assert(widget.builder != null || _usesRouterWithConfig);
1447 assert(!_usesRouterWithDelegates && !_usesNavigator);
1448 _clearRouterResource();
1449 _clearNavigatorResource();
1450 }
1451 // If we use a navigator, we have a navigator key.
1452 assert(_usesNavigator == (_navigator != null));
1453 }
1454
1455 bool get _usesRouterWithDelegates => widget.routerDelegate != null;
1456 bool get _usesRouterWithConfig => widget.routerConfig != null;
1457 bool get _usesNavigator => widget.home != null
1458 || (widget.routes?.isNotEmpty ?? false)
1459 || widget.onGenerateRoute != null
1460 || widget.onUnknownRoute != null;
1461
1462 // ROUTER
1463
1464 RouteInformationProvider? get _effectiveRouteInformationProvider => widget.routeInformationProvider ?? _defaultRouteInformationProvider;
1465 PlatformRouteInformationProvider? _defaultRouteInformationProvider;
1466 BackButtonDispatcher get _effectiveBackButtonDispatcher => widget.backButtonDispatcher ?? _defaultBackButtonDispatcher!;
1467 RootBackButtonDispatcher? _defaultBackButtonDispatcher;
1468
1469 // NAVIGATOR
1470
1471 GlobalKey<NavigatorState>? _navigator;
1472
1473 Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
1474 final String? name = settings.name;
1475 final WidgetBuilder? pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null
1476 ? (BuildContext context) => widget.home!
1477 : widget.routes![name];
1478
1479 if (pageContentBuilder != null) {
1480 assert(
1481 widget.pageRouteBuilder != null,
1482 'The default onGenerateRoute handler for WidgetsApp must have a '
1483 'pageRouteBuilder set if the home or routes properties are set.',
1484 );
1485 final Route<dynamic> route = widget.pageRouteBuilder!<dynamic>(
1486 settings,
1487 pageContentBuilder,
1488 );
1489 return route;
1490 }
1491 if (widget.onGenerateRoute != null) {
1492 return widget.onGenerateRoute!(settings);
1493 }
1494 return null;
1495 }
1496
1497 Route<dynamic> _onUnknownRoute(RouteSettings settings) {
1498 assert(() {
1499 if (widget.onUnknownRoute == null) {
1500 throw FlutterError(
1501 'Could not find a generator for route $settings in the $runtimeType.\n'
1502 'Make sure your root app widget has provided a way to generate \n'
1503 'this route.\n'
1504 'Generators for routes are searched for in the following order:\n'
1505 ' 1. For the "/" route, the "home" property, if non-null, is used.\n'
1506 ' 2. Otherwise, the "routes" table is used, if it has an entry for '
1507 'the route.\n'
1508 ' 3. Otherwise, onGenerateRoute is called. It should return a '
1509 'non-null value for any valid route not handled by "home" and "routes".\n'
1510 ' 4. Finally if all else fails onUnknownRoute is called.\n'
1511 'Unfortunately, onUnknownRoute was not set.',
1512 );
1513 }
1514 return true;
1515 }());
1516 final Route<dynamic>? result = widget.onUnknownRoute!(settings);
1517 assert(() {
1518 if (result == null) {
1519 throw FlutterError(
1520 'The onUnknownRoute callback returned null.\n'
1521 'When the $runtimeType requested the route $settings from its '
1522 'onUnknownRoute callback, the callback returned null. Such callbacks '
1523 'must never return null.',
1524 );
1525 }
1526 return true;
1527 }());
1528 return result!;
1529 }
1530
1531 // On Android: the user has pressed the back button.
1532 @override
1533 Future<bool> didPopRoute() async {
1534 assert(mounted);
1535 // The back button dispatcher should handle the pop route if we use a
1536 // router.
1537 if (_usesRouterWithDelegates) {
1538 return false;
1539 }
1540
1541 final NavigatorState? navigator = _navigator?.currentState;
1542 if (navigator == null) {
1543 return false;
1544 }
1545 return navigator.maybePop();
1546 }
1547
1548 @override
1549 Future<bool> didPushRouteInformation(RouteInformation routeInformation) async {
1550 assert(mounted);
1551 // The route name provider should handle the push route if we uses a
1552 // router.
1553 if (_usesRouterWithDelegates) {
1554 return false;
1555 }
1556
1557 final NavigatorState? navigator = _navigator?.currentState;
1558 if (navigator == null) {
1559 return false;
1560 }
1561 final Uri uri = routeInformation.uri;
1562 navigator.pushNamed(
1563 Uri.decodeComponent(
1564 Uri(
1565 path: uri.path.isEmpty ? '/' : uri.path,
1566 queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
1567 fragment: uri.fragment.isEmpty ? null : uri.fragment,
1568 ).toString(),
1569 ),
1570 );
1571 return true;
1572 }
1573
1574 // LOCALIZATION
1575
1576 /// This is the resolved locale, and is one of the supportedLocales.
1577 Locale? _locale;
1578
1579 Locale _resolveLocales(List<Locale>? preferredLocales, Iterable<Locale> supportedLocales) {
1580 // Attempt to use localeListResolutionCallback.
1581 if (widget.localeListResolutionCallback != null) {
1582 final Locale? locale = widget.localeListResolutionCallback!(preferredLocales, widget.supportedLocales);
1583 if (locale != null) {
1584 return locale;
1585 }
1586 }
1587 // localeListResolutionCallback failed, falling back to localeResolutionCallback.
1588 if (widget.localeResolutionCallback != null) {
1589 final Locale? locale = widget.localeResolutionCallback!(
1590 preferredLocales != null && preferredLocales.isNotEmpty ? preferredLocales.first : null,
1591 widget.supportedLocales,
1592 );
1593 if (locale != null) {
1594 return locale;
1595 }
1596 }
1597 // Both callbacks failed, falling back to default algorithm.
1598 return basicLocaleListResolution(preferredLocales, supportedLocales);
1599 }
1600
1601 @override
1602 void didChangeLocales(List<Locale>? locales) {
1603 final Locale newLocale = _resolveLocales(locales, widget.supportedLocales);
1604 if (newLocale != _locale) {
1605 setState(() {
1606 _locale = newLocale;
1607 });
1608 }
1609 }
1610
1611 // Combine the Localizations for Widgets with the ones contributed
1612 // by the localizationsDelegates parameter, if any. Only the first delegate
1613 // of a particular LocalizationsDelegate.type is loaded so the
1614 // localizationsDelegate parameter can be used to override
1615 // WidgetsLocalizations.delegate.
1616 Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
1617 return <LocalizationsDelegate<dynamic>>[
1618 if (widget.localizationsDelegates != null)
1619 ...widget.localizationsDelegates!,
1620 DefaultWidgetsLocalizations.delegate,
1621 ];
1622 }
1623
1624 // BUILDER
1625
1626 bool _debugCheckLocalizations(Locale appLocale) {
1627 assert(() {
1628 final Set<Type> unsupportedTypes =
1629 _localizationsDelegates.map<Type>((LocalizationsDelegate<dynamic> delegate) => delegate.type).toSet();
1630 for (final LocalizationsDelegate<dynamic> delegate in _localizationsDelegates) {
1631 if (!unsupportedTypes.contains(delegate.type)) {
1632 continue;
1633 }
1634 if (delegate.isSupported(appLocale)) {
1635 unsupportedTypes.remove(delegate.type);
1636 }
1637 }
1638 if (unsupportedTypes.isEmpty) {
1639 return true;
1640 }
1641
1642 FlutterError.reportError(FlutterErrorDetails(
1643 exception: "Warning: This application's locale, $appLocale, is not supported by all of its localization delegates.",
1644 library: 'widgets',
1645 informationCollector: () => <DiagnosticsNode>[
1646 for (final Type unsupportedType in unsupportedTypes)
1647 ErrorDescription(
1648 '• A $unsupportedType delegate that supports the $appLocale locale was not found.',
1649 ),
1650 ErrorSpacer(),
1651 if (unsupportedTypes.length == 1 && unsupportedTypes.single.toString() == 'CupertinoLocalizations')
1652 // We previously explicitly avoided checking for this class so it's not uncommon for applications
1653 // to have omitted importing the required delegate.
1654 ...<DiagnosticsNode>[
1655 ErrorHint(
1656 'If the application is built using GlobalMaterialLocalizations.delegate, consider using '
1657 'GlobalMaterialLocalizations.delegates (plural) instead, as that will automatically declare '
1658 'the appropriate Cupertino localizations.'
1659 ),
1660 ErrorSpacer(),
1661 ],
1662 ErrorHint(
1663 'The declared supported locales for this app are: ${widget.supportedLocales.join(", ")}'
1664 ),
1665 ErrorSpacer(),
1666 ErrorDescription(
1667 'See https://flutter.dev/to/internationalization/ for more '
1668 "information about configuring an app's locale, supportedLocales, "
1669 'and localizationsDelegates parameters.',
1670 ),
1671 ],
1672 ));
1673 return true;
1674 }());
1675 return true;
1676 }
1677
1678 @override
1679 Widget build(BuildContext context) {
1680 Widget? routing;
1681 if (_usesRouterWithDelegates) {
1682 routing = Router<Object>(
1683 restorationScopeId: 'router',
1684 routeInformationProvider: _effectiveRouteInformationProvider,
1685 routeInformationParser: widget.routeInformationParser,
1686 routerDelegate: widget.routerDelegate!,
1687 backButtonDispatcher: _effectiveBackButtonDispatcher,
1688 );
1689 } else if (_usesNavigator) {
1690 assert(_navigator != null);
1691 routing = FocusScope(
1692 debugLabel: 'Navigator Scope',
1693 autofocus: true,
1694 child: Navigator(
1695 clipBehavior: Clip.none,
1696 restorationScopeId: 'nav',
1697 key: _navigator,
1698 initialRoute: _initialRouteName,
1699 onGenerateRoute: _onGenerateRoute,
1700 onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null
1701 ? Navigator.defaultGenerateInitialRoutes
1702 : (NavigatorState navigator, String initialRouteName) {
1703 return widget.onGenerateInitialRoutes!(initialRouteName);
1704 },
1705 onUnknownRoute: _onUnknownRoute,
1706 observers: widget.navigatorObservers!,
1707 routeTraversalEdgeBehavior: kIsWeb ? TraversalEdgeBehavior.leaveFlutterView : TraversalEdgeBehavior.parentScope,
1708 reportsRouteUpdateToEngine: true,
1709 ),
1710 );
1711 } else if (_usesRouterWithConfig) {
1712 routing = Router<Object>.withConfig(
1713 restorationScopeId: 'router',
1714 config: widget.routerConfig!,
1715 );
1716 }
1717
1718 Widget result;
1719 if (widget.builder != null) {
1720 result = Builder(
1721 builder: (BuildContext context) {
1722 return widget.builder!(context, routing);
1723 },
1724 );
1725 } else {
1726 assert(routing != null);
1727 result = routing!;
1728 }
1729
1730 if (widget.textStyle != null) {
1731 result = DefaultTextStyle(
1732 style: widget.textStyle!,
1733 child: result,
1734 );
1735 }
1736
1737 if (widget.showPerformanceOverlay || WidgetsApp.showPerformanceOverlayOverride) {
1738 result = Stack(
1739 children: <Widget>[
1740 result,
1741 Positioned(top: 0.0, left: 0.0, right: 0.0, child: PerformanceOverlay.allEnabled()),
1742 ],
1743 );
1744 }
1745
1746 if (widget.showSemanticsDebugger) {
1747 result = SemanticsDebugger(
1748 child: result,
1749 );
1750 }
1751
1752 assert(() {
1753 result = ValueListenableBuilder<bool>(
1754 valueListenable: WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier,
1755 builder: (BuildContext context, bool debugShowWidgetInspectorOverride, Widget? child) {
1756 if (widget.debugShowWidgetInspector || debugShowWidgetInspectorOverride) {
1757 return WidgetInspector(
1758 selectButtonBuilder: widget.inspectorSelectButtonBuilder,
1759 child: child!,
1760 );
1761 }
1762 return child!;
1763 },
1764 child: result,
1765 );
1766 if (widget.debugShowCheckedModeBanner && WidgetsApp.debugAllowBannerOverride) {
1767 result = CheckedModeBanner(
1768 child: result,
1769 );
1770 }
1771 return true;
1772 }());
1773
1774 final Widget? title;
1775 if (widget.onGenerateTitle != null) {
1776 title = Builder(
1777 // This Builder exists to provide a context below the Localizations widget.
1778 // The onGenerateTitle callback can refer to Localizations via its context
1779 // parameter.
1780 builder: (BuildContext context) {
1781 final String title = widget.onGenerateTitle!(context);
1782 return Title(
1783 title: title,
1784 color: widget.color.withOpacity(1.0),
1785 child: result,
1786 );
1787 },
1788 );
1789 } else if (widget.title == null && kIsWeb) {
1790 // Updating the element in the DOM is problematic in embedded</i></td></tr> <tr><th id="1791">1791</th><td> <i>// and multiview modes as title should be managed by host apps.</i></td></tr> <tr><th id="1792">1792</th><td> <i>// Refer to https://github.com/flutter/flutter/pull/152003 for more info.</i></td></tr> <tr><th id="1793">1793</th><td> <a href="#76091" data-ref="76091" class="local ref" title="title">title</a> = <var>null</var>;</td></tr> <tr><th id="1794">1794</th><td> } <b>else</b> {</td></tr> <tr><th id="1795">1795</th><td> <a href="#76091" data-ref="76091" class="local ref" title="title">title</a> = <a href="title.dart.html#12" class="type ref" title="Title" data-ref="Title">Title</a>(</td></tr> <tr><th id="1796">1796</th><td> <a href="#706" data-ref="706" class="local ref" title="title">title</a>: <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#831" data-ref="WidgetsApp.title" class="field ref" title="title">title</a> ?? <q>''</q>,</td></tr> <tr><th id="1797">1797</th><td> <a href="#736" data-ref="736" class="local ref" title="color">color</a>: <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#858" data-ref="WidgetsApp.color" class="field ref" title="color">color</a>.<a data-ref="Color.withOpacity" class="member fn ref" title="withOpacity">withOpacity</a>(<var>1.0</var>),</td></tr> <tr><th id="1798">1798</th><td> <a href="#761" data-ref="761" class="local ref" title="child">child</a>: <a href="#74525" data-ref="74525" class="local ref" title="result">result</a>,</td></tr> <tr><th id="1799">1799</th><td> );</td></tr> <tr><th id="1800">1800</th><td> }</td></tr> <tr><th id="1801">1801</th><td></td></tr> <tr><th id="1802">1802</th><td> <b>final</b> <a class="type ref" title="Locale" data-ref="Locale">Locale</a> <dfn id="77087" class="decl local" data-type="Locale" data-ref="77087" title="appLocale">appLocale</dfn> = <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#877" data-ref="WidgetsApp.locale" class="field ref" title="locale">locale</a> != <var>null</var></td></tr> <tr><th id="1803">1803</th><td> ? <a href="#69002" data-ref="69002" class="member fn local ref" title="_resolveLocales">_resolveLocales</a>(<<a class="type ref" title="Locale" data-ref="Locale">Locale</a>>[<a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#877" data-ref="WidgetsApp.locale" class="field ref" title="locale">locale</a>!], <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#1009" data-ref="WidgetsApp.supportedLocales" class="field ref" title="supportedLocales">supportedLocales</a>)</td></tr> <tr><th id="1804">1804</th><td> : <a href="#68983" data-ref="68983" class="field local ref" title="_locale">_locale</a>!;</td></tr> <tr><th id="1805">1805</th><td></td></tr> <tr><th id="1806">1806</th><td> <b>assert</b>(<a href="#70756" data-ref="70756" class="member fn local ref" title="_debugCheckLocalizations">_debugCheckLocalizations</a>(<a href="#77087" data-ref="77087" class="local ref" title="appLocale">appLocale</a>));</td></tr> <tr><th id="1807">1807</th><td></td></tr> <tr><th id="1808">1808</th><td> <b>return</b> <a class="type ref" title="RootRestorationScope" data-ref="RootRestorationScope">RootRestorationScope</a>(</td></tr> <tr><th id="1809">1809</th><td> <a href="#12232" data-ref="12232" class="local ref" title="restorationId">restorationId</a>: <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#1170" data-ref="WidgetsApp.restorationScopeId" class="field ref" title="restorationScopeId">restorationScopeId</a>,</td></tr> <tr><th id="1810">1810</th><td> <a href="#12265" data-ref="12265" class="local ref" title="child">child</a>: <a class="type ref" title="SharedAppData" data-ref="SharedAppData">SharedAppData</a>(</td></tr> <tr><th id="1811">1811</th><td> <a href="#3585" data-ref="3585" class="local ref" title="child">child</a>: <a class="type ref" title="NotificationListener" data-ref="NotificationListener">NotificationListener</a><<a href="navigator.dart.html#6171" class="type ref" title="NavigationNotification" data-ref="NavigationNotification">NavigationNotification</a>>(</td></tr> <tr><th id="1812">1812</th><td> <a href="#4229" data-ref="4229" class="local ref" title="onNotification">onNotification</a>: <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#720" data-ref="WidgetsApp.onNavigationNotification" class="field ref" title="onNavigationNotification">onNavigationNotification</a> ?? <a href="#61848" data-ref="61848" class="member fn local ref" title="_defaultOnNavigationNotification">_defaultOnNavigationNotification</a>,</td></tr> <tr><th id="1813">1813</th><td> <a href="#4213" data-ref="4213" class="local ref" title="child">child</a>: <a class="type ref" title="Shortcuts" data-ref="Shortcuts">Shortcuts</a>(</td></tr> <tr><th id="1814">1814</th><td> <a href="#40237" data-ref="40237" class="local ref" title="debugLabel">debugLabel</a>: <q>'<Default WidgetsApp Shortcuts>'</q>,</td></tr> <tr><th id="1815">1815</th><td> <a href="#40192" data-ref="40192" class="local ref" title="shortcuts">shortcuts</a>: <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#1098" data-ref="WidgetsApp.shortcuts" class="field ref" title="shortcuts">shortcuts</a> ?? <a href="#272" data-ref="WidgetsApp" class="type ref" title="WidgetsApp">WidgetsApp</a>.<a data-ref="WidgetsApp.defaultShortcuts" class="fn ref" title="defaultShortcuts">defaultShortcuts</a>,</td></tr> <tr><th id="1816">1816</th><td> <i>// DefaultTextEditingShortcuts is nested inside Shortcuts so that it can</i></td></tr> <tr><th id="1817">1817</th><td> <i>// fall through to the defaultShortcuts.</i></td></tr> <tr><th id="1818">1818</th><td> <a href="#40221" data-ref="40221" class="local ref" title="child">child</a>: <a href="default_text_editing_shortcuts.dart.html#162" class="type ref" title="DefaultTextEditingShortcuts" data-ref="DefaultTextEditingShortcuts">DefaultTextEditingShortcuts</a>(</td></tr> <tr><th id="1819">1819</th><td> <a href="#6039" data-ref="6039" class="local ref" title="child">child</a>: <a class="type ref" title="Actions" data-ref="Actions">Actions</a>(</td></tr> <tr><th id="1820">1820</th><td> <a href="#29375" data-ref="29375" class="local ref" title="actions">actions</a>: <a data-ref="State.widget" class="fn ref" title="widget">widget</a>.<a href="#1152" data-ref="WidgetsApp.actions" class="field ref" title="actions">actions</a> ?? <<a class="type ref" title="Type" data-ref="Type">Type</a>, <a class="type ref" title="Action" data-ref="Action">Action</a><<a class="type ref" title="Intent" data-ref="Intent">Intent</a>>>{</td></tr> <tr><th id="1821">1821</th><td> ...<a href="#272" data-ref="WidgetsApp" class="type ref" title="WidgetsApp">WidgetsApp</a>.<a href="#1334" data-ref="WidgetsApp.defaultActions" class="field ref" title="defaultActions">defaultActions</a>,</td></tr> <tr><th id="1822">1822</th><td> <a data-ref="ScrollIntent" class="type ref" title="ScrollIntent">ScrollIntent</a>: <a class="type ref" title="Action" data-ref="Action">Action</a><<a class="type ref" title="ScrollIntent" data-ref="ScrollIntent">ScrollIntent</a>>.<a data-ref="Action.overridable" class="fn ref" title="overridable">overridable</a>(<a href="#8188" data-ref="8188" class="local ref" title="context">context</a>: <a href="#73016" data-ref="73016" class="local ref" title="context">context</a>, <a href="#8147" data-ref="8147" class="local ref" title="defaultAction">defaultAction</a>: <a class="type ref" title="ScrollAction" data-ref="ScrollAction">ScrollAction</a>()),</td></tr> <tr><th id="1823">1823</th><td> },</td></tr> <tr><th id="1824">1824</th><td> <a href="#29402" data-ref="29402" class="local ref" title="child">child</a>: <a class="type ref" title="FocusTraversalGroup" data-ref="FocusTraversalGroup">FocusTraversalGroup</a>(</td></tr> <tr><th id="1825">1825</th><td> <a href="#75356" data-ref="75356" class="local ref" title="policy">policy</a>: <a class="type ref" title="ReadingOrderTraversalPolicy" data-ref="ReadingOrderTraversalPolicy">ReadingOrderTraversalPolicy</a>(),</td></tr> <tr><th id="1826">1826</th><td> <a href="#75466" data-ref="75466" class="local ref" title="child">child</a>: <a class="type ref" title="TapRegionSurface" data-ref="TapRegionSurface">TapRegionSurface</a>(</td></tr> <tr><th id="1827">1827</th><td> <a href="#5119" data-ref="5119" class="local ref" title="child">child</a>: <a class="type ref" title="ShortcutRegistrar" data-ref="ShortcutRegistrar">ShortcutRegistrar</a>(</td></tr> <tr><th id="1828">1828</th><td> <a href="#58711" data-ref="58711" class="local ref" title="child">child</a>: <a class="type ref" title="Localizations" data-ref="Localizations">Localizations</a>(</td></tr> <tr><th id="1829">1829</th><td> <a href="#15598" data-ref="15598" class="local ref" title="locale">locale</a>: <a href="#77087" data-ref="77087" class="local ref" title="appLocale">appLocale</a>,</td></tr> <tr><th id="1830">1830</th><td> <a href="#15624" data-ref="15624" class="local ref" title="delegates">delegates</a>: <a href="#70516" data-ref="70516" class="fn local ref" title="_localizationsDelegates">_localizationsDelegates</a>.<a data-ref="Iterable.toList" class="member fn ref" title="toList">toList</a>(),</td></tr> <tr><th id="1831">1831</th><td> <a href="#15644" data-ref="15644" class="local ref" title="child">child</a>: <a href="#76091" data-ref="76091" class="local ref" title="title">title</a> ?? <a href="#74525" data-ref="74525" class="local ref" title="result">result</a>,</td></tr> <tr><th id="1832">1832</th><td> ),</td></tr> <tr><th id="1833">1833</th><td> ),</td></tr> <tr><th id="1834">1834</th><td> ),</td></tr> <tr><th id="1835">1835</th><td> ),</td></tr> <tr><th id="1836">1836</th><td> ),</td></tr> <tr><th id="1837">1837</th><td> ),</td></tr> <tr><th id="1838">1838</th><td> ),</td></tr> <tr><th id="1839">1839</th><td> ),</td></tr> <tr><th id="1840">1840</th><td> ),</td></tr> <tr><th id="1841">1841</th><td> );</td></tr> <tr><th id="1842">1842</th><td> }</td></tr> <tr><th id="1843">1843</th><td>}</td></tr> <tr><th id="1844">1844</th><td></tbody> </table> <div id='footercontainer'><div id='footer'> Generated on <em>2025-Jan-2</em> from project flutter revision <em>3.27.1</em> <br />Powered by <a href='https://codebrowser.dev'>Code Browser</a> 2.1<br/>Generator usage only permitted with license. </div></div> </div> </body> </html>