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_localizations/flutter_localizations.dart';
6///
7/// @docImport 'app_bar.dart';
8/// @docImport 'color_scheme.dart';
9/// @docImport 'dialog.dart';
10/// @docImport 'drawer.dart';
11/// @docImport 'material.dart';
12/// @docImport 'popup_menu.dart';
13/// @docImport 'scaffold.dart';
14library;
15
16import 'dart:ui' as ui;
17
18import 'package:flutter/cupertino.dart';
19import 'package:flutter/foundation.dart';
20import 'package:flutter/services.dart';
21
22import 'arc.dart';
23import 'button_style.dart';
24import 'colors.dart';
25import 'icon_button.dart';
26import 'icons.dart';
27import 'material_localizations.dart';
28import 'page.dart';
29import 'scaffold.dart' show ScaffoldMessenger, ScaffoldMessengerState;
30import 'scrollbar.dart';
31import 'theme.dart';
32import 'theme_data.dart';
33import 'tooltip.dart';
34
35// Examples can assume:
36// typedef GlobalWidgetsLocalizations = DefaultWidgetsLocalizations;
37// typedef GlobalMaterialLocalizations = DefaultMaterialLocalizations;
38
39/// [MaterialApp] uses this [TextStyle] as its [DefaultTextStyle] to encourage
40/// developers to be intentional about their [DefaultTextStyle].
41///
42/// In Material Design, most [Text] widgets are contained in [Material] widgets,
43/// which sets a specific [DefaultTextStyle]. If you're seeing text that uses
44/// this text style, consider putting your text in a [Material] widget (or
45/// another widget that sets a [DefaultTextStyle]).
46const TextStyle _errorTextStyle = TextStyle(
47 color: Color(0xD0FF0000),
48 fontFamily: 'monospace',
49 fontSize: 48.0,
50 fontWeight: FontWeight.w900,
51 decoration: TextDecoration.underline,
52 decorationColor: Color(0xFFFFFF00),
53 decorationStyle: TextDecorationStyle.double,
54 debugLabel: 'fallback style; consider putting your text in a Material',
55);
56
57/// Describes which theme will be used by [MaterialApp].
58enum ThemeMode {
59 /// Use either the light or dark theme based on what the user has selected in
60 /// the system settings.
61 system,
62
63 /// Always use the light mode regardless of system preference.
64 light,
65
66 /// Always use the dark mode (if available) regardless of system preference.
67 dark,
68}
69
70/// An application that uses Material Design.
71///
72/// A convenience widget that wraps a number of widgets that are commonly
73/// required for Material Design applications. It builds upon a [WidgetsApp] by
74/// adding material-design specific functionality, such as [AnimatedTheme] and
75/// [GridPaper].
76///
77/// [MaterialApp] configures its [WidgetsApp.textStyle] with an ugly red/yellow
78/// text style that's intended to warn the developer that their app hasn't defined
79/// a default text style. Typically the app's [Scaffold] builds a [Material] widget
80/// whose default [Material.textStyle] defines the text style for the entire scaffold.
81///
82/// The [MaterialApp] configures the top-level [Navigator] to search for routes
83/// in the following order:
84///
85/// 1. For the `/` route, the [home] property, if non-null, is used.
86///
87/// 2. Otherwise, the [routes] table is used, if it has an entry for the route.
88///
89/// 3. Otherwise, [onGenerateRoute] is called, if provided. It should return a
90/// non-null value for any _valid_ route not handled by [home] and [routes].
91///
92/// 4. Finally if all else fails [onUnknownRoute] is called.
93///
94/// If a [Navigator] is created, at least one of these options must handle the
95/// `/` route, since it is used when an invalid [initialRoute] is specified on
96/// startup (e.g. by another application launching this one with an intent on
97/// Android; see [dart:ui.PlatformDispatcher.defaultRouteName]).
98///
99/// This widget also configures the observer of the top-level [Navigator] (if
100/// any) to perform [Hero] animations.
101///
102/// {@template flutter.material.MaterialApp.defaultSelectionStyle}
103/// The [MaterialApp] automatically creates a [DefaultSelectionStyle]. It uses
104/// the colors in the [ThemeData.textSelectionTheme] if they are not null;
105/// otherwise, the [MaterialApp] sets [DefaultSelectionStyle.selectionColor] to
106/// [ColorScheme.primary] with 0.4 opacity and
107/// [DefaultSelectionStyle.cursorColor] to [ColorScheme.primary].
108/// {@endtemplate}
109///
110/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
111/// and [builder] is not null, then no [Navigator] is created.
112///
113/// {@tool snippet}
114/// This example shows how to create a [MaterialApp] that disables the "debug"
115/// banner with a [home] route that will be displayed when the app is launched.
116///
117/// ![The MaterialApp displays a Scaffold ](https://flutter.github.io/assets-for-api-docs/assets/material/basic_material_app.png)
118///
119/// ```dart
120/// MaterialApp(
121/// home: Scaffold(
122/// appBar: AppBar(
123/// title: const Text('Home'),
124/// ),
125/// ),
126/// debugShowCheckedModeBanner: false,
127/// )
128/// ```
129/// {@end-tool}
130///
131/// {@tool snippet}
132/// This example shows how to create a [MaterialApp] that uses the [routes]
133/// `Map` to define the "home" route and an "about" route.
134///
135/// ```dart
136/// MaterialApp(
137/// routes: <String, WidgetBuilder>{
138/// '/': (BuildContext context) {
139/// return Scaffold(
140/// appBar: AppBar(
141/// title: const Text('Home Route'),
142/// ),
143/// );
144/// },
145/// '/about': (BuildContext context) {
146/// return Scaffold(
147/// appBar: AppBar(
148/// title: const Text('About Route'),
149/// ),
150/// );
151/// }
152/// },
153/// )
154/// ```
155/// {@end-tool}
156///
157/// {@tool snippet}
158/// This example shows how to create a [MaterialApp] that defines a [theme] that
159/// will be used for material widgets in the app.
160///
161/// ![The MaterialApp displays a Scaffold with a dark background and a blue / grey AppBar at the top](https://flutter.github.io/assets-for-api-docs/assets/material/theme_material_app.png)
162///
163/// ```dart
164/// MaterialApp(
165/// theme: ThemeData(
166/// brightness: Brightness.dark,
167/// primaryColor: Colors.blueGrey
168/// ),
169/// home: Scaffold(
170/// appBar: AppBar(
171/// title: const Text('MaterialApp Theme'),
172/// ),
173/// ),
174/// )
175/// ```
176/// {@end-tool}
177///
178/// ## Troubleshooting
179///
180/// ### Why is my app's text red with yellow underlines?
181///
182/// [Text] widgets that lack a [Material] ancestor will be rendered with an ugly
183/// red/yellow text style.
184///
185/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/material_app_unspecified_textstyle.png)
186///
187/// The typical fix is to give the widget a [Scaffold] ancestor. The [Scaffold] creates
188/// a [Material] widget that defines its default text style.
189///
190/// ```dart
191/// const MaterialApp(
192/// title: 'Material App',
193/// home: Scaffold(
194/// body: Center(
195/// child: Text('Hello World'),
196/// ),
197/// ),
198/// )
199/// ```
200///
201/// See also:
202///
203/// * [Scaffold], which provides standard app elements like an [AppBar] and a [Drawer].
204/// * [Navigator], which is used to manage the app's stack of pages.
205/// * [MaterialPageRoute], which defines an app page that transitions in a material-specific way.
206/// * [WidgetsApp], which defines the basic app elements but does not depend on the material library.
207/// * The Flutter Internationalization Tutorial,
208/// <https://flutter.dev/to/internationalization/>.
209class MaterialApp extends StatefulWidget {
210 /// Creates a MaterialApp.
211 ///
212 /// At least one of [home], [routes], [onGenerateRoute], or [builder] must be
213 /// non-null. If only [routes] is given, it must include an entry for the
214 /// [Navigator.defaultRouteName] (`/`), since that is the route used when the
215 /// application is launched with an intent that specifies an otherwise
216 /// unsupported route.
217 ///
218 /// This class creates an instance of [WidgetsApp].
219 const MaterialApp({
220 super.key,
221 this.navigatorKey,
222 this.scaffoldMessengerKey,
223 this.home,
224 Map<String, WidgetBuilder> this.routes = const <String, WidgetBuilder>{},
225 this.initialRoute,
226 this.onGenerateRoute,
227 this.onGenerateInitialRoutes,
228 this.onUnknownRoute,
229 this.onNavigationNotification,
230 List<NavigatorObserver> this.navigatorObservers = const <NavigatorObserver>[],
231 this.builder,
232 this.title = '',
233 this.onGenerateTitle,
234 this.color,
235 this.theme,
236 this.darkTheme,
237 this.highContrastTheme,
238 this.highContrastDarkTheme,
239 this.themeMode = ThemeMode.system,
240 this.themeAnimationDuration = kThemeAnimationDuration,
241 this.themeAnimationCurve = Curves.linear,
242 this.locale,
243 this.localizationsDelegates,
244 this.localeListResolutionCallback,
245 this.localeResolutionCallback,
246 this.supportedLocales = const <Locale>[Locale('en', 'US')],
247 this.debugShowMaterialGrid = false,
248 this.showPerformanceOverlay = false,
249 this.checkerboardRasterCacheImages = false,
250 this.checkerboardOffscreenLayers = false,
251 this.showSemanticsDebugger = false,
252 this.debugShowCheckedModeBanner = true,
253 this.shortcuts,
254 this.actions,
255 this.restorationScopeId,
256 this.scrollBehavior,
257 @Deprecated(
258 'Remove this parameter as it is now ignored. '
259 'MaterialApp never introduces its own MediaQuery; the View widget takes care of that. '
260 'This feature was deprecated after v3.7.0-29.0.pre.',
261 )
262 this.useInheritedMediaQuery = false,
263 this.themeAnimationStyle,
264 }) : routeInformationProvider = null,
265 routeInformationParser = null,
266 routerDelegate = null,
267 backButtonDispatcher = null,
268 routerConfig = null;
269
270 /// Creates a [MaterialApp] that uses the [Router] instead of a [Navigator].
271 ///
272 /// {@macro flutter.widgets.WidgetsApp.router}
273 const MaterialApp.router({
274 super.key,
275 this.scaffoldMessengerKey,
276 this.routeInformationProvider,
277 this.routeInformationParser,
278 this.routerDelegate,
279 this.routerConfig,
280 this.backButtonDispatcher,
281 this.builder,
282 this.title,
283 this.onGenerateTitle,
284 this.onNavigationNotification,
285 this.color,
286 this.theme,
287 this.darkTheme,
288 this.highContrastTheme,
289 this.highContrastDarkTheme,
290 this.themeMode = ThemeMode.system,
291 this.themeAnimationDuration = kThemeAnimationDuration,
292 this.themeAnimationCurve = Curves.linear,
293 this.locale,
294 this.localizationsDelegates,
295 this.localeListResolutionCallback,
296 this.localeResolutionCallback,
297 this.supportedLocales = const <Locale>[Locale('en', 'US')],
298 this.debugShowMaterialGrid = false,
299 this.showPerformanceOverlay = false,
300 this.checkerboardRasterCacheImages = false,
301 this.checkerboardOffscreenLayers = false,
302 this.showSemanticsDebugger = false,
303 this.debugShowCheckedModeBanner = true,
304 this.shortcuts,
305 this.actions,
306 this.restorationScopeId,
307 this.scrollBehavior,
308 @Deprecated(
309 'Remove this parameter as it is now ignored. '
310 'MaterialApp never introduces its own MediaQuery; the View widget takes care of that. '
311 'This feature was deprecated after v3.7.0-29.0.pre.',
312 )
313 this.useInheritedMediaQuery = false,
314 this.themeAnimationStyle,
315 }) : assert(routerDelegate != null || routerConfig != null),
316 navigatorObservers = null,
317 navigatorKey = null,
318 onGenerateRoute = null,
319 home = null,
320 onGenerateInitialRoutes = null,
321 onUnknownRoute = null,
322 routes = null,
323 initialRoute = null;
324
325 /// {@macro flutter.widgets.widgetsApp.navigatorKey}
326 final GlobalKey<NavigatorState>? navigatorKey;
327
328 /// A key to use when building the [ScaffoldMessenger].
329 ///
330 /// If a [scaffoldMessengerKey] is specified, the [ScaffoldMessenger] can be
331 /// directly manipulated without first obtaining it from a [BuildContext] via
332 /// [ScaffoldMessenger.of]: from the [scaffoldMessengerKey], use the
333 /// [GlobalKey.currentState] getter.
334 final GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey;
335
336 /// {@macro flutter.widgets.widgetsApp.home}
337 final Widget? home;
338
339 /// The application's top-level routing table.
340 ///
341 /// When a named route is pushed with [Navigator.pushNamed], the route name is
342 /// looked up in this map. If the name is present, the associated
343 /// [WidgetBuilder] is used to construct a [MaterialPageRoute] that
344 /// performs an appropriate transition, including [Hero] animations, to the
345 /// new route.
346 ///
347 /// {@macro flutter.widgets.widgetsApp.routes}
348 final Map<String, WidgetBuilder>? routes;
349
350 /// {@macro flutter.widgets.widgetsApp.initialRoute}
351 final String? initialRoute;
352
353 /// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
354 final RouteFactory? onGenerateRoute;
355
356 /// {@macro flutter.widgets.widgetsApp.onGenerateInitialRoutes}
357 final InitialRouteListFactory? onGenerateInitialRoutes;
358
359 /// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
360 final RouteFactory? onUnknownRoute;
361
362 /// {@macro flutter.widgets.widgetsApp.onNavigationNotification}
363 final NotificationListenerCallback<NavigationNotification>? onNavigationNotification;
364
365 /// {@macro flutter.widgets.widgetsApp.navigatorObservers}
366 final List<NavigatorObserver>? navigatorObservers;
367
368 /// {@macro flutter.widgets.widgetsApp.routeInformationProvider}
369 final RouteInformationProvider? routeInformationProvider;
370
371 /// {@macro flutter.widgets.widgetsApp.routeInformationParser}
372 final RouteInformationParser<Object>? routeInformationParser;
373
374 /// {@macro flutter.widgets.widgetsApp.routerDelegate}
375 final RouterDelegate<Object>? routerDelegate;
376
377 /// {@macro flutter.widgets.widgetsApp.backButtonDispatcher}
378 final BackButtonDispatcher? backButtonDispatcher;
379
380 /// {@macro flutter.widgets.widgetsApp.routerConfig}
381 final RouterConfig<Object>? routerConfig;
382
383 /// {@macro flutter.widgets.widgetsApp.builder}
384 ///
385 /// Material specific features such as [showDialog] and [showMenu], and widgets
386 /// such as [Tooltip], [PopupMenuButton], also require a [Navigator] to properly
387 /// function.
388 final TransitionBuilder? builder;
389
390 /// {@macro flutter.widgets.widgetsApp.title}
391 ///
392 /// This value is passed unmodified to [WidgetsApp.title].
393 final String? title;
394
395 /// {@macro flutter.widgets.widgetsApp.onGenerateTitle}
396 ///
397 /// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
398 final GenerateAppTitle? onGenerateTitle;
399
400 /// Default visual properties, like colors fonts and shapes, for this app's
401 /// material widgets.
402 ///
403 /// A second [darkTheme] [ThemeData] value, which is used to provide a dark
404 /// version of the user interface can also be specified. [themeMode] will
405 /// control which theme will be used if a [darkTheme] is provided.
406 ///
407 /// The default value of this property is the value of [ThemeData.light()].
408 ///
409 /// See also:
410 ///
411 /// * [themeMode], which controls which theme to use.
412 /// * [MediaQueryData.platformBrightness], which indicates the platform's
413 /// desired brightness and is used to automatically toggle between [theme]
414 /// and [darkTheme] in [MaterialApp].
415 /// * [ThemeData.brightness], which indicates the [Brightness] of a theme's
416 /// colors.
417 final ThemeData? theme;
418
419 /// The [ThemeData] to use when a 'dark mode' is requested by the system.
420 ///
421 /// Some host platforms allow the users to select a system-wide 'dark mode',
422 /// or the application may want to offer the user the ability to choose a
423 /// dark theme just for this application. This is theme that will be used for
424 /// such cases. [themeMode] will control which theme will be used.
425 ///
426 /// This theme should have a [ThemeData.brightness] set to [Brightness.dark].
427 ///
428 /// Uses [theme] instead when null. Defaults to the value of
429 /// [ThemeData.light()] when both [darkTheme] and [theme] are null.
430 ///
431 /// See also:
432 ///
433 /// * [themeMode], which controls which theme to use.
434 /// * [MediaQueryData.platformBrightness], which indicates the platform's
435 /// desired brightness and is used to automatically toggle between [theme]
436 /// and [darkTheme] in [MaterialApp].
437 /// * [ThemeData.brightness], which is typically set to the value of
438 /// [MediaQueryData.platformBrightness].
439 final ThemeData? darkTheme;
440
441 /// The [ThemeData] to use when 'high contrast' is requested by the system.
442 ///
443 /// Some host platforms (for example, iOS) allow the users to increase
444 /// contrast through an accessibility setting.
445 ///
446 /// Uses [theme] instead when null.
447 ///
448 /// See also:
449 ///
450 /// * [MediaQueryData.highContrast], which indicates the platform's
451 /// desire to increase contrast.
452 final ThemeData? highContrastTheme;
453
454 /// The [ThemeData] to use when a 'dark mode' and 'high contrast' is requested
455 /// by the system.
456 ///
457 /// Some host platforms (for example, iOS) allow the users to increase
458 /// contrast through an accessibility setting.
459 ///
460 /// This theme should have a [ThemeData.brightness] set to [Brightness.dark].
461 ///
462 /// Uses [darkTheme] instead when null.
463 ///
464 /// See also:
465 ///
466 /// * [MediaQueryData.highContrast], which indicates the platform's
467 /// desire to increase contrast.
468 final ThemeData? highContrastDarkTheme;
469
470 /// Determines which theme will be used by the application if both [theme]
471 /// and [darkTheme] are provided.
472 ///
473 /// If set to [ThemeMode.system], the choice of which theme to use will
474 /// be based on the user's system preferences. If the [MediaQuery.platformBrightnessOf]
475 /// is [Brightness.light], [theme] will be used. If it is [Brightness.dark],
476 /// [darkTheme] will be used (unless it is null, in which case [theme]
477 /// will be used.
478 ///
479 /// If set to [ThemeMode.light] the [theme] will always be used,
480 /// regardless of the user's system preference.
481 ///
482 /// If set to [ThemeMode.dark] the [darkTheme] will be used
483 /// regardless of the user's system preference. If [darkTheme] is null
484 /// then it will fallback to using [theme].
485 ///
486 /// The default value is [ThemeMode.system].
487 ///
488 /// See also:
489 ///
490 /// * [theme], which is used when a light mode is selected.
491 /// * [darkTheme], which is used when a dark mode is selected.
492 /// * [ThemeData.brightness], which indicates to various parts of the
493 /// system what kind of theme is being used.
494 final ThemeMode? themeMode;
495
496 /// The duration of animated theme changes.
497 ///
498 /// When the theme changes (either by the [theme], [darkTheme] or [themeMode]
499 /// parameters changing) it is animated to the new theme over time.
500 /// The [themeAnimationDuration] determines how long this animation takes.
501 ///
502 /// To have the theme change immediately, you can set this to [Duration.zero].
503 ///
504 /// The default is [kThemeAnimationDuration].
505 ///
506 /// See also:
507 /// [themeAnimationCurve], which defines the curve used for the animation.
508 final Duration themeAnimationDuration;
509
510 /// The curve to apply when animating theme changes.
511 ///
512 /// The default is [Curves.linear].
513 ///
514 /// This is ignored if [themeAnimationDuration] is [Duration.zero].
515 ///
516 /// See also:
517 /// [themeAnimationDuration], which defines how long the animation is.
518 final Curve themeAnimationCurve;
519
520 /// {@macro flutter.widgets.widgetsApp.color}
521 final Color? color;
522
523 /// {@macro flutter.widgets.widgetsApp.locale}
524 final Locale? locale;
525
526 /// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
527 ///
528 /// Internationalized apps that require translations for one of the locales
529 /// listed in [GlobalMaterialLocalizations] should specify this parameter
530 /// and list the [supportedLocales] that the application can handle.
531 ///
532 /// ```dart
533 /// // The GlobalMaterialLocalizations and GlobalWidgetsLocalizations
534 /// // classes require the following import:
535 /// // import 'package:flutter_localizations/flutter_localizations.dart';
536 ///
537 /// const MaterialApp(
538 /// localizationsDelegates: <LocalizationsDelegate<Object>>[
539 /// // ... app-specific localization delegate(s) here
540 /// GlobalMaterialLocalizations.delegate,
541 /// GlobalWidgetsLocalizations.delegate,
542 /// ],
543 /// supportedLocales: <Locale>[
544 /// Locale('en', 'US'), // English
545 /// Locale('he', 'IL'), // Hebrew
546 /// // ... other locales the app supports
547 /// ],
548 /// // ...
549 /// )
550 /// ```
551 ///
552 /// ## Adding localizations for a new locale
553 ///
554 /// The information that follows applies to the unusual case of an app
555 /// adding translations for a language not already supported by
556 /// [GlobalMaterialLocalizations].
557 ///
558 /// Delegates that produce [WidgetsLocalizations] and [MaterialLocalizations]
559 /// are included automatically. Apps can provide their own versions of these
560 /// localizations by creating implementations of
561 /// [LocalizationsDelegate<WidgetsLocalizations>] or
562 /// [LocalizationsDelegate<MaterialLocalizations>] whose load methods return
563 /// custom versions of [WidgetsLocalizations] or [MaterialLocalizations].
564 ///
565 /// For example: to add support to [MaterialLocalizations] for a locale it
566 /// doesn't already support, say `const Locale('foo', 'BR')`, one first
567 /// creates a subclass of [MaterialLocalizations] that provides the
568 /// translations:
569 ///
570 /// ```dart
571 /// class FooLocalizations extends MaterialLocalizations {
572 /// FooLocalizations();
573 /// @override
574 /// String get okButtonLabel => 'foo';
575 /// // ...
576 /// // lots of other getters and methods to override!
577 /// }
578 /// ```
579 ///
580 /// One must then create a [LocalizationsDelegate] subclass that can provide
581 /// an instance of the [MaterialLocalizations] subclass. In this case, this is
582 /// essentially just a method that constructs a `FooLocalizations` object. A
583 /// [SynchronousFuture] is used here because no asynchronous work takes place
584 /// upon "loading" the localizations object.
585 ///
586 /// ```dart
587 /// // continuing from previous example...
588 /// class FooLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
589 /// const FooLocalizationsDelegate();
590 /// @override
591 /// bool isSupported(Locale locale) {
592 /// return locale == const Locale('foo', 'BR');
593 /// }
594 /// @override
595 /// Future<FooLocalizations> load(Locale locale) {
596 /// assert(locale == const Locale('foo', 'BR'));
597 /// return SynchronousFuture<FooLocalizations>(FooLocalizations());
598 /// }
599 /// @override
600 /// bool shouldReload(FooLocalizationsDelegate old) => false;
601 /// }
602 /// ```
603 ///
604 /// Constructing a [MaterialApp] with a `FooLocalizationsDelegate` overrides
605 /// the automatically included delegate for [MaterialLocalizations] because
606 /// only the first delegate of each [LocalizationsDelegate.type] is used and
607 /// the automatically included delegates are added to the end of the app's
608 /// [localizationsDelegates] list.
609 ///
610 /// ```dart
611 /// // continuing from previous example...
612 /// const MaterialApp(
613 /// localizationsDelegates: <LocalizationsDelegate<Object>>[
614 /// FooLocalizationsDelegate(),
615 /// ],
616 /// // ...
617 /// )
618 /// ```
619 /// See also:
620 ///
621 /// * [supportedLocales], which must be specified along with
622 /// [localizationsDelegates].
623 /// * [GlobalMaterialLocalizations], a [localizationsDelegates] value
624 /// which provides material localizations for many languages.
625 /// * The Flutter Internationalization Tutorial,
626 /// <https://flutter.dev/to/internationalization/>.
627 final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
628
629 /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
630 ///
631 /// This callback is passed along to the [WidgetsApp] built by this widget.
632 final LocaleListResolutionCallback? localeListResolutionCallback;
633
634 /// {@macro flutter.widgets.LocaleResolutionCallback}
635 ///
636 /// This callback is passed along to the [WidgetsApp] built by this widget.
637 final LocaleResolutionCallback? localeResolutionCallback;
638
639 /// {@macro flutter.widgets.widgetsApp.supportedLocales}
640 ///
641 /// It is passed along unmodified to the [WidgetsApp] built by this widget.
642 ///
643 /// See also:
644 ///
645 /// * [localizationsDelegates], which must be specified for localized
646 /// applications.
647 /// * [GlobalMaterialLocalizations], a [localizationsDelegates] value
648 /// which provides material localizations for many languages.
649 /// * The Flutter Internationalization Tutorial,
650 /// <https://flutter.dev/to/internationalization/>.
651 final Iterable<Locale> supportedLocales;
652
653 /// Turns on a performance overlay.
654 ///
655 /// See also:
656 ///
657 /// * <https://flutter.dev/to/performance-overlay>
658 final bool showPerformanceOverlay;
659
660 /// Turns on checkerboarding of raster cache images.
661 final bool checkerboardRasterCacheImages;
662
663 /// Turns on checkerboarding of layers rendered to offscreen bitmaps.
664 final bool checkerboardOffscreenLayers;
665
666 /// Turns on an overlay that shows the accessibility information
667 /// reported by the framework.
668 final bool showSemanticsDebugger;
669
670 /// {@macro flutter.widgets.widgetsApp.debugShowCheckedModeBanner}
671 final bool debugShowCheckedModeBanner;
672
673 /// {@macro flutter.widgets.widgetsApp.shortcuts}
674 /// {@tool snippet}
675 /// This example shows how to add a single shortcut for
676 /// [LogicalKeyboardKey.select] to the default shortcuts without needing to
677 /// add your own [Shortcuts] widget.
678 ///
679 /// Alternatively, you could insert a [Shortcuts] widget with just the mapping
680 /// you want to add between the [WidgetsApp] and its child and get the same
681 /// effect.
682 ///
683 /// ```dart
684 /// Widget build(BuildContext context) {
685 /// return WidgetsApp(
686 /// shortcuts: <ShortcutActivator, Intent>{
687 /// ... WidgetsApp.defaultShortcuts,
688 /// const SingleActivator(LogicalKeyboardKey.select): const ActivateIntent(),
689 /// },
690 /// color: const Color(0xFFFF0000),
691 /// builder: (BuildContext context, Widget? child) {
692 /// return const Placeholder();
693 /// },
694 /// );
695 /// }
696 /// ```
697 /// {@end-tool}
698 /// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso}
699 final Map<ShortcutActivator, Intent>? shortcuts;
700
701 /// {@macro flutter.widgets.widgetsApp.actions}
702 /// {@tool snippet}
703 /// This example shows how to add a single action handling an
704 /// [ActivateAction] to the default actions without needing to
705 /// add your own [Actions] widget.
706 ///
707 /// Alternatively, you could insert a [Actions] widget with just the mapping
708 /// you want to add between the [WidgetsApp] and its child and get the same
709 /// effect.
710 ///
711 /// ```dart
712 /// Widget build(BuildContext context) {
713 /// return WidgetsApp(
714 /// actions: <Type, Action<Intent>>{
715 /// ... WidgetsApp.defaultActions,
716 /// ActivateAction: CallbackAction<Intent>(
717 /// onInvoke: (Intent intent) {
718 /// // Do something here...
719 /// return null;
720 /// },
721 /// ),
722 /// },
723 /// color: const Color(0xFFFF0000),
724 /// builder: (BuildContext context, Widget? child) {
725 /// return const Placeholder();
726 /// },
727 /// );
728 /// }
729 /// ```
730 /// {@end-tool}
731 /// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
732 final Map<Type, Action<Intent>>? actions;
733
734 /// {@macro flutter.widgets.widgetsApp.restorationScopeId}
735 final String? restorationScopeId;
736
737 /// {@template flutter.material.materialApp.scrollBehavior}
738 /// The default [ScrollBehavior] for the application.
739 ///
740 /// [ScrollBehavior]s describe how [Scrollable] widgets behave. Providing
741 /// a [ScrollBehavior] can set the default [ScrollPhysics] across
742 /// an application, and manage [Scrollable] decorations like [Scrollbar]s and
743 /// [GlowingOverscrollIndicator]s.
744 /// {@endtemplate}
745 ///
746 /// When null, defaults to [MaterialScrollBehavior].
747 ///
748 /// See also:
749 ///
750 /// * [ScrollConfiguration], which controls how [Scrollable] widgets behave
751 /// in a subtree.
752 final ScrollBehavior? scrollBehavior;
753
754 /// Turns on a [GridPaper] overlay that paints a baseline grid
755 /// Material apps.
756 ///
757 /// Only available in debug mode.
758 ///
759 /// See also:
760 ///
761 /// * <https://material.io/design/layout/spacing-methods.html>
762 final bool debugShowMaterialGrid;
763
764 /// {@macro flutter.widgets.widgetsApp.useInheritedMediaQuery}
765 @Deprecated(
766 'This setting is now ignored. '
767 'MaterialApp never introduces its own MediaQuery; the View widget takes care of that. '
768 'This feature was deprecated after v3.7.0-29.0.pre.',
769 )
770 final bool useInheritedMediaQuery;
771
772 /// Used to override the theme animation curve and duration.
773 ///
774 /// If [AnimationStyle.duration] is provided, it will be used to override
775 /// the theme animation duration in the underlying [AnimatedTheme] widget.
776 /// If it is null, then [themeAnimationDuration] will be used. Otherwise,
777 /// defaults to 200ms.
778 ///
779 /// If [AnimationStyle.curve] is provided, it will be used to override
780 /// the theme animation curve in the underlying [AnimatedTheme] widget.
781 /// If it is null, then [themeAnimationCurve] will be used. Otherwise,
782 /// defaults to [Curves.linear].
783 ///
784 /// To disable the theme animation, use [AnimationStyle.noAnimation].
785 ///
786 /// {@tool dartpad}
787 /// This sample showcases how to override the theme animation curve and
788 /// duration in the [MaterialApp] widget using [AnimationStyle].
789 ///
790 /// ** See code in examples/api/lib/material/app/app.0.dart **
791 /// {@end-tool}
792 final AnimationStyle? themeAnimationStyle;
793
794 @override
795 State<MaterialApp> createState() => _MaterialAppState();
796
797 /// The [HeroController] used for Material page transitions.
798 ///
799 /// Used by the [MaterialApp].
800 static HeroController createMaterialHeroController() {
801 return HeroController(
802 createRectTween: (Rect? begin, Rect? end) {
803 return MaterialRectArcTween(begin: begin, end: end);
804 },
805 );
806 }
807}
808
809/// Describes how [Scrollable] widgets behave for [MaterialApp]s.
810///
811/// {@macro flutter.widgets.scrollBehavior}
812///
813/// Setting a [MaterialScrollBehavior] will apply a
814/// [GlowingOverscrollIndicator] to [Scrollable] descendants when executing on
815/// [TargetPlatform.android] and [TargetPlatform.fuchsia].
816///
817/// When using the desktop platform, if the [Scrollable] widget scrolls in the
818/// [Axis.vertical], a [Scrollbar] is applied.
819///
820/// If the scroll direction is [Axis.horizontal] scroll views are less
821/// discoverable, so consider adding a Scrollbar in these cases, either directly
822/// or through the [buildScrollbar] method.
823///
824/// [ThemeData.useMaterial3] specifies the
825/// overscroll indicator that is used on [TargetPlatform.android], which
826/// defaults to true, resulting in a [StretchingOverscrollIndicator]. Setting
827/// [ThemeData.useMaterial3] to false will instead use a
828/// [GlowingOverscrollIndicator].
829///
830/// See also:
831///
832/// * [ScrollBehavior], the default scrolling behavior extended by this class.
833class MaterialScrollBehavior extends ScrollBehavior {
834 /// Creates a MaterialScrollBehavior that decorates [Scrollable]s with
835 /// [StretchingOverscrollIndicator]s and [Scrollbar]s based on the current
836 /// platform and provided [ScrollableDetails].
837 ///
838 /// [ThemeData.useMaterial3] specifies the
839 /// overscroll indicator that is used on [TargetPlatform.android], which
840 /// defaults to true, resulting in a [StretchingOverscrollIndicator]. Setting
841 /// [ThemeData.useMaterial3] to false will instead use a
842 /// [GlowingOverscrollIndicator].
843 const MaterialScrollBehavior();
844
845 @override
846 TargetPlatform getPlatform(BuildContext context) => Theme.of(context).platform;
847
848 @override
849 Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) {
850 // When modifying this function, consider modifying the implementation in
851 // the base class ScrollBehavior as well.
852 switch (axisDirectionToAxis(details.direction)) {
853 case Axis.horizontal:
854 return child;
855 case Axis.vertical:
856 switch (getPlatform(context)) {
857 case TargetPlatform.linux:
858 case TargetPlatform.macOS:
859 case TargetPlatform.windows:
860 assert(details.controller != null);
861 return Scrollbar(controller: details.controller, child: child);
862 case TargetPlatform.android:
863 case TargetPlatform.fuchsia:
864 case TargetPlatform.iOS:
865 return child;
866 }
867 }
868 }
869
870 @override
871 Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
872 // When modifying this function, consider modifying the implementation in
873 // the base class ScrollBehavior as well.
874 final AndroidOverscrollIndicator indicator =
875 Theme.of(context).useMaterial3
876 ? AndroidOverscrollIndicator.stretch
877 : AndroidOverscrollIndicator.glow;
878 switch (getPlatform(context)) {
879 case TargetPlatform.iOS:
880 case TargetPlatform.linux:
881 case TargetPlatform.macOS:
882 case TargetPlatform.windows:
883 return child;
884 case TargetPlatform.android:
885 switch (indicator) {
886 case AndroidOverscrollIndicator.stretch:
887 return StretchingOverscrollIndicator(
888 axisDirection: details.direction,
889 clipBehavior: details.clipBehavior ?? Clip.hardEdge,
890 child: child,
891 );
892 case AndroidOverscrollIndicator.glow:
893 break;
894 }
895 case TargetPlatform.fuchsia:
896 break;
897 }
898 return GlowingOverscrollIndicator(
899 axisDirection: details.direction,
900 color: Theme.of(context).colorScheme.secondary,
901 child: child,
902 );
903 }
904}
905
906class _MaterialAppState extends State<MaterialApp> {
907 late HeroController _heroController;
908
909 bool get _usesRouter => widget.routerDelegate != null || widget.routerConfig != null;
910
911 @override
912 void initState() {
913 super.initState();
914 _heroController = MaterialApp.createMaterialHeroController();
915 }
916
917 @override
918 void dispose() {
919 _heroController.dispose();
920 super.dispose();
921 }
922
923 // Combine the Localizations for Material with the ones contributed
924 // by the localizationsDelegates parameter, if any. Only the first delegate
925 // of a particular LocalizationsDelegate.type is loaded so the
926 // localizationsDelegate parameter can be used to override
927 // _MaterialLocalizationsDelegate.
928 Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
929 return <LocalizationsDelegate<dynamic>>[
930 if (widget.localizationsDelegates != null) ...widget.localizationsDelegates!,
931 DefaultMaterialLocalizations.delegate,
932 DefaultCupertinoLocalizations.delegate,
933 ];
934 }
935
936 Widget _exitWidgetSelectionButtonBuilder(
937 BuildContext context, {
938 required VoidCallback onPressed,
939 required String semanticLabel,
940 required GlobalKey key,
941 }) {
942 return _MaterialInspectorButton.filled(
943 onPressed: onPressed,
944 semanticLabel: semanticLabel,
945 icon: Icons.close,
946 isDarkTheme: _isDarkTheme(context),
947 buttonKey: key,
948 );
949 }
950
951 Widget _moveExitWidgetSelectionButtonBuilder(
952 BuildContext context, {
953 required VoidCallback onPressed,
954 required String semanticLabel,
955 bool isLeftAligned = true,
956 }) {
957 return _MaterialInspectorButton.iconOnly(
958 onPressed: onPressed,
959 semanticLabel: semanticLabel,
960 icon: isLeftAligned ? Icons.arrow_right : Icons.arrow_left,
961 isDarkTheme: _isDarkTheme(context),
962 );
963 }
964
965 Widget _tapBehaviorButtonBuilder(
966 BuildContext context, {
967 required VoidCallback onPressed,
968 required String semanticLabel,
969 required bool selectionOnTapEnabled,
970 }) {
971 return _MaterialInspectorButton.toggle(
972 onPressed: onPressed,
973 semanticLabel: semanticLabel,
974 // This unicode icon is also used for the Cupertino-styled button and for
975 // DevTools. It should be updated in all 3 places if changed.
976 icon: const IconData(0x1F74A),
977 isDarkTheme: _isDarkTheme(context),
978 toggledOn: selectionOnTapEnabled,
979 );
980 }
981
982 bool _isDarkTheme(BuildContext context) {
983 return widget.themeMode == ThemeMode.dark ||
984 widget.themeMode == ThemeMode.system &&
985 MediaQuery.platformBrightnessOf(context) == Brightness.dark;
986 }
987
988 ThemeData _themeBuilder(BuildContext context) {
989 ThemeData? theme;
990 // Resolve which theme to use based on brightness and high contrast.
991 final ThemeMode mode = widget.themeMode ?? ThemeMode.system;
992 final Brightness platformBrightness = MediaQuery.platformBrightnessOf(context);
993 final bool useDarkTheme =
994 mode == ThemeMode.dark ||
995 (mode == ThemeMode.system && platformBrightness == ui.Brightness.dark);
996 final bool highContrast = MediaQuery.highContrastOf(context);
997 if (useDarkTheme && highContrast && widget.highContrastDarkTheme != null) {
998 theme = widget.highContrastDarkTheme;
999 } else if (useDarkTheme && widget.darkTheme != null) {
1000 theme = widget.darkTheme;
1001 } else if (highContrast && widget.highContrastTheme != null) {
1002 theme = widget.highContrastTheme;
1003 }
1004 theme ??= widget.theme ?? ThemeData();
1005 SystemChrome.setSystemUIOverlayStyle(
1006 theme.brightness == Brightness.dark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark,
1007 );
1008
1009 return theme;
1010 }
1011
1012 Widget _materialBuilder(BuildContext context, Widget? child) {
1013 final ThemeData theme = _themeBuilder(context);
1014 final Color effectiveSelectionColor =
1015 theme.textSelectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
1016 final Color effectiveCursorColor =
1017 theme.textSelectionTheme.cursorColor ?? theme.colorScheme.primary;
1018
1019 Widget childWidget = child ?? const SizedBox.shrink();
1020
1021 if (widget.builder != null) {
1022 childWidget = Builder(
1023 builder: (BuildContext context) {
1024 // Why are we surrounding a builder with a builder?
1025 //
1026 // The widget.builder may contain code that invokes
1027 // Theme.of(), which should return the theme we selected
1028 // above in AnimatedTheme. However, if we invoke
1029 // widget.builder() directly as the child of AnimatedTheme
1030 // then there is no BuildContext separating them, the
1031 // widget.builder() will not find the theme. Therefore, we
1032 // surround widget.builder with yet another builder so that
1033 // a context separates them and Theme.of() correctly
1034 // resolves to the theme we passed to AnimatedTheme.
1035 return widget.builder!(context, child);
1036 },
1037 );
1038 }
1039
1040 childWidget = ScaffoldMessenger(
1041 key: widget.scaffoldMessengerKey,
1042 child: DefaultSelectionStyle(
1043 selectionColor: effectiveSelectionColor,
1044 cursorColor: effectiveCursorColor,
1045 child: childWidget,
1046 ),
1047 );
1048
1049 if (widget.themeAnimationStyle != AnimationStyle.noAnimation) {
1050 childWidget = AnimatedTheme(
1051 data: theme,
1052 duration: widget.themeAnimationStyle?.duration ?? widget.themeAnimationDuration,
1053 curve: widget.themeAnimationStyle?.curve ?? widget.themeAnimationCurve,
1054 child: childWidget,
1055 );
1056 } else {
1057 childWidget = Theme(data: theme, child: childWidget);
1058 }
1059
1060 return childWidget;
1061 }
1062
1063 Widget _buildWidgetApp(BuildContext context) {
1064 // The color property is always pulled from the light theme, even if dark
1065 // mode is activated. This was done to simplify the technical details
1066 // of switching themes and it was deemed acceptable because this color
1067 // property is only used on old Android OSes to color the app bar in
1068 // Android's switcher UI.
1069 //
1070 // blue is the primary color of the default theme.
1071 final Color materialColor = widget.color ?? widget.theme?.primaryColor ?? Colors.blue;
1072 if (_usesRouter) {
1073 return WidgetsApp.router(
1074 key: GlobalObjectKey(this),
1075 routeInformationProvider: widget.routeInformationProvider,
1076 routeInformationParser: widget.routeInformationParser,
1077 routerDelegate: widget.routerDelegate,
1078 routerConfig: widget.routerConfig,
1079 backButtonDispatcher: widget.backButtonDispatcher,
1080 onNavigationNotification: widget.onNavigationNotification,
1081 builder: _materialBuilder,
1082 title: widget.title,
1083 onGenerateTitle: widget.onGenerateTitle,
1084 textStyle: _errorTextStyle,
1085 color: materialColor,
1086 locale: widget.locale,
1087 localizationsDelegates: _localizationsDelegates,
1088 localeResolutionCallback: widget.localeResolutionCallback,
1089 localeListResolutionCallback: widget.localeListResolutionCallback,
1090 supportedLocales: widget.supportedLocales,
1091 showPerformanceOverlay: widget.showPerformanceOverlay,
1092 showSemanticsDebugger: widget.showSemanticsDebugger,
1093 debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,
1094 exitWidgetSelectionButtonBuilder: _exitWidgetSelectionButtonBuilder,
1095 moveExitWidgetSelectionButtonBuilder: _moveExitWidgetSelectionButtonBuilder,
1096 tapBehaviorButtonBuilder: _tapBehaviorButtonBuilder,
1097 shortcuts: widget.shortcuts,
1098 actions: widget.actions,
1099 restorationScopeId: widget.restorationScopeId,
1100 );
1101 }
1102
1103 return WidgetsApp(
1104 key: GlobalObjectKey(this),
1105 navigatorKey: widget.navigatorKey,
1106 navigatorObservers: widget.navigatorObservers!,
1107 pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
1108 return MaterialPageRoute<T>(settings: settings, builder: builder);
1109 },
1110 home: widget.home,
1111 routes: widget.routes!,
1112 initialRoute: widget.initialRoute,
1113 onGenerateRoute: widget.onGenerateRoute,
1114 onGenerateInitialRoutes: widget.onGenerateInitialRoutes,
1115 onUnknownRoute: widget.onUnknownRoute,
1116 onNavigationNotification: widget.onNavigationNotification,
1117 builder: _materialBuilder,
1118 title: widget.title,
1119 onGenerateTitle: widget.onGenerateTitle,
1120 textStyle: _errorTextStyle,
1121 color: materialColor,
1122 locale: widget.locale,
1123 localizationsDelegates: _localizationsDelegates,
1124 localeResolutionCallback: widget.localeResolutionCallback,
1125 localeListResolutionCallback: widget.localeListResolutionCallback,
1126 supportedLocales: widget.supportedLocales,
1127 showPerformanceOverlay: widget.showPerformanceOverlay,
1128 showSemanticsDebugger: widget.showSemanticsDebugger,
1129 debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,
1130 exitWidgetSelectionButtonBuilder: _exitWidgetSelectionButtonBuilder,
1131 moveExitWidgetSelectionButtonBuilder: _moveExitWidgetSelectionButtonBuilder,
1132 tapBehaviorButtonBuilder: _tapBehaviorButtonBuilder,
1133 shortcuts: widget.shortcuts,
1134 actions: widget.actions,
1135 restorationScopeId: widget.restorationScopeId,
1136 );
1137 }
1138
1139 @override
1140 Widget build(BuildContext context) {
1141 Widget result = _buildWidgetApp(context);
1142 result = Focus(
1143 canRequestFocus: false,
1144 onKeyEvent: (FocusNode node, KeyEvent event) {
1145 if ((event is! KeyDownEvent && event is! KeyRepeatEvent) ||
1146 event.logicalKey != LogicalKeyboardKey.escape) {
1147 return KeyEventResult.ignored;
1148 }
1149 return Tooltip.dismissAllToolTips() ? KeyEventResult.handled : KeyEventResult.ignored;
1150 },
1151 child: result,
1152 );
1153 assert(() {
1154 if (widget.debugShowMaterialGrid) {
1155 result = GridPaper(
1156 color: const Color(0xE0F9BBE0),
1157 interval: 8.0,
1158 subdivisions: 1,
1159 child: result,
1160 );
1161 }
1162 return true;
1163 }());
1164
1165 return ScrollConfiguration(
1166 behavior: widget.scrollBehavior ?? const MaterialScrollBehavior(),
1167 child: HeroControllerScope(controller: _heroController, child: result),
1168 );
1169 }
1170}
1171
1172class _MaterialInspectorButton extends InspectorButton {
1173 const _MaterialInspectorButton.filled({
1174 required super.onPressed,
1175 required super.semanticLabel,
1176 required super.icon,
1177 required this.isDarkTheme,
1178 super.buttonKey,
1179 }) : super.filled();
1180
1181 const _MaterialInspectorButton.toggle({
1182 required super.onPressed,
1183 required super.semanticLabel,
1184 required super.icon,
1185 required this.isDarkTheme,
1186 super.toggledOn,
1187 }) : super.toggle();
1188
1189 const _MaterialInspectorButton.iconOnly({
1190 required super.onPressed,
1191 required super.semanticLabel,
1192 required super.icon,
1193 required this.isDarkTheme,
1194 }) : super.iconOnly();
1195
1196 final bool isDarkTheme;
1197
1198 static const EdgeInsets _buttonPadding = EdgeInsets.zero;
1199 static const BoxConstraints _buttonConstraints = BoxConstraints.tightFor(
1200 width: InspectorButton.buttonSize,
1201 height: InspectorButton.buttonSize,
1202 );
1203
1204 @override
1205 Widget build(BuildContext context) {
1206 return IconButton(
1207 key: buttonKey,
1208 onPressed: onPressed,
1209 iconSize: iconSizeForVariant,
1210 padding: _buttonPadding,
1211 constraints: _buttonConstraints,
1212 style: _selectionButtonsIconStyle(context),
1213 icon: Icon(icon, semanticLabel: semanticLabel),
1214 );
1215 }
1216
1217 ButtonStyle _selectionButtonsIconStyle(BuildContext context) {
1218 final Color foreground = foregroundColor(context);
1219 final Color background = backgroundColor(context);
1220
1221 return IconButton.styleFrom(
1222 foregroundColor: foreground,
1223 backgroundColor: background,
1224 side:
1225 variant == InspectorButtonVariant.toggle && !toggledOn!
1226 ? BorderSide(color: foreground)
1227 : null,
1228 tapTargetSize: MaterialTapTargetSize.padded,
1229 );
1230 }
1231
1232 @override
1233 Color foregroundColor(BuildContext context) {
1234 final Color primaryColor = _primaryColor(context);
1235 final Color secondaryColor = _secondaryColor(context);
1236 switch (variant) {
1237 case InspectorButtonVariant.filled:
1238 return primaryColor;
1239 case InspectorButtonVariant.iconOnly:
1240 return secondaryColor;
1241 case InspectorButtonVariant.toggle:
1242 return !toggledOn! ? secondaryColor : primaryColor;
1243 }
1244 }
1245
1246 @override
1247 Color backgroundColor(BuildContext context) {
1248 final Color secondaryColor = _secondaryColor(context);
1249 switch (variant) {
1250 case InspectorButtonVariant.filled:
1251 return secondaryColor;
1252 case InspectorButtonVariant.iconOnly:
1253 return Colors.transparent;
1254 case InspectorButtonVariant.toggle:
1255 return !toggledOn! ? Colors.transparent : secondaryColor;
1256 }
1257 }
1258
1259 Color _primaryColor(BuildContext context) {
1260 final ThemeData theme = Theme.of(context);
1261 return isDarkTheme ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.primaryContainer;
1262 }
1263
1264 Color _secondaryColor(BuildContext context) {
1265 final ThemeData theme = Theme.of(context);
1266 return isDarkTheme ? theme.colorScheme.primaryContainer : theme.colorScheme.onPrimaryContainer;
1267 }
1268}
1269

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com