app.dart 21.8 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
xster's avatar
xster committed
2 3 4 5 6 7 8 9
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/widgets.dart';

import 'button.dart';
import 'colors.dart';
import 'icons.dart';
10
import 'interface_level.dart';
11
import 'localizations.dart';
12
import 'route.dart';
13
import 'scrollbar.dart';
xster's avatar
xster committed
14
import 'theme.dart';
xster's avatar
xster committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

/// An application that uses Cupertino design.
///
/// A convenience widget that wraps a number of widgets that are commonly
/// required for an iOS-design targeting application. It builds upon a
/// [WidgetsApp] by iOS specific defaulting such as fonts and scrolling
/// physics.
///
/// The [CupertinoApp] 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.
///
/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
/// and [builder] is not null, then no [Navigator] is created.
///
38 39 40
/// This widget also configures the observer of the top-level [Navigator] (if
/// any) to perform [Hero] animations.
///
41 42 43 44 45
/// The [CupertinoApp] widget isn't a required ancestor for other Cupertino
/// widgets, but many Cupertino widgets could depend on the [CupertinoTheme]
/// widget, which the [CupertinoApp] composes. If you use Material widgets, a
/// [MaterialApp] also creates the needed dependencies for Cupertino widgets.
///
46 47 48 49 50 51
/// {@template flutter.cupertino.CupertinoApp.defaultSelectionStyle}
/// The [CupertinoApp] automatically creates a [DefaultSelectionStyle] with
/// selectionColor sets to [CupertinoThemeData.primaryColor] with 0.2 opacity
/// and cursorColor sets to [CupertinoThemeData.primaryColor].
/// {@endtemplate}
///
Sigurd Meldgaard's avatar
Sigurd Meldgaard committed
52
/// Use this widget with caution on Android since it may produce behaviors
xster's avatar
xster committed
53 54 55 56 57 58 59
/// Android users are not expecting such as:
///
///  * Pages will be dismissible via a back swipe.
///  * Scrolling past extremities will trigger iOS-style spring overscrolls.
///  * The San Francisco font family is unavailable on Android and can result
///    in undefined font behavior.
///
60 61 62 63 64 65 66
/// {@tool snippet}
/// This example shows how to create a [CupertinoApp] that disables the "debug"
/// banner with a [home] route that will be displayed when the app is launched.
///
/// ![The CupertinoApp displays a CupertinoPageScaffold](https://flutter.github.io/assets-for-api-docs/assets/cupertino/basic_cupertino_app.png)
///
/// ```dart
67
/// const CupertinoApp(
68 69
///   home: CupertinoPageScaffold(
///     navigationBar: CupertinoNavigationBar(
70
///       middle: Text('Home'),
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
///     ),
///     child: Center(child: Icon(CupertinoIcons.share)),
///   ),
///   debugShowCheckedModeBanner: false,
/// )
/// ```
/// {@end-tool}
///
/// {@tool snippet}
/// This example shows how to create a [CupertinoApp] that uses the [routes]
/// `Map` to define the "home" route and an "about" route.
///
/// ```dart
/// CupertinoApp(
///   routes: <String, WidgetBuilder>{
///     '/': (BuildContext context) {
87
///       return const CupertinoPageScaffold(
88
///         navigationBar: CupertinoNavigationBar(
89
///           middle: Text('Home Route'),
90 91 92 93 94
///         ),
///         child: Center(child: Icon(CupertinoIcons.share)),
///       );
///     },
///     '/about': (BuildContext context) {
95
///       return const CupertinoPageScaffold(
96
///         navigationBar: CupertinoNavigationBar(
97
///           middle: Text('About Route'),
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
///         ),
///         child: Center(child: Icon(CupertinoIcons.share)),
///       );
///     }
///   },
/// )
/// ```
/// {@end-tool}
///
/// {@tool snippet}
/// This example shows how to create a [CupertinoApp] that defines a [theme] that
/// will be used for Cupertino widgets in the app.
///
/// ![The CupertinoApp displays a CupertinoPageScaffold with orange-colored icons](https://flutter.github.io/assets-for-api-docs/assets/cupertino/theme_cupertino_app.png)
///
/// ```dart
114
/// const CupertinoApp(
115 116 117 118 119 120
///   theme: CupertinoThemeData(
///     brightness: Brightness.dark,
///     primaryColor: CupertinoColors.systemOrange,
///   ),
///   home: CupertinoPageScaffold(
///     navigationBar: CupertinoNavigationBar(
121
///       middle: Text('CupertinoApp Theme'),
122 123 124 125 126 127 128
///     ),
///     child: Center(child: Icon(CupertinoIcons.share)),
///   ),
/// )
/// ```
/// {@end-tool}
///
xster's avatar
xster committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/// See also:
///
///  * [CupertinoPageScaffold], which provides a standard page layout default
///    with nav bars.
///  * [Navigator], which is used to manage the app's stack of pages.
///  * [CupertinoPageRoute], which defines an app page that transitions in an
///    iOS-specific way.
///  * [WidgetsApp], which defines the basic app elements but does not depend
///    on the Cupertino library.
class CupertinoApp extends StatefulWidget {
  /// Creates a CupertinoApp.
  ///
  /// 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
  /// [Navigator.defaultRouteName] (`/`), since that is the route used when the
  /// application is launched with an intent that specifies an otherwise
  /// unsupported route.
  ///
  /// This class creates an instance of [WidgetsApp].
  ///
  /// The boolean arguments, [routes], and [navigatorObservers], must not be null.
150
  const CupertinoApp({
151
    super.key,
152
    this.navigatorKey,
xster's avatar
xster committed
153
    this.home,
xster's avatar
xster committed
154
    this.theme,
155
    Map<String, Widget Function(BuildContext)> this.routes = const <String, WidgetBuilder>{},
xster's avatar
xster committed
156 157
    this.initialRoute,
    this.onGenerateRoute,
158
    this.onGenerateInitialRoutes,
xster's avatar
xster committed
159
    this.onUnknownRoute,
160
    List<NavigatorObserver> this.navigatorObservers = const <NavigatorObserver>[],
xster's avatar
xster committed
161 162 163 164 165 166
    this.builder,
    this.title = '',
    this.onGenerateTitle,
    this.color,
    this.locale,
    this.localizationsDelegates,
167
    this.localeListResolutionCallback,
xster's avatar
xster committed
168
    this.localeResolutionCallback,
169
    this.supportedLocales = const <Locale>[Locale('en', 'US')],
xster's avatar
xster committed
170 171 172 173 174
    this.showPerformanceOverlay = false,
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,
    this.debugShowCheckedModeBanner = true,
175 176
    this.shortcuts,
    this.actions,
177
    this.restorationScopeId,
178
    this.scrollBehavior,
179
    this.useInheritedMediaQuery = false,
xster's avatar
xster committed
180 181 182 183 184 185 186 187
  }) : assert(routes != null),
       assert(navigatorObservers != null),
       assert(title != null),
       assert(showPerformanceOverlay != null),
       assert(checkerboardRasterCacheImages != null),
       assert(checkerboardOffscreenLayers != null),
       assert(showSemanticsDebugger != null),
       assert(debugShowCheckedModeBanner != null),
188 189 190
       routeInformationProvider = null,
       routeInformationParser = null,
       routerDelegate = null,
191
       backButtonDispatcher = null;
192 193 194

  /// Creates a [CupertinoApp] that uses the [Router] instead of a [Navigator].
  const CupertinoApp.router({
195
    super.key,
196
    this.routeInformationProvider,
197 198
    required RouteInformationParser<Object> this.routeInformationParser,
    required RouterDelegate<Object> this.routerDelegate,
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    this.backButtonDispatcher,
    this.theme,
    this.builder,
    this.title = '',
    this.onGenerateTitle,
    this.color,
    this.locale,
    this.localizationsDelegates,
    this.localeListResolutionCallback,
    this.localeResolutionCallback,
    this.supportedLocales = const <Locale>[Locale('en', 'US')],
    this.showPerformanceOverlay = false,
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,
    this.debugShowCheckedModeBanner = true,
    this.shortcuts,
    this.actions,
217
    this.restorationScopeId,
218
    this.scrollBehavior,
219
    this.useInheritedMediaQuery = false,
220 221 222 223 224 225 226 227 228 229 230 231 232
  }) : assert(title != null),
       assert(showPerformanceOverlay != null),
       assert(checkerboardRasterCacheImages != null),
       assert(checkerboardOffscreenLayers != null),
       assert(showSemanticsDebugger != null),
       assert(debugShowCheckedModeBanner != null),
       navigatorObservers = null,
       navigatorKey = null,
       onGenerateRoute = null,
       home = null,
       onGenerateInitialRoutes = null,
       onUnknownRoute = null,
       routes = null,
233
       initialRoute = null;
xster's avatar
xster committed
234

235
  /// {@macro flutter.widgets.widgetsApp.navigatorKey}
236
  final GlobalKey<NavigatorState>? navigatorKey;
237 238

  /// {@macro flutter.widgets.widgetsApp.home}
239
  final Widget? home;
xster's avatar
xster committed
240

xster's avatar
xster committed
241 242 243 244
  /// The top-level [CupertinoTheme] styling.
  ///
  /// A null [theme] or unspecified [theme] attributes will default to iOS
  /// system values.
245
  final CupertinoThemeData? theme;
xster's avatar
xster committed
246

xster's avatar
xster committed
247 248 249 250
  /// 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
Janice Collins's avatar
Janice Collins committed
251 252 253
  /// [widgets.WidgetBuilder] is used to construct a [CupertinoPageRoute] that
  /// performs an appropriate transition, including [Hero] animations, to the
  /// new route.
xster's avatar
xster committed
254
  ///
255
  /// {@macro flutter.widgets.widgetsApp.routes}
256
  final Map<String, WidgetBuilder>? routes;
xster's avatar
xster committed
257 258

  /// {@macro flutter.widgets.widgetsApp.initialRoute}
259
  final String? initialRoute;
xster's avatar
xster committed
260 261

  /// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
262
  final RouteFactory? onGenerateRoute;
xster's avatar
xster committed
263

264
  /// {@macro flutter.widgets.widgetsApp.onGenerateInitialRoutes}
265
  final InitialRouteListFactory? onGenerateInitialRoutes;
266

xster's avatar
xster committed
267
  /// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
268
  final RouteFactory? onUnknownRoute;
xster's avatar
xster committed
269 270

  /// {@macro flutter.widgets.widgetsApp.navigatorObservers}
271
  final List<NavigatorObserver>? navigatorObservers;
xster's avatar
xster committed
272

273
  /// {@macro flutter.widgets.widgetsApp.routeInformationProvider}
274
  final RouteInformationProvider? routeInformationProvider;
275 276

  /// {@macro flutter.widgets.widgetsApp.routeInformationParser}
277
  final RouteInformationParser<Object>? routeInformationParser;
278 279

  /// {@macro flutter.widgets.widgetsApp.routerDelegate}
280
  final RouterDelegate<Object>? routerDelegate;
281 282

  /// {@macro flutter.widgets.widgetsApp.backButtonDispatcher}
283
  final BackButtonDispatcher? backButtonDispatcher;
284

xster's avatar
xster committed
285
  /// {@macro flutter.widgets.widgetsApp.builder}
286
  final TransitionBuilder? builder;
xster's avatar
xster committed
287 288 289 290 291 292 293 294 295

  /// {@macro flutter.widgets.widgetsApp.title}
  ///
  /// This value is passed unmodified to [WidgetsApp.title].
  final String title;

  /// {@macro flutter.widgets.widgetsApp.onGenerateTitle}
  ///
  /// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
296
  final GenerateAppTitle? onGenerateTitle;
xster's avatar
xster committed
297 298

  /// {@macro flutter.widgets.widgetsApp.color}
299
  final Color? color;
xster's avatar
xster committed
300 301

  /// {@macro flutter.widgets.widgetsApp.locale}
302
  final Locale? locale;
xster's avatar
xster committed
303 304

  /// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
305
  final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
xster's avatar
xster committed
306

307 308 309
  /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
  ///
  /// This callback is passed along to the [WidgetsApp] built by this widget.
310
  final LocaleListResolutionCallback? localeListResolutionCallback;
311

312
  /// {@macro flutter.widgets.LocaleResolutionCallback}
xster's avatar
xster committed
313 314
  ///
  /// This callback is passed along to the [WidgetsApp] built by this widget.
315
  final LocaleResolutionCallback? localeResolutionCallback;
xster's avatar
xster committed
316 317 318 319 320 321 322 323 324 325

  /// {@macro flutter.widgets.widgetsApp.supportedLocales}
  ///
  /// It is passed along unmodified to the [WidgetsApp] built by this widget.
  final Iterable<Locale> supportedLocales;

  /// Turns on a performance overlay.
  ///
  /// See also:
  ///
326
  ///  * <https://flutter.dev/debugging/#performance-overlay>
xster's avatar
xster committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
  final bool showPerformanceOverlay;

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

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

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

  /// {@macro flutter.widgets.widgetsApp.debugShowCheckedModeBanner}
  final bool debugShowCheckedModeBanner;

342
  /// {@macro flutter.widgets.widgetsApp.shortcuts}
343
  /// {@tool snippet}
344 345 346 347 348 349 350 351 352 353 354
  /// This example shows how to add a single shortcut for
  /// [LogicalKeyboardKey.select] to the default shortcuts without needing to
  /// add your own [Shortcuts] widget.
  ///
  /// Alternatively, you could insert a [Shortcuts] widget with just the mapping
  /// you want to add between the [WidgetsApp] and its child and get the same
  /// effect.
  ///
  /// ```dart
  /// Widget build(BuildContext context) {
  ///   return WidgetsApp(
355
  ///     shortcuts: <ShortcutActivator, Intent>{
356
  ///       ... WidgetsApp.defaultShortcuts,
357
  ///       const SingleActivator(LogicalKeyboardKey.select): const ActivateIntent(),
358 359
  ///     },
  ///     color: const Color(0xFFFF0000),
360
  ///     builder: (BuildContext context, Widget? child) {
361 362 363 364 365 366 367
  ///       return const Placeholder();
  ///     },
  ///   );
  /// }
  /// ```
  /// {@end-tool}
  /// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso}
368
  final Map<ShortcutActivator, Intent>? shortcuts;
369 370

  /// {@macro flutter.widgets.widgetsApp.actions}
371
  /// {@tool snippet}
372 373 374 375 376 377 378 379 380 381 382
  /// This example shows how to add a single action handling an
  /// [ActivateAction] to the default actions without needing to
  /// add your own [Actions] widget.
  ///
  /// Alternatively, you could insert a [Actions] widget with just the mapping
  /// you want to add between the [WidgetsApp] and its child and get the same
  /// effect.
  ///
  /// ```dart
  /// Widget build(BuildContext context) {
  ///   return WidgetsApp(
383
  ///     actions: <Type, Action<Intent>>{
384
  ///       ... WidgetsApp.defaultActions,
385
  ///       ActivateAction: CallbackAction<Intent>(
386
  ///         onInvoke: (Intent intent) {
387
  ///           // Do something here...
388
  ///           return null;
389 390 391 392
  ///         },
  ///       ),
  ///     },
  ///     color: const Color(0xFFFF0000),
393
  ///     builder: (BuildContext context, Widget? child) {
394 395 396 397 398 399 400
  ///       return const Placeholder();
  ///     },
  ///   );
  /// }
  /// ```
  /// {@end-tool}
  /// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
401
  final Map<Type, Action<Intent>>? actions;
402

403 404 405
  /// {@macro flutter.widgets.widgetsApp.restorationScopeId}
  final String? restorationScopeId;

406 407 408 409 410 411 412 413 414 415
  /// {@macro flutter.material.materialApp.scrollBehavior}
  ///
  /// When null, defaults to [CupertinoScrollBehavior].
  ///
  /// See also:
  ///
  ///  * [ScrollConfiguration], which controls how [Scrollable] widgets behave
  ///    in a subtree.
  final ScrollBehavior? scrollBehavior;

416 417 418
  /// {@macro flutter.widgets.widgetsApp.useInheritedMediaQuery}
  final bool useInheritedMediaQuery;

xster's avatar
xster committed
419
  @override
420
  State<CupertinoApp> createState() => _CupertinoAppState();
421 422 423 424 425 426

  /// The [HeroController] used for Cupertino page transitions.
  ///
  /// Used by [CupertinoTabView] and [CupertinoApp].
  static HeroController createCupertinoHeroController() =>
      HeroController(); // Linear tweening.
xster's avatar
xster committed
427 428
}

429 430 431 432 433 434 435
/// Describes how [Scrollable] widgets behave for [CupertinoApp]s.
///
/// {@macro flutter.widgets.scrollBehavior}
///
/// Setting a [CupertinoScrollBehavior] will result in descendant [Scrollable] widgets
/// using [BouncingScrollPhysics] by default. No [GlowingOverscrollIndicator] is
/// applied when using a [CupertinoScrollBehavior] either, regardless of platform.
436
/// When executing on desktop platforms, a [CupertinoScrollbar] is applied to the child.
437 438 439 440 441
///
/// See also:
///
///  * [ScrollBehavior], the default scrolling behavior extended by this class.
class CupertinoScrollBehavior extends ScrollBehavior {
442 443 444 445 446 447 448 449 450 451 452 453 454 455
  /// Creates a CupertinoScrollBehavior that uses [BouncingScrollPhysics] and
  /// adds [CupertinoScrollbar]s on desktop platforms.
  const CupertinoScrollBehavior();

  @override
  Widget buildScrollbar(BuildContext context , Widget child, ScrollableDetails details) {
    // When modifying this function, consider modifying the implementation in
    // the base class as well.
    switch (getPlatform(context)) {
      case TargetPlatform.linux:
      case TargetPlatform.macOS:
      case TargetPlatform.windows:
        return CupertinoScrollbar(
          controller: details.controller,
456
          child: child,
457 458 459 460 461 462 463 464
        );
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
      case TargetPlatform.iOS:
        return child;
    }
  }

xster's avatar
xster committed
465
  @override
466 467 468 469
  Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
    // No overscroll indicator.
    // When modifying this function, consider modifying the implementation in
    // the base class as well.
xster's avatar
xster committed
470 471 472 473 474 475 476 477 478 479
    return child;
  }

  @override
  ScrollPhysics getScrollPhysics(BuildContext context) {
    return const BouncingScrollPhysics();
  }
}

class _CupertinoAppState extends State<CupertinoApp> {
480
  late HeroController _heroController;
481
  bool get _usesRouter => widget.routerDelegate != null;
482

xster's avatar
xster committed
483 484 485
  @override
  void initState() {
    super.initState();
486
    _heroController = CupertinoApp.createCupertinoHeroController();
xster's avatar
xster committed
487 488
  }

489 490 491 492 493
  // Combine the default localization for Cupertino 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
  // _CupertinoLocalizationsDelegate.
494 495 496 497 498 499
  Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
    return <LocalizationsDelegate<dynamic>>[
      if (widget.localizationsDelegates != null)
        ...widget.localizationsDelegates!,
      DefaultCupertinoLocalizations.delegate,
    ];
500 501
  }

502 503
  Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) {
    return CupertinoButton.filled(
504 505
      padding: EdgeInsets.zero,
      onPressed: onPressed,
506 507 508 509 510 511 512 513 514 515
      child: const Icon(
        CupertinoIcons.search,
        size: 28.0,
        color: CupertinoColors.white,
      ),
    );
  }

  WidgetsApp _buildWidgetApp(BuildContext context) {
    final CupertinoThemeData effectiveThemeData = CupertinoTheme.of(context);
516
    final Color color = CupertinoDynamicColor.resolve(widget.color ?? effectiveThemeData.primaryColor, context);
517 518 519 520 521

    if (_usesRouter) {
      return WidgetsApp.router(
        key: GlobalObjectKey(this),
        routeInformationProvider: widget.routeInformationProvider,
522 523
        routeInformationParser: widget.routeInformationParser!,
        routerDelegate: widget.routerDelegate!,
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
        backButtonDispatcher: widget.backButtonDispatcher,
        builder: widget.builder,
        title: widget.title,
        onGenerateTitle: widget.onGenerateTitle,
        textStyle: effectiveThemeData.textTheme.textStyle,
        color: color,
        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: _inspectorSelectButtonBuilder,
        shortcuts: widget.shortcuts,
        actions: widget.actions,
543
        restorationScopeId: widget.restorationScopeId,
544
        useInheritedMediaQuery: widget.useInheritedMediaQuery,
545 546 547 548 549
      );
    }
    return WidgetsApp(
      key: GlobalObjectKey(this),
      navigatorKey: widget.navigatorKey,
550
      navigatorObservers: widget.navigatorObservers!,
551 552 553 554
      pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
        return CupertinoPageRoute<T>(settings: settings, builder: builder);
      },
      home: widget.home,
555
      routes: widget.routes!,
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
      initialRoute: widget.initialRoute,
      onGenerateRoute: widget.onGenerateRoute,
      onGenerateInitialRoutes: widget.onGenerateInitialRoutes,
      onUnknownRoute: widget.onUnknownRoute,
      builder: widget.builder,
      title: widget.title,
      onGenerateTitle: widget.onGenerateTitle,
      textStyle: effectiveThemeData.textTheme.textStyle,
      color: color,
      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: _inspectorSelectButtonBuilder,
      shortcuts: widget.shortcuts,
      actions: widget.actions,
578
      restorationScopeId: widget.restorationScopeId,
579
      useInheritedMediaQuery: widget.useInheritedMediaQuery,
580 581 582
    );
  }

xster's avatar
xster committed
583 584
  @override
  Widget build(BuildContext context) {
xster's avatar
xster committed
585 586
    final CupertinoThemeData effectiveThemeData = widget.theme ?? const CupertinoThemeData();

587
    return ScrollConfiguration(
588
      behavior: widget.scrollBehavior ?? const CupertinoScrollBehavior(),
589 590 591 592
      child: CupertinoUserInterfaceLevel(
        data: CupertinoUserInterfaceLevelData.base,
        child: CupertinoTheme(
          data: effectiveThemeData,
593 594 595 596 597 598 599 600
          child: DefaultSelectionStyle(
            selectionColor: effectiveThemeData.primaryColor.withOpacity(0.2),
            cursorColor: effectiveThemeData.primaryColor,
            child: HeroControllerScope(
              controller: _heroController,
              child: Builder(
                builder: _buildWidgetApp,
              ),
601
            ),
602
          ),
xster's avatar
xster committed
603
        ),
xster's avatar
xster committed
604 605 606 607
      ),
    );
  }
}