app.dart 23.6 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:ui' as ui;

7
import 'package:flutter/cupertino.dart';
8
import 'package:flutter/rendering.dart';
9
import 'package:flutter/widgets.dart';
10

11
import 'arc.dart';
12
import 'colors.dart';
13 14
import 'floating_action_button.dart';
import 'icons.dart';
15
import 'material_localizations.dart';
16
import 'page.dart';
17
import 'theme.dart';
18

19 20 21 22 23 24 25
/// [MaterialApp] uses this [TextStyle] as its [DefaultTextStyle] to encourage
/// developers to be intentional about their [DefaultTextStyle].
///
/// In Material Design, most [Text] widgets are contained in [Material] widgets,
/// which sets a specific [DefaultTextStyle]. If you're seeing text that uses
/// this text style, consider putting your text in a [Material] widget (or
/// another widget that sets a [DefaultTextStyle]).
26 27
const TextStyle _errorTextStyle = TextStyle(
  color: Color(0xD0FF0000),
28 29 30
  fontFamily: 'monospace',
  fontSize: 48.0,
  fontWeight: FontWeight.w900,
31
  decoration: TextDecoration.underline,
32
  decorationColor: Color(0xFFFFFF00),
33 34
  decorationStyle: TextDecorationStyle.double,
  debugLabel: 'fallback style; consider putting your text in a Material',
35 36
);

37 38 39 40 41 42 43 44 45 46 47 48 49
/// Describes which theme will be used by [MaterialApp].
enum ThemeMode {
  /// Use either the light or dark theme based on what the user has selected in
  /// the system settings.
  system,

  /// Always use the light mode regardless of system preference.
  light,

  /// Always use the dark mode (if available) regardless of system preference.
  dark,
}

50 51 52
/// An application that uses material design.
///
/// A convenience widget that wraps a number of widgets that are commonly
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
/// required for material design applications. It builds upon a [WidgetsApp] by
/// adding material-design specific functionality, such as [AnimatedTheme] and
/// [GridPaper].
///
/// The [MaterialApp] configures the top-level [Navigator] to search for routes
/// in the following order:
///
///  1. For the `/` route, the [home] property, if non-null, is used.
///
///  2. Otherwise, the [routes] table is used, if it has an entry for the route.
///
///  3. Otherwise, [onGenerateRoute] is called, if provided. It should return a
///     non-null value for any _valid_ route not handled by [home] and [routes].
///
///  4. Finally if all else fails [onUnknownRoute] is called.
///
69 70 71 72
/// If a [Navigator] is created, at least one of these options must handle the
/// `/` route, since it is used when an invalid [initialRoute] is specified on
/// startup (e.g. by another application launching this one with an intent on
/// Android; see [Window.defaultRouteName]).
73
///
74 75 76 77 78
/// This widget also configures the observer of the top-level [Navigator] (if
/// any) to perform [Hero] animations.
///
/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
/// and [builder] is not null, then no [Navigator] is created.
79
///
80 81 82 83
/// {@tool sample}
/// This example shows how to create a [MaterialApp] that disables the "debug"
/// banner with a [home] route that will be displayed when the app is launched.
///
84
/// ![The MaterialApp displays a Scaffold ](https://flutter.github.io/assets-for-api-docs/assets/material/basic_material_app.png)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
///
/// ```dart
/// MaterialApp(
///   home: Scaffold(
///     appBar: AppBar(
///       title: const Text('Home'),
///     ),
///   ),
///   debugShowCheckedModeBanner: false,
/// )
/// ```
/// {@end-tool}
///
/// {@tool sample}
/// This example shows how to create a [MaterialApp] that uses the [routes]
/// `Map` to define the "home" route and an "about" route.
///
/// ```dart
/// MaterialApp(
///   routes: <String, WidgetBuilder>{
///     '/': (BuildContext context) {
///       return Scaffold(
///         appBar: AppBar(
///           title: const Text('Home Route'),
///         ),
///       );
///     },
///     '/about': (BuildContext context) {
///       return Scaffold(
///         appBar: AppBar(
///           title: const Text('About Route'),
///         ),
///       );
///      }
///    },
/// )
/// ```
/// {@end-tool}
///
/// {@tool sample}
/// This example shows how to create a [MaterialApp] that defines a [theme] that
/// will be used for material widgets in the app.
///
128
/// ![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)
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
///
/// ```dart
/// MaterialApp(
///   theme: ThemeData(
///     brightness: Brightness.dark,
///     primaryColor: Colors.blueGrey
///   ),
///   home: Scaffold(
///     appBar: AppBar(
///       title: const Text('MaterialApp Theme'),
///     ),
///   ),
/// )
/// ```
/// {@end-tool}
///
145 146
/// See also:
///
147 148 149 150
///  * [Scaffold], which provides standard app elements like an [AppBar] and a [Drawer].
///  * [Navigator], which is used to manage the app's stack of pages.
///  * [MaterialPageRoute], which defines an app page that transitions in a material-specific way.
///  * [WidgetsApp], which defines the basic app elements but does not depend on the material library.
151
///  * The Flutter Internationalization Tutorial,
152
///    <https://flutter.dev/tutorials/internationalization/>.
153
class MaterialApp extends StatefulWidget {
154 155
  /// Creates a MaterialApp.
  ///
156 157
  /// At least one of [home], [routes], [onGenerateRoute], or [builder] must be
  /// non-null. If only [routes] is given, it must include an entry for the
158 159 160
  /// [Navigator.defaultRouteName] (`/`), since that is the route used when the
  /// application is launched with an intent that specifies an otherwise
  /// unsupported route.
161
  ///
162
  /// This class creates an instance of [WidgetsApp].
163 164
  ///
  /// The boolean arguments, [routes], and [navigatorObservers], must not be null.
165
  const MaterialApp({
166
    Key key,
167
    this.navigatorKey,
168
    this.home,
169
    this.routes = const <String, WidgetBuilder>{},
170
    this.initialRoute,
171
    this.onGenerateRoute,
172
    this.onUnknownRoute,
173
    this.navigatorObservers = const <NavigatorObserver>[],
174
    this.builder,
175
    this.title = '',
176 177 178
    this.onGenerateTitle,
    this.color,
    this.theme,
179
    this.darkTheme,
180
    this.themeMode = ThemeMode.system,
181 182
    this.locale,
    this.localizationsDelegates,
183
    this.localeListResolutionCallback,
184
    this.localeResolutionCallback,
185
    this.supportedLocales = const <Locale>[Locale('en', 'US')],
186 187 188 189 190 191
    this.debugShowMaterialGrid = false,
    this.showPerformanceOverlay = false,
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,
    this.debugShowCheckedModeBanner = true,
192
  }) : assert(routes != null),
193
       assert(navigatorObservers != null),
194 195 196 197 198 199 200
       assert(title != null),
       assert(debugShowMaterialGrid != null),
       assert(showPerformanceOverlay != null),
       assert(checkerboardRasterCacheImages != null),
       assert(checkerboardOffscreenLayers != null),
       assert(showSemanticsDebugger != null),
       assert(debugShowCheckedModeBanner != null),
201
       super(key: key);
202

203
  /// {@macro flutter.widgets.widgetsApp.navigatorKey}
204
  final GlobalKey<NavigatorState> navigatorKey;
205

206
  /// {@macro flutter.widgets.widgetsApp.home}
207
  final Widget home;
208

209 210 211 212 213 214
  /// The application's top-level routing table.
  ///
  /// When a named route is pushed with [Navigator.pushNamed], the route name is
  /// looked up in this map. If the name is present, the associated
  /// [WidgetBuilder] is used to construct a [MaterialPageRoute] that performs
  /// an appropriate transition, including [Hero] animations, to the new route.
215
  ///
216
  /// {@macro flutter.widgets.widgetsApp.routes}
Ian Hickson's avatar
Ian Hickson committed
217 218
  final Map<String, WidgetBuilder> routes;

xster's avatar
xster committed
219
  /// {@macro flutter.widgets.widgetsApp.initialRoute}
220 221
  final String initialRoute;

xster's avatar
xster committed
222
  /// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
223 224
  final RouteFactory onGenerateRoute;

xster's avatar
xster committed
225
  /// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
226 227
  final RouteFactory onUnknownRoute;

xster's avatar
xster committed
228
  /// {@macro flutter.widgets.widgetsApp.navigatorObservers}
229 230
  final List<NavigatorObserver> navigatorObservers;

xster's avatar
xster committed
231
  /// {@macro flutter.widgets.widgetsApp.builder}
232
  ///
233 234
  /// Material specific features such as [showDialog] and [showMenu], and widgets
  /// such as [Tooltip], [PopupMenuButton], also require a [Navigator] to properly
235 236 237
  /// function.
  final TransitionBuilder builder;

xster's avatar
xster committed
238
  /// {@macro flutter.widgets.widgetsApp.title}
239 240 241 242
  ///
  /// This value is passed unmodified to [WidgetsApp.title].
  final String title;

xster's avatar
xster committed
243
  /// {@macro flutter.widgets.widgetsApp.onGenerateTitle}
244 245 246 247
  ///
  /// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
  final GenerateAppTitle onGenerateTitle;

248 249 250
  /// Default visual properties, like colors fonts and shapes, for this app's
  /// material widgets.
  ///
251 252 253
  /// A second [darkTheme] [ThemeData] value, which is used to provide a dark
  /// version of the user interface can also be specified. [themeMode] will
  /// control which theme will be used if a [darkTheme] is provided.
254 255 256 257 258
  ///
  /// The default value of this property is the value of [ThemeData.light()].
  ///
  /// See also:
  ///
259
  ///  * [themeMode], which controls which theme to use.
260 261 262 263 264
  ///  * [MediaQueryData.platformBrightness], which indicates the platform's
  ///    desired brightness and is used to automatically toggle between [theme]
  ///    and [darkTheme] in [MaterialApp].
  ///  * [ThemeData.brightness], which indicates the [Brightness] of a theme's
  ///    colors.
265 266
  final ThemeData theme;

267
  /// The [ThemeData] to use when a 'dark mode' is requested by the system.
268
  ///
269 270 271 272
  /// Some host platforms allow the users to select a system-wide 'dark mode',
  /// or the application may want to offer the user the ability to choose a
  /// dark theme just for this application. This is theme that will be used for
  /// such cases. [themeMode] will control which theme will be used.
273
  ///
274
  /// This theme should have a [ThemeData.brightness] set to [Brightness.dark].
275 276 277 278 279 280
  ///
  /// Uses [theme] instead when null. Defaults to the value of
  /// [ThemeData.light()] when both [darkTheme] and [theme] are null.
  ///
  /// See also:
  ///
281
  ///  * [themeMode], which controls which theme to use.
282 283 284 285 286 287 288
  ///  * [MediaQueryData.platformBrightness], which indicates the platform's
  ///    desired brightness and is used to automatically toggle between [theme]
  ///    and [darkTheme] in [MaterialApp].
  ///  * [ThemeData.brightness], which is typically set to the value of
  ///    [MediaQueryData.platformBrightness].
  final ThemeData darkTheme;

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
  /// Determines which theme will be used by the application if both [theme]
  /// and [darkTheme] are provided.
  ///
  /// If set to [ThemeMode.system], the choice of which theme to use will
  /// be based on the user's system preferences. If the [MediaQuery.platformBrightnessOf]
  /// is [Brightness.light], [theme] will be used. If it is [Brightness.dark],
  /// [darkTheme] will be used (unless it is [null], in which case [theme]
  /// will be used.
  ///
  /// If set to [ThemeMode.light] the [theme] will always be used,
  /// regardless of the user's system preference.
  ///
  /// If set to [ThemeMode.dark] the [darkTheme] will be used
  /// regardless of the user's system preference. If [darkTheme] is [null]
  /// then it will fallback to using [theme].
  ///
  /// The default value is [ThemeMode.system].
  ///
  /// See also:
  ///
  ///   * [theme], which is used when a light mode is selected.
  ///   * [darkTheme], which is used when a dark mode is selected.
  ///   * [ThemeData.brightness], which indicates to various parts of the
  ///     system what kind of theme is being used.
  final ThemeMode themeMode;

xster's avatar
xster committed
315
  /// {@macro flutter.widgets.widgetsApp.color}
316 317
  final Color color;

xster's avatar
xster committed
318
  /// {@macro flutter.widgets.widgetsApp.locale}
319 320
  final Locale locale;

xster's avatar
xster committed
321
  /// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
322
  ///
323
  /// Internationalized apps that require translations for one of the locales
324
  /// listed in [GlobalMaterialLocalizations] should specify this parameter
325 326 327 328 329
  /// and list the [supportedLocales] that the application can handle.
  ///
  /// ```dart
  /// import 'package:flutter_localizations/flutter_localizations.dart';
  /// MaterialApp(
330 331 332 333 334 335
  ///   localizationsDelegates: [
  ///     // ... app-specific localization delegate[s] here
  ///     GlobalMaterialLocalizations.delegate,
  ///     GlobalWidgetsLocalizations.delegate,
  ///   ],
  ///   supportedLocales: [
336 337 338 339 340 341 342 343 344 345 346 347 348 349
  ///     const Locale('en', 'US'), // English
  ///     const Locale('he', 'IL'), // Hebrew
  ///     // ... other locales the app supports
  ///   ],
  ///   // ...
  /// )
  /// ```
  ///
  /// ## Adding localizations for a new locale
  ///
  /// The information that follows applies to the unusual case of an app
  /// adding translations for a language not already supported by
  /// [GlobalMaterialLocalizations].
  ///
350 351 352
  /// Delegates that produce [WidgetsLocalizations] and [MaterialLocalizations]
  /// are included automatically. Apps can provide their own versions of these
  /// localizations by creating implementations of
353
  /// [LocalizationsDelegate<WidgetsLocalizations>] or
354
  /// [LocalizationsDelegate<MaterialLocalizations>] whose load methods return
355
  /// custom versions of [WidgetsLocalizations] or [MaterialLocalizations].
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
  ///
  /// For example: to add support to [MaterialLocalizations] for a
  /// locale it doesn't already support, say `const Locale('foo', 'BR')`,
  /// one could just extend [DefaultMaterialLocalizations]:
  ///
  /// ```dart
  /// class FooLocalizations extends DefaultMaterialLocalizations {
  ///   FooLocalizations(Locale locale) : super(locale);
  ///   @override
  ///   String get okButtonLabel {
  ///     if (locale == const Locale('foo', 'BR'))
  ///       return 'foo';
  ///     return super.okButtonLabel;
  ///   }
  /// }
  ///
  /// ```
  ///
  /// A `FooLocalizationsDelegate` is essentially just a method that constructs
  /// a `FooLocalizations` object. We return a [SynchronousFuture] here because
  /// no asynchronous work takes place upon "loading" the localizations object.
  ///
  /// ```dart
  /// class FooLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
  ///   const FooLocalizationsDelegate();
  ///   @override
  ///   Future<FooLocalizations> load(Locale locale) {
383
  ///     return SynchronousFuture(FooLocalizations(locale));
384 385 386 387 388 389 390 391 392 393 394 395 396
  ///   }
  ///   @override
  ///   bool shouldReload(FooLocalizationsDelegate old) => false;
  /// }
  /// ```
  ///
  /// Constructing a [MaterialApp] with a `FooLocalizationsDelegate` overrides
  /// the automatically included delegate for [MaterialLocalizations] because
  /// only the first delegate of each [LocalizationsDelegate.type] is used and
  /// the automatically included delegates are added to the end of the app's
  /// [localizationsDelegates] list.
  ///
  /// ```dart
397
  /// MaterialApp(
398 399 400 401 402 403
  ///   localizationsDelegates: [
  ///     const FooLocalizationsDelegate(),
  ///   ],
  ///   // ...
  /// )
  /// ```
404 405 406 407 408 409 410
  /// See also:
  ///
  ///  * [supportedLocales], which must be specified along with
  ///    [localizationsDelegates].
  ///  * [GlobalMaterialLocalizations], a [localizationsDelegates] value
  ///    which provides material localizations for many languages.
  ///  * The Flutter Internationalization Tutorial,
411
  ///    <https://flutter.dev/tutorials/internationalization/>.
412
  final Iterable<LocalizationsDelegate<dynamic>> localizationsDelegates;
413

414 415 416 417 418
  /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
  ///
  /// This callback is passed along to the [WidgetsApp] built by this widget.
  final LocaleListResolutionCallback localeListResolutionCallback;

xster's avatar
xster committed
419
  /// {@macro flutter.widgets.widgetsApp.localeResolutionCallback}
420 421 422 423
  ///
  /// This callback is passed along to the [WidgetsApp] built by this widget.
  final LocaleResolutionCallback localeResolutionCallback;

xster's avatar
xster committed
424
  /// {@macro flutter.widgets.widgetsApp.supportedLocales}
425
  ///
xster's avatar
xster committed
426
  /// It is passed along unmodified to the [WidgetsApp] built by this widget.
427
  ///
428 429 430 431 432 433 434
  /// See also:
  ///
  ///  * [localizationsDelegates], which must be specified for localized
  ///    applications.
  ///  * [GlobalMaterialLocalizations], a [localizationsDelegates] value
  ///    which provides material localizations for many languages.
  ///  * The Flutter Internationalization Tutorial,
435
  ///    <https://flutter.dev/tutorials/internationalization/>.
436 437
  final Iterable<Locale> supportedLocales;

438
  /// Turns on a performance overlay.
439 440 441
  ///
  /// See also:
  ///
442
  ///  * <https://flutter.dev/debugging/#performanceoverlay>
443 444
  final bool showPerformanceOverlay;

445 446 447
  /// Turns on checkerboarding of raster cache images.
  final bool checkerboardRasterCacheImages;

448 449 450
  /// Turns on checkerboarding of layers rendered to offscreen bitmaps.
  final bool checkerboardOffscreenLayers;

451 452 453 454
  /// Turns on an overlay that shows the accessibility information
  /// reported by the framework.
  final bool showSemanticsDebugger;

xster's avatar
xster committed
455
  /// {@macro flutter.widgets.widgetsApp.debugShowCheckedModeBanner}
456 457
  final bool debugShowCheckedModeBanner;

458
  /// Turns on a [GridPaper] overlay that paints a baseline grid
459 460
  /// Material apps.
  ///
461
  /// Only available in checked mode.
462 463 464
  ///
  /// See also:
  ///
465
  ///  * <https://material.io/design/layout/spacing-methods.html>
Ian Hickson's avatar
Ian Hickson committed
466
  final bool debugShowMaterialGrid;
467

468
  @override
469
  _MaterialAppState createState() => _MaterialAppState();
470 471
}

Adam Barth's avatar
Adam Barth committed
472
class _MaterialScrollBehavior extends ScrollBehavior {
473 474 475 476 477 478
  @override
  TargetPlatform getPlatform(BuildContext context) {
    return Theme.of(context).platform;
  }

  @override
479 480 481 482 483 484 485 486
  Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
    // When modifying this function, consider modifying the implementation in
    // the base class as well.
    switch (getPlatform(context)) {
      case TargetPlatform.iOS:
        return child;
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
487
        return GlowingOverscrollIndicator(
488 489 490 491 492 493
          child: child,
          axisDirection: axisDirection,
          color: Theme.of(context).accentColor,
        );
    }
    return null;
494 495 496
  }
}

497
class _MaterialAppState extends State<MaterialApp> {
498 499 500 501 502
  HeroController _heroController;

  @override
  void initState() {
    super.initState();
503
    _heroController = HeroController(createRectTween: _createRectTween);
504
    _updateNavigator();
505 506
  }

507 508 509 510 511 512 513 514
  @override
  void didUpdateWidget(MaterialApp oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.navigatorKey != oldWidget.navigatorKey) {
      // If the Navigator changes, we have to create a new observer, because the
      // old Navigator won't be disposed (and thus won't unregister with its
      // observers) until after the new one has been created (because the
      // Navigator has a GlobalKey).
515
      _heroController = HeroController(createRectTween: _createRectTween);
516
    }
517
    _updateNavigator();
518 519
  }

520 521 522
  List<NavigatorObserver> _navigatorObservers;

  void _updateNavigator() {
523 524 525 526 527 528
    if (widget.home != null ||
        widget.routes.isNotEmpty ||
        widget.onGenerateRoute != null ||
        widget.onUnknownRoute != null) {
      _navigatorObservers = List<NavigatorObserver>.from(widget.navigatorObservers)
        ..add(_heroController);
529
    } else {
530
      _navigatorObservers = const <NavigatorObserver>[];
531 532 533
    }
  }

534 535
  RectTween _createRectTween(Rect begin, Rect end) {
    return MaterialRectArcTween(begin: begin, end: end);
536 537
  }

538 539 540 541 542 543 544 545 546
  // Combine the Localizations for Material with the ones contributed
  // by the localizationsDelegates parameter, if any. Only the first delegate
  // of a particular LocalizationsDelegate.type is loaded so the
  // localizationsDelegate parameter can be used to override
  // _MaterialLocalizationsDelegate.
  Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
    if (widget.localizationsDelegates != null)
      yield* widget.localizationsDelegates;
    yield DefaultMaterialLocalizations.delegate;
547
    yield DefaultCupertinoLocalizations.delegate;
548 549
  }

550
  @override
551
  Widget build(BuildContext context) {
552 553 554 555
    Widget result = WidgetsApp(
      key: GlobalObjectKey(this),
      navigatorKey: widget.navigatorKey,
      navigatorObservers: _navigatorObservers,
556 557 558
      pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
        return MaterialPageRoute<T>(settings: settings, builder: builder);
      },
559 560 561 562 563 564 565
      home: widget.home,
      routes: widget.routes,
      initialRoute: widget.initialRoute,
      onGenerateRoute: widget.onGenerateRoute,
      onUnknownRoute: widget.onUnknownRoute,
      builder: (BuildContext context, Widget child) {
        // Use a light theme, dark theme, or fallback theme.
566
        final ThemeMode mode = widget.themeMode ?? ThemeMode.system;
567
        ThemeData theme;
568 569 570 571 572 573
        if (widget.darkTheme != null) {
          final ui.Brightness platformBrightness = MediaQuery.platformBrightnessOf(context);
          if (mode == ThemeMode.dark ||
              (mode == ThemeMode.system && platformBrightness == ui.Brightness.dark)) {
            theme = widget.darkTheme;
          }
574
        }
575
        theme ??= widget.theme ?? ThemeData.fallback();
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

        return AnimatedTheme(
          data: theme,
          isMaterialAppTheme: true,
          child: widget.builder != null
              ? Builder(
                  builder: (BuildContext context) {
                    // Why are we surrounding a builder with a builder?
                    //
                    // The widget.builder may contain code that invokes
                    // Theme.of(), which should return the theme we selected
                    // above in AnimatedTheme. However, if we invoke
                    // widget.builder() directly as the child of AnimatedTheme
                    // then there is no Context separating them, and the
                    // widget.builder() will not find the theme. Therefore, we
                    // surround widget.builder with yet another builder so that
                    // a context separates them and Theme.of() correctly
                    // resolves to the theme we passed to AnimatedTheme.
                    return widget.builder(context, child);
                  },
                )
              : child,
        );
      },
      title: widget.title,
      onGenerateTitle: widget.onGenerateTitle,
      textStyle: _errorTextStyle,
      // The color property is always pulled from the light theme, even if dark
      // mode is activated. This was done to simplify the technical details
      // of switching themes and it was deemed acceptable because this color
      // property is only used on old Android OSes to color the app bar in
      // Android's switcher UI.
      //
      // blue is the primary color of the default theme
      color: widget.color ?? widget.theme?.primaryColor ?? Colors.blue,
      locale: widget.locale,
      localizationsDelegates: _localizationsDelegates,
      localeResolutionCallback: widget.localeResolutionCallback,
      localeListResolutionCallback: widget.localeListResolutionCallback,
      supportedLocales: widget.supportedLocales,
      showPerformanceOverlay: widget.showPerformanceOverlay,
      checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
      checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
      showSemanticsDebugger: widget.showSemanticsDebugger,
      debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,
      inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) {
        return FloatingActionButton(
          child: const Icon(Icons.search),
          onPressed: onPressed,
          mini: true,
        );
      },
628
    );
629

Ian Hickson's avatar
Ian Hickson committed
630
    assert(() {
631
      if (widget.debugShowMaterialGrid) {
632
        result = GridPaper(
Ian Hickson's avatar
Ian Hickson committed
633 634 635
          color: const Color(0xE0F9BBE0),
          interval: 8.0,
          divisions: 2,
636 637
          subdivisions: 1,
          child: result,
Ian Hickson's avatar
Ian Hickson committed
638 639 640
        );
      }
      return true;
641
    }());
642

643 644
    return ScrollConfiguration(
      behavior: _MaterialScrollBehavior(),
645
      child: result,
646
    );
647
  }
648
}