From 06f63aaa5a98c0df72e99b860f9e1eb9593bb07b Mon Sep 17 00:00:00 2001 From: xster Date: Wed, 4 Jul 2018 16:55:22 -0700 Subject: [PATCH] Create a CupertinoApp (#18469) --- bin/internal/goldens.version | 2 +- packages/flutter/lib/cupertino.dart | 1 + packages/flutter/lib/src/cupertino/app.dart | 405 ++++++++++++++ packages/flutter/lib/src/material/app.dart | 143 +---- packages/flutter/lib/src/widgets/app.dart | 43 +- .../flutter/test/cupertino/nav_bar_test.dart | 526 +++++++----------- .../flutter/test/cupertino/page_test.dart | 24 +- .../flutter/test/cupertino/scaffold_test.dart | 302 +++++----- .../test/cupertino/tab_scaffold_test.dart | 190 +++---- packages/flutter/test/cupertino/tab_test.dart | 124 ++--- 10 files changed, 897 insertions(+), 863 deletions(-) create mode 100644 packages/flutter/lib/src/cupertino/app.dart diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version index 8fc4d8b7528..1bd60a1db47 100644 --- a/bin/internal/goldens.version +++ b/bin/internal/goldens.version @@ -1 +1 @@ -bc99a4bdea0533994b34bc49f160af75f4904952 +413041fd6bbedd1c8bc6ca0b6ea6ea948079b669 diff --git a/packages/flutter/lib/cupertino.dart b/packages/flutter/lib/cupertino.dart index 6003c732741..7f9c9788273 100644 --- a/packages/flutter/lib/cupertino.dart +++ b/packages/flutter/lib/cupertino.dart @@ -8,6 +8,7 @@ library cupertino; export 'src/cupertino/activity_indicator.dart'; +export 'src/cupertino/app.dart'; export 'src/cupertino/bottom_tab_bar.dart'; export 'src/cupertino/button.dart'; export 'src/cupertino/colors.dart'; diff --git a/packages/flutter/lib/src/cupertino/app.dart b/packages/flutter/lib/src/cupertino/app.dart new file mode 100644 index 00000000000..3140671aae5 --- /dev/null +++ b/packages/flutter/lib/src/cupertino/app.dart @@ -0,0 +1,405 @@ +// Copyright 2018 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. + +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +import 'button.dart'; +import 'colors.dart'; +import 'icons.dart'; +import 'tab_view.dart'; + +// Based on specs from https://developer.apple.com/design/resources/ for +// iOS 12. +const TextStyle _kDefaultTextStyle = const TextStyle( + fontFamily: '.SF Pro Text', + fontSize: 17.0, + letterSpacing: -0.38, + color: CupertinoColors.black, + decoration: TextDecoration.none, +); + +/// 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. +/// +/// Using this widget with caution on Android since it may produce behaviors +/// 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. +/// +/// 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. + CupertinoApp({ // can't be const because the asserts use methods on Map :-( + Key key, + this.home, + this.routes = const {}, + this.initialRoute, + this.onGenerateRoute, + this.onUnknownRoute, + this.navigatorObservers = const [], + this.builder, + this.title = '', + this.onGenerateTitle, + this.color, + this.locale, + this.localizationsDelegates, + this.localeResolutionCallback, + this.supportedLocales = const [const Locale('en', 'US')], + this.showPerformanceOverlay = false, + this.checkerboardRasterCacheImages = false, + this.checkerboardOffscreenLayers = false, + this.showSemanticsDebugger = false, + this.debugShowCheckedModeBanner = true, + }) : assert(routes != null), + assert(navigatorObservers != null), + assert( + home == null || + !routes.containsKey(Navigator.defaultRouteName), + 'If the home property is specified, the routes table ' + 'cannot include an entry for "/", since it would be redundant.' + ), + assert( + builder != null || + home != null || + routes.containsKey(Navigator.defaultRouteName) || + onGenerateRoute != null || + onUnknownRoute != null, + 'Either the home property must be specified, ' + 'or the routes table must include an entry for "/", ' + 'or there must be on onGenerateRoute callback specified, ' + 'or there must be an onUnknownRoute callback specified, ' + 'or the builder property must be specified, ' + 'because otherwise there is nothing to fall back on if the ' + 'app is started with an intent that specifies an unknown route.' + ), + assert( + (home != null || + routes.isNotEmpty || + onGenerateRoute != null || + onUnknownRoute != null) + || + (builder != null && + initialRoute == null && + navigatorObservers.isEmpty), + 'If no route is provided using ' + 'home, routes, onGenerateRoute, or onUnknownRoute, ' + 'a non-null callback for the builder property must be provided, ' + 'and the other navigator-related properties, ' + 'navigatorKey, initialRoute, and navigatorObservers, ' + 'must have their initial values ' + '(null, null, and the empty list, respectively).' + ), + assert(title != null), + assert(showPerformanceOverlay != null), + assert(checkerboardRasterCacheImages != null), + assert(checkerboardOffscreenLayers != null), + assert(showSemanticsDebugger != null), + assert(debugShowCheckedModeBanner != null), + super(key: key); + + /// The widget for the default route of the app ([Navigator.defaultRouteName], + /// which is `/`). + /// + /// This is the route that is displayed first when the application is started + /// normally, unless [initialRoute] is specified. It's also the route that's + /// displayed if the [initialRoute] can't be displayed. + /// + /// To be able to directly call [MediaQuery.of], [Navigator.of], etc, in the + /// code that sets the [home] argument in the constructor, you can use a + /// [Builder] widget to get a [BuildContext]. + /// + /// If [home] is specified, then [routes] must not include an entry for `/`, + /// as [home] takes its place. + /// + /// The [Navigator] is only built if routes are provided (either via [home], + /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, + /// [builder] must not be null. + /// + /// The difference between using [home] and using [builder] is that the [home] + /// subtree is inserted into the application below a [Navigator] (and thus + /// below an [Overlay], which [Navigator] uses). With [home], therefore, + /// dialog boxes will work automatically, the [routes] table will be used, and + /// APIs such as [Navigator.push] and [Navigator.pop] will work as expected. + /// In contrast, the widget returned from [builder] is inserted _above_ the + /// [CupertinoApp]'s [Navigator] (if any). + final Widget home; + + /// 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 [CupertinoPageRoute] that performs + /// an appropriate transition to the new route. + /// + /// If the app only has one page, then you can specify it using [home] instead. + /// + /// If [home] is specified, then it implies an entry in this table for the + /// [Navigator.defaultRouteName] route (`/`), and it is an error to + /// redundantly provide such a route in the [routes] table. + /// + /// If a route is requested that is not specified in this table (or by + /// [home]), then the [onGenerateRoute] callback is called to build the page + /// instead. + /// + /// The [Navigator] is only built if routes are provided (either via [home], + /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, + /// [builder] must not be null. + final Map routes; + + /// {@macro flutter.widgets.widgetsApp.initialRoute} + /// + /// The [Navigator] is only built if routes are provided (either via [home], + /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, + /// [initialRoute] must be null and [builder] must not be null. + /// + /// See also: + /// + /// * [Navigator.initialRoute], which is used to implement this property. + /// * [Navigator.push], for pushing additional routes. + /// * [Navigator.pop], for removing a route from the stack. + final String initialRoute; + + /// {@macro flutter.widgets.widgetsApp.onGenerateRoute} + /// + /// This is used if [routes] does not contain the requested route. + /// + /// The [Navigator] is only built if routes are provided (either via [home], + /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, + /// [builder] must not be null. + final RouteFactory onGenerateRoute; + + /// Called when [onGenerateRoute] fails to generate a route, except for the + /// [initialRoute]. + /// + /// {@macro flutter.widgets.widgetsApp.onUnknownRoute} + /// + /// The [Navigator] is only built if routes are provided (either via [home], + /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, + /// [builder] must not be null. + final RouteFactory onUnknownRoute; + + /// {@macro flutter.widgets.widgetsApp.navigatorObservers} + /// + /// The [Navigator] is only built if routes are provided (either via [home], + /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, + /// [navigatorObservers] must be the empty list and [builder] must not be null. + final List navigatorObservers; + + /// {@macro flutter.widgets.widgetsApp.builder} + /// + /// If no routes are provided using [home], [routes], [onGenerateRoute], or + /// [onUnknownRoute], the `child` will be null, and it is the responsibility + /// of the [builder] to provide the application's routing machinery. + /// + /// If routes _are_ provided using one or more of those properties, then + /// `child` is not null, and the returned value should include the `child` in + /// the widget subtree; if it does not, then the application will have no + /// navigator and the [navigatorKey], [home], [routes], [onGenerateRoute], + /// [onUnknownRoute], [initialRoute], and [navigatorObservers] properties will + /// have no effect. + /// + /// If [builder] is null, it is as if a builder was specified that returned + /// the `child` directly. If it is null, routes must be provided using one of + /// the other properties listed above. + /// + /// Unless a [Navigator] is provided, either implicitly from [builder] being + /// null, or by a [builder] including its `child` argument, or by a [builder] + /// explicitly providing a [Navigator] of its own, APIs such as + /// [Navigator.push] and [Navigator.pop], will not function. + final TransitionBuilder builder; + + /// {@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]. + final GenerateAppTitle onGenerateTitle; + + /// {@macro flutter.widgets.widgetsApp.color} + final Color color; + + /// {@macro flutter.widgets.widgetsApp.locale} + final Locale locale; + + /// {@macro flutter.widgets.widgetsApp.localizationsDelegates} + final Iterable> localizationsDelegates; + + /// {@macro flutter.widgets.widgetsApp.localeResolutionCallback} + /// + /// This callback is passed along to the [WidgetsApp] built by this widget. + final LocaleResolutionCallback localeResolutionCallback; + + /// {@macro flutter.widgets.widgetsApp.supportedLocales} + /// + /// It is passed along unmodified to the [WidgetsApp] built by this widget. + final Iterable supportedLocales; + + /// Turns on a performance overlay. + /// + /// See also: + /// + /// * + 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; + + @override + _CupertinoAppState createState() => new _CupertinoAppState(); +} + +class _AlwaysCupertinoScrollBehavior extends ScrollBehavior { + @override + Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) { + // Never build any overscroll glow indicators. + return child; + } + + @override + ScrollPhysics getScrollPhysics(BuildContext context) { + return const BouncingScrollPhysics(); + } +} + +class _CupertinoAppState extends State { + @override + void initState() { + super.initState(); + _updateNavigator(); + } + + @override + void didUpdateWidget(CupertinoApp oldWidget) { + super.didUpdateWidget(oldWidget); + _updateNavigator(); + } + + bool _haveNavigator; + void _updateNavigator() { + _haveNavigator = widget.home != null || + widget.routes.isNotEmpty || + widget.onGenerateRoute != null || + widget.onUnknownRoute != null; + } + + Widget defaultBuilder(BuildContext context, Widget child) { + // The `child` coming back out from WidgetsApp will always be null since + // we never passed in anything for it to create a Navigator inside + // WidgetsApp. + assert(child == null); + if (_haveNavigator) { + // Reuse CupertinoTabView which creates a routing Navigator for us. + final Widget navigator = new CupertinoTabView( + builder: widget.home != null + ? (BuildContext context) => widget.home + : null, + routes: widget.routes, + onGenerateRoute: widget.onGenerateRoute, + onUnknownRoute: widget.onUnknownRoute, + navigatorObservers: widget.navigatorObservers, + ); + if (widget.builder != null) { + return widget.builder(context, navigator); + } else { + return navigator; + } + } else { + // We asserted that child is null above. + return widget.builder(context, null); + } + } + + @override + Widget build(BuildContext context) { + return new ScrollConfiguration( + behavior: new _AlwaysCupertinoScrollBehavior(), + child: new WidgetsApp( + key: new GlobalObjectKey(this), + // We're passing in a builder and nothing else that the WidgetsApp uses + // to build its own Navigator because we're building a Navigator with + // routes in this class. + builder: defaultBuilder, + title: widget.title, + onGenerateTitle: widget.onGenerateTitle, + textStyle: _kDefaultTextStyle, + color: widget.color ?? CupertinoColors.activeBlue, + locale: widget.locale, + localizationsDelegates: widget.localizationsDelegates, + localeResolutionCallback: widget.localeResolutionCallback, + supportedLocales: widget.supportedLocales, + showPerformanceOverlay: widget.showPerformanceOverlay, + checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, + checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, + showSemanticsDebugger: widget.showSemanticsDebugger, + debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, + inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) { + return new CupertinoButton( + child: const Icon( + CupertinoIcons.search, + size: 28.0, + color: CupertinoColors.white, + ), + color: CupertinoColors.activeBlue, + padding: EdgeInsets.zero, + onPressed: onPressed, + ); + }, + ), + ); + } +} diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index 6a7079c9536..089d8c4a4d6 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -218,20 +218,7 @@ class MaterialApp extends StatefulWidget { /// [builder] must not be null. final Map routes; - /// The name of the first route to show, if a [Navigator] is built. - /// - /// Defaults to [Window.defaultRouteName], which may be overridden by the code - /// that launched the application. - /// - /// If the route contains slashes, then it is treated as a "deep link", and - /// before this route is pushed, the routes leading to this one are pushed - /// also. For example, if the route was `/a/b/c`, then the app would start - /// with the three routes `/a`, `/a/b`, and `/a/b/c` loaded, in that order. - /// - /// If any part of this process fails to generate routes, then the - /// [initialRoute] is ignored and [Navigator.defaultRouteName] is used instead - /// (`/`). This can happen if the app is started with an intent that specifies - /// a non-existent route. + /// {@macro flutter.widgets.widgetsApp.initialRoute} /// /// The [Navigator] is only built if routes are provided (either via [home], /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, @@ -244,19 +231,10 @@ class MaterialApp extends StatefulWidget { /// * [Navigator.pop], for removing a route from the stack. final String initialRoute; - /// The route generator callback used when the app is navigated to a - /// named route. + /// {@macro flutter.widgets.widgetsApp.onGenerateRoute} /// /// This is used if [routes] does not contain the requested route. /// - /// If this returns null when building the routes to handle the specified - /// [initialRoute], then all the routes are discarded and - /// [Navigator.defaultRouteName] is used instead (`/`). See [initialRoute]. - /// - /// During normal app operation, the [onGenerateRoute] callback will only be - /// applied to route names pushed by the application, and so should never - /// return null. - /// /// The [Navigator] is only built if routes are provided (either via [home], /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, /// [builder] must not be null. @@ -265,44 +243,21 @@ class MaterialApp extends StatefulWidget { /// Called when [onGenerateRoute] fails to generate a route, except for the /// [initialRoute]. /// - /// This callback is typically used for error handling. For example, this - /// callback might always generate a "not found" page that describes the route - /// that wasn't found. - /// - /// The default implementation pushes a route that displays an ugly error - /// message. + /// {@macro flutter.widgets.widgetsApp.onUnknownRoute} /// /// The [Navigator] is only built if routes are provided (either via [home], /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, /// [builder] must not be null. final RouteFactory onUnknownRoute; - /// The list of observers for the [Navigator] created for this app. - /// - /// This list must be replaced by a list of newly-created observers if the - /// [navigatorKey] is changed. + /// {@macro flutter.widgets.widgetsApp.navigatorObservers} /// /// The [Navigator] is only built if routes are provided (either via [home], /// [routes], [onGenerateRoute], or [onUnknownRoute]); if they are not, /// [navigatorObservers] must be the empty list and [builder] must not be null. final List navigatorObservers; - /// A builder for inserting widgets above the [Navigator] but below the other - /// widgets created by the [MaterialApp] widget, or for replacing the - /// [Navigator] entirely. - /// - /// For example, from the [BuildContext] passed to this method, the - /// [Directionality], [Localizations], [DefaultTextStyle], [MediaQuery], etc, - /// are all available. They can also be overridden in a way that impacts all - /// the routes in the [Navigator]. - /// - /// This is rarely useful, but can be used in applications that wish to - /// override those defaults, e.g. to force the application into right-to-left - /// mode despite being in English, or to override the [MediaQuery] metrics - /// (e.g. to leave a gap for advertisements shown by a plugin from OEM code). - /// - /// The [builder] callback is passed two arguments, the [BuildContext] (as - /// `context`) and a [Navigator] widget (as `child`). + /// {@macro flutter.widgets.widgetsApp.builder} /// /// If no routes are provided using [home], [routes], [onGenerateRoute], or /// [onUnknownRoute], the `child` will be null, and it is the responsibility @@ -325,34 +280,14 @@ class MaterialApp extends StatefulWidget { /// [showDialog] and [showMenu], widgets such as [Tooltip], [PopupMenuButton], /// or [Hero], and APIs such as [Navigator.push] and [Navigator.pop], will not /// function. - /// - /// For specifically overriding the [title] with a value based on the - /// [Localizations], consider [onGenerateTitle] instead. final TransitionBuilder builder; - /// A one-line description used by the device to identify the app for the user. - /// - /// On Android the titles appear above the task manager's app snapshots which are - /// displayed when the user presses the "recent apps" button. Similarly, on - /// iOS the titles appear in the App Switcher when the user double presses the - /// home button. - /// - /// To provide a localized title instead, use [onGenerateTitle]. + /// {@macro flutter.widgets.widgetsApp.title} /// /// This value is passed unmodified to [WidgetsApp.title]. final String title; - /// If non-null this callback is called to produce the app's - /// title string, otherwise [title] is used. - /// - /// The [onGenerateTitle] `context` parameter includes the [WidgetsApp]'s - /// [Localizations] widget so that this callback can be used to produce a - /// localized title. - /// - /// This callback function must not return null. - /// - /// The [onGenerateTitle] callback is called each time the [MaterialApp] - /// rebuilds. + /// {@macro flutter.widgets.widgetsApp.onGenerateTitle} /// /// This value is passed unmodified to [WidgetsApp.onGenerateTitle]. final GenerateAppTitle onGenerateTitle; @@ -360,22 +295,13 @@ class MaterialApp extends StatefulWidget { /// The colors to use for the application's widgets. final ThemeData theme; - /// The primary color to use for the application in the operating system - /// interface. - /// - /// For example, on Android this is the color used for the application in the - /// application switcher. + /// {@macro flutter.widgets.widgetsApp.color} final Color color; - /// The initial locale for this app's [Localizations] widget. - /// - /// If the `locale` is null the system's locale value is used. + /// {@macro flutter.widgets.widgetsApp.locale} final Locale locale; - /// The delegates for this app's [Localizations] widget. - /// - /// The delegates collectively define all of the localized resources - /// for this application's [Localizations] widget. + /// {@macro flutter.widgets.widgetsApp.localizationsDelegates} /// /// Delegates that produce [WidgetsLocalizations] and [MaterialLocalizations] /// are included automatically. Apps can provide their own versions of these @@ -433,43 +359,14 @@ class MaterialApp extends StatefulWidget { /// ``` final Iterable> localizationsDelegates; - /// This callback is responsible for choosing the app's locale - /// when the app is started, and when the user changes the - /// device's locale. - /// - /// The returned value becomes the locale of this app's [Localizations] - /// widget. The callback's `locale` parameter is the device's locale when - /// the app started, or the device locale the user selected after the app was - /// started. The callback's `supportedLocales` parameter is just the value - /// [supportedLocales]. - /// - /// An app could use this callback to substitute locales based on the app's - /// intended audience. If the device's OS provides a prioritized - /// list of locales, this callback could be used to defer to it. - /// - /// If the callback is null then the resolved locale is: - /// - The callback's `locale` parameter if it's equal to a supported locale. - /// - The first supported locale with the same [Locale.languageCode] as the - /// callback's `locale` parameter. - /// - The first supported locale. + /// {@macro flutter.widgets.widgetsApp.localeResolutionCallback} /// /// This callback is passed along to the [WidgetsApp] built by this widget. final LocaleResolutionCallback localeResolutionCallback; - /// The list of locales that this app has been localized for. + /// {@macro flutter.widgets.widgetsApp.supportedLocales} /// - /// By default only the American English locale is supported. Apps should - /// configure this list to match the locales they support. - /// - /// This list must not null. It's default value is just - /// `[const Locale('en', 'US')]`. It is passed along unmodified to the - /// [WidgetsApp] built by this widget. - /// - /// The order of the list matters. By default, if the device's locale doesn't - /// exactly match a locale in [supportedLocales] then the first locale in - /// [supportedLocales] with a matching [Locale.languageCode] is used. If that - /// fails then the first locale in [supportedLocales] is used. The default - /// locale resolution algorithm can be overridden with [localeResolutionCallback]. + /// It is passed along unmodified to the [WidgetsApp] built by this widget. /// /// The material widgets include translations for locales with the following /// language codes: @@ -510,19 +407,7 @@ class MaterialApp extends StatefulWidget { /// reported by the framework. final bool showSemanticsDebugger; - /// Turns on a little "DEBUG" banner in checked mode to indicate - /// that the app is in checked mode. This is on by default (in - /// checked mode), to turn it off, set the constructor argument to - /// false. In release mode this has no effect. - /// - /// To get this banner in your application if you're not using - /// WidgetsApp, include a [CheckedModeBanner] widget in your app. - /// - /// This banner is intended to deter people from complaining that your - /// app is slow when it's in checked mode. In checked mode, Flutter - /// enables a large number of expensive diagnostics to aid in - /// development, and so performance in checked mode is not - /// representative of what will happen in release mode. + /// {@macro flutter.widgets.widgetsApp.debugShowCheckedModeBanner} final bool debugShowCheckedModeBanner; /// Turns on a [GridPaper] overlay that paints a baseline grid diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart index 3611ccc8257..47a34e34489 100644 --- a/packages/flutter/lib/src/widgets/app.dart +++ b/packages/flutter/lib/src/widgets/app.dart @@ -123,6 +123,7 @@ class WidgetsApp extends StatefulWidget { /// null, [navigatorKey] must also be null. final GlobalKey navigatorKey; + /// {@template flutter.widgets.widgetsApp.onGenerateRoute} /// The route generator callback used when the app is navigated to a /// named route. /// @@ -133,6 +134,7 @@ class WidgetsApp extends StatefulWidget { /// During normal app operation, the [onGenerateRoute] callback will only be /// applied to route names pushed by the application, and so should never /// return null. + /// {@endtemplate} /// /// The [Navigator] is only built if [onGenerateRoute] is not null. If /// [onGenerateRoute] is null, the [builder] must be non-null. @@ -140,18 +142,21 @@ class WidgetsApp extends StatefulWidget { /// Called when [onGenerateRoute] fails to generate a route. /// + /// {@template flutter.widgets.widgetsApp.onUnknownRoute} /// This callback is typically used for error handling. For example, this /// callback might always generate a "not found" page that describes the route /// that wasn't found. /// /// Unknown routes can arise either from errors in the app or from external /// requests to push routes, such as from Android intents. + /// {@endtemplate} /// /// The [Navigator] is only built if [onGenerateRoute] is not null; if it is /// null, [onUnknownRoute] must also be null. final RouteFactory onUnknownRoute; - /// The name of the first route to show. + /// {@template flutter.widgets.widgetsApp.initialRoute} + /// The name of the first route to show, if a [Navigator] is built. /// /// Defaults to [Window.defaultRouteName], which may be overridden by the code /// that launched the application. @@ -165,6 +170,7 @@ class WidgetsApp extends StatefulWidget { /// [initialRoute] is ignored and [Navigator.defaultRouteName] is used instead /// (`/`). This can happen if the app is started with an intent that specifies /// a non-existent route. + /// {@endtemplate} /// /// The [Navigator] is only built if [onGenerateRoute] is not null; if it is /// null, [initialRoute] must also be null. @@ -176,16 +182,19 @@ class WidgetsApp extends StatefulWidget { /// * [Navigator.pop], for removing a route from the stack. final String initialRoute; + /// {@template flutter.widgets.widgetsApp.navigatorObservers} /// The list of observers for the [Navigator] created for this app. /// /// This list must be replaced by a list of newly-created observers if the /// [navigatorKey] is changed. + /// {@endtemplate} /// /// The [Navigator] is only built if [onGenerateRoute] is not null; if it is /// null, [navigatorObservers] must be left to its default value, the empty /// list. final List navigatorObservers; + /// {@template flutter.widgets.widgetsApp.builder} /// A builder for inserting widgets above the [Navigator] but below the other /// widgets created by the [WidgetsApp] widget, or for replacing the /// [Navigator] entirely. @@ -200,8 +209,12 @@ class WidgetsApp extends StatefulWidget { /// mode despite being in English, or to override the [MediaQuery] metrics /// (e.g. to leave a gap for advertisements shown by a plugin from OEM code). /// + /// For specifically overriding the [title] with a value based on the + /// [Localizations], consider [onGenerateTitle] instead. + /// /// The [builder] callback is passed two arguments, the [BuildContext] (as /// `context`) and a [Navigator] widget (as `child`). + /// {@endtemplate} /// /// If [onGenerateRoute] is null, the `child` will be null, and it is the /// responsibility of the [builder] to provide the application's routing @@ -216,11 +229,9 @@ class WidgetsApp extends StatefulWidget { /// If [builder] is null, it is as if a builder was specified that returned /// the `child` directly. At least one of either [onGenerateRoute] or /// [builder] must be non-null. - /// - /// For specifically overriding the [title] with a value based on the - /// [Localizations], consider [onGenerateTitle] instead. final TransitionBuilder builder; + /// {@template flutter.widgets.widgetsApp.title} /// A one-line description used by the device to identify the app for the user. /// /// On Android the titles appear above the task manager's app snapshots which are @@ -229,8 +240,10 @@ class WidgetsApp extends StatefulWidget { /// home button. /// /// To provide a localized title instead, use [onGenerateTitle]. + /// {@endtemplate} final String title; + /// {@template flutter.widgets.widgetsApp.onGenerateTitle} /// If non-null this callback function is called to produce the app's /// title string, otherwise [title] is used. /// @@ -242,29 +255,37 @@ class WidgetsApp extends StatefulWidget { /// /// The [onGenerateTitle] callback is called each time the [WidgetsApp] /// rebuilds. + /// {@endtemplate} final GenerateAppTitle onGenerateTitle; /// The default text style for [Text] in the application. final TextStyle textStyle; + /// {@template flutter.widgets.widgetsApp.color} /// The primary color to use for the application in the operating system /// interface. /// /// For example, on Android this is the color used for the application in the /// application switcher. + /// {@endtemplate} final Color color; + /// {@template flutter.widgets.widgetsApp.locale} /// The initial locale for this app's [Localizations] widget. /// /// If the 'locale' is null the system's locale value is used. + /// {@endtemplate} final Locale locale; + /// {@template flutter.widgets.widgetsApp.localizationsDelegates} /// The delegates for this app's [Localizations] widget. /// /// The delegates collectively define all of the localized resources /// for this application's [Localizations] widget. + /// {@endtemplate} final Iterable> localizationsDelegates; + /// {@template flutter.widgets.widgetsApp.localeResolutionCallback} /// This callback is responsible for choosing the app's locale /// when the app is started, and when the user changes the /// device's locale. @@ -281,6 +302,7 @@ class WidgetsApp extends StatefulWidget { /// - The first supported locale with the same [Locale.languageCode] as the /// callback's `locale` parameter. /// - The first locale in [supportedLocales]. + /// {@endtemplate} /// /// See also: /// @@ -288,6 +310,7 @@ class WidgetsApp extends StatefulWidget { /// [WidgetsApp] it creates. final LocaleResolutionCallback localeResolutionCallback; + /// {@template flutter.widgets.widgetsApp.supportedLocales} /// The list of locales that this app has been localized for. /// /// By default only the American English locale is supported. Apps should @@ -301,6 +324,7 @@ class WidgetsApp extends StatefulWidget { /// [supportedLocales] with a matching [Locale.languageCode] is used. If that /// fails then the first locale in [supportedLocales] is used. The default /// locale resolution algorithm can be overridden with [localeResolutionCallback]. + /// {@endtemplate} /// /// See also: /// @@ -315,7 +339,10 @@ class WidgetsApp extends StatefulWidget { final Iterable supportedLocales; /// Turns on a performance overlay. - /// https://flutter.io/debugging/#performanceoverlay + /// + /// See also: + /// + /// * final bool showPerformanceOverlay; /// Checkerboards raster cache images. @@ -347,7 +374,8 @@ class WidgetsApp extends StatefulWidget { /// material package. final InspectorSelectButtonBuilder inspectorSelectButtonBuilder; - /// Turns on a "DEBUG" little banner in checked mode to indicate + /// {@template flutter.widgets.widgetsApp.debugShowCheckedModeBanner} + /// Turns on a little "DEBUG" banner in checked mode to indicate /// that the app is in checked mode. This is on by default (in /// checked mode), to turn it off, set the constructor argument to /// false. In release mode this has no effect. @@ -355,11 +383,12 @@ class WidgetsApp extends StatefulWidget { /// To get this banner in your application if you're not using /// WidgetsApp, include a [CheckedModeBanner] widget in your app. /// - /// This banner is intended to avoid people complaining that your + /// This banner is intended to deter people from complaining that your /// app is slow when it's in checked mode. In checked mode, Flutter /// enables a large number of expensive diagnostics to aid in /// development, and so performance in checked mode is not /// representative of what will happen in release mode. + /// {@endtemplate} final bool debugShowCheckedModeBanner; /// If true, forces the performance overlay to be visible in all instances. diff --git a/packages/flutter/test/cupertino/nav_bar_test.dart b/packages/flutter/test/cupertino/nav_bar_test.dart index fdde8b701ee..1d4f4b39fc0 100644 --- a/packages/flutter/test/cupertino/nav_bar_test.dart +++ b/packages/flutter/test/cupertino/nav_bar_test.dart @@ -16,19 +16,11 @@ int count = 0; void main() { testWidgets('Middle still in center with asymmetrical actions', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - leading: const CupertinoButton(child: const Text('Something'), onPressed: null,), - middle: const Text('Title'), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + leading: const CupertinoButton(child: const Text('Something'), onPressed: null,), + middle: const Text('Title'), + ), ), ); @@ -38,19 +30,11 @@ void main() { testWidgets('Opaque background does not add blur effects', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - middle: const Text('Title'), - backgroundColor: const Color(0xFFE5E5E5), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + middle: const Text('Title'), + backgroundColor: const Color(0xFFE5E5E5), + ), ), ); expect(find.byType(BackdropFilter), findsNothing); @@ -58,18 +42,10 @@ void main() { testWidgets('Non-opaque background adds blur effects', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - middle: const Text('Title'), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + middle: const Text('Title'), + ), ), ); expect(find.byType(BackdropFilter), findsOneWidget); @@ -78,21 +54,13 @@ void main() { testWidgets('Verify styles of each slot', (WidgetTester tester) async { count = 0x000000; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - leading: const _ExpectStyles(color: const Color(0xFF001122), index: 0x000001), - middle: const _ExpectStyles(color: const Color(0xFF000000), letterSpacing: -0.08, index: 0x000100), - trailing: const _ExpectStyles(color: const Color(0xFF001122), index: 0x010000), - actionsForegroundColor: const Color(0xFF001122), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + leading: const _ExpectStyles(color: const Color(0xFF001122), index: 0x000001), + middle: const _ExpectStyles(color: const Color(0xFF000000), letterSpacing: -0.08, index: 0x000100), + trailing: const _ExpectStyles(color: const Color(0xFF001122), index: 0x010000), + actionsForegroundColor: const Color(0xFF001122), + ), ), ); expect(count, 0x010101); @@ -100,21 +68,13 @@ void main() { testWidgets('No slivers with no large titles', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: const Text('Title'), - ), - child: const Center(), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: const Text('Title'), + ), + child: const Center(), + ), ), ); @@ -128,43 +88,35 @@ void main() { final Key trailingKey = new GlobalKey(); final Key titleKey = new GlobalKey(); await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new MediaQuery( - data: const MediaQueryData( - padding: const EdgeInsets.only( - top: 10.0, - left: 20.0, - bottom: 30.0, - right: 40.0, + new CupertinoApp( + home: new MediaQuery( + data: const MediaQueryData( + padding: const EdgeInsets.only( + top: 10.0, + left: 20.0, + bottom: 30.0, + right: 40.0, + ), + ), + child: new CupertinoPageScaffold( + child: new CustomScrollView( + controller: scrollController, + slivers: [ + new CupertinoSliverNavigationBar( + leading: new Placeholder(key: leadingKey), + middle: new Placeholder(key: middleKey), + largeTitle: new Text('Large Title', key: titleKey), + trailing: new Placeholder(key: trailingKey), + ), + new SliverToBoxAdapter( + child: new Container( + height: 1200.0, ), ), - child: new CupertinoPageScaffold( - child: new CustomScrollView( - controller: scrollController, - slivers: [ - new CupertinoSliverNavigationBar( - leading: new Placeholder(key: leadingKey), - middle: new Placeholder(key: middleKey), - largeTitle: new Text('Large Title', key: titleKey), - trailing: new Placeholder(key: trailingKey), - ), - new SliverToBoxAdapter( - child: new Container( - height: 1200.0, - ), - ), - ], - ), - ), - ); - }, - ); - }, + ], + ), + ), + ), ), ); @@ -180,30 +132,22 @@ void main() { testWidgets('Large title nav bar scrolls', (WidgetTester tester) async { final ScrollController scrollController = new ScrollController(); await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - controller: scrollController, - slivers: [ - const CupertinoSliverNavigationBar( - largeTitle: const Text('Title'), - ), - new SliverToBoxAdapter( - child: new Container( - height: 1200.0, - ), - ), - ], + new CupertinoApp( + home: new CupertinoPageScaffold( + child: new CustomScrollView( + controller: scrollController, + slivers: [ + const CupertinoSliverNavigationBar( + largeTitle: const Text('Title'), + ), + new SliverToBoxAdapter( + child: new Container( + height: 1200.0, ), - ); - }, - ); - }, + ), + ], + ), + ), ), ); @@ -268,31 +212,23 @@ void main() { testWidgets('Small title can be overridden', (WidgetTester tester) async { final ScrollController scrollController = new ScrollController(); await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - controller: scrollController, - slivers: [ - const CupertinoSliverNavigationBar( - middle: const Text('Different title'), - largeTitle: const Text('Title'), - ), - new SliverToBoxAdapter( - child: new Container( - height: 1200.0, - ), - ), - ], + new CupertinoApp( + home: new CupertinoPageScaffold( + child: new CustomScrollView( + controller: scrollController, + slivers: [ + const CupertinoSliverNavigationBar( + middle: const Text('Different title'), + largeTitle: const Text('Title'), + ), + new SliverToBoxAdapter( + child: new Container( + height: 1200.0, ), - ); - }, - ); - }, + ), + ], + ), + ), ), ); @@ -339,18 +275,10 @@ void main() { testWidgets('Auto back/close button', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - middle: const Text('Home page'), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + middle: const Text('Home page'), + ), ), ); @@ -403,18 +331,10 @@ void main() { testWidgets('Border should be displayed by default', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - middle: const Text('Title'), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + middle: const Text('Title'), + ), ), ); @@ -433,24 +353,16 @@ void main() { testWidgets('Overrides border color', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - middle: const Text('Title'), - border: const Border( - bottom: const BorderSide( - color: const Color(0xFFAABBCC), - width: 0.0, - ), - ), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + middle: const Text('Title'), + border: const Border( + bottom: const BorderSide( + color: const Color(0xFFAABBCC), + width: 0.0, + ), + ), + ), ), ); @@ -470,19 +382,11 @@ void main() { testWidgets('Border should not be displayed when null', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoNavigationBar( - middle: const Text('Title'), - border: null, - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoNavigationBar( + middle: const Text('Title'), + border: null, + ), ), ); @@ -500,24 +404,16 @@ void main() { 'Border is displayed by default in sliver nav bar', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - slivers: const [ - const CupertinoSliverNavigationBar( - largeTitle: const Text('Large Title'), - ), - ], - ), - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoPageScaffold( + child: new CustomScrollView( + slivers: const [ + const CupertinoSliverNavigationBar( + largeTitle: const Text('Large Title'), + ), + ], + ), + ), ), ); @@ -538,25 +434,17 @@ void main() { 'Border is not displayed when null in sliver nav bar', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - slivers: const [ - const CupertinoSliverNavigationBar( - largeTitle: const Text('Large Title'), - border: null, - ), - ], - ), - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoPageScaffold( + child: new CustomScrollView( + slivers: const [ + const CupertinoSliverNavigationBar( + largeTitle: const Text('Large Title'), + border: null, + ), + ], + ), + ), ), ); @@ -573,25 +461,17 @@ void main() { testWidgets('CupertinoSliverNavigationBar has semantics', (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); - await tester.pumpWidget(new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - slivers: const [ - const CupertinoSliverNavigationBar( - largeTitle: const Text('Large Title'), - border: null, - ), - ], - ), - ); - }, - ); - } + await tester.pumpWidget(new CupertinoApp( + home: new CupertinoPageScaffold( + child: new CustomScrollView( + slivers: const [ + const CupertinoSliverNavigationBar( + largeTitle: const Text('Large Title'), + border: null, + ), + ], + ), + ), )); expect(semantics.nodesWith( @@ -606,21 +486,13 @@ void main() { testWidgets('CupertinoNavigationBar has semantics', (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); - await tester.pumpWidget(new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: const Text('Fixed Title'), - ), - child: new Container(), - ); - }, - ); - } + await tester.pumpWidget(new CupertinoApp( + home: new CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: const Text('Fixed Title'), + ), + child: new Container(), + ), )); expect(semantics.nodesWith( @@ -636,30 +508,22 @@ void main() { 'Border can be overridden in sliver nav bar', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - slivers: const [ - const CupertinoSliverNavigationBar( - largeTitle: const Text('Large Title'), - border: const Border( - bottom: const BorderSide( - color: const Color(0xFFAABBCC), - width: 0.0, - ), - ), - ), - ], + new CupertinoApp( + home: new CupertinoPageScaffold( + child: new CustomScrollView( + slivers: const [ + const CupertinoSliverNavigationBar( + largeTitle: const Text('Large Title'), + border: const Border( + bottom: const BorderSide( + color: const Color(0xFFAABBCC), + width: 0.0, + ), ), - ); - }, - ); - }, + ), + ], + ), + ), ), ); @@ -684,23 +548,15 @@ void main() { 'Standard title golden', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const RepaintBoundary( - child: const CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: const Text('Bling bling'), - ), - child: const Center(), - ), - ); - }, - ); - }, + new CupertinoApp( + home: const RepaintBoundary( + child: const CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: const Text('Bling bling'), + ), + child: const Center(), + ), + ), ), ); @@ -718,29 +574,23 @@ void main() { 'Large title golden', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoPageScaffold( - child: new CustomScrollView( - slivers: [ - const CupertinoSliverNavigationBar( - largeTitle: const Text('Bling bling'), - ), - new SliverToBoxAdapter( - child: new Container( - height: 1200.0, - ), - ), - ], + new CupertinoApp( + home: new RepaintBoundary( + child: new CupertinoPageScaffold( + child: new CustomScrollView( + slivers: [ + const CupertinoSliverNavigationBar( + largeTitle: const Text('Bling bling'), ), - ); - }, - ); - }, + new SliverToBoxAdapter( + child: new Container( + height: 1200.0, + ), + ), + ], + ), + ), + ), ), ); diff --git a/packages/flutter/test/cupertino/page_test.dart b/packages/flutter/test/cupertino/page_test.dart index 9c5b64c795e..25c3b16472f 100644 --- a/packages/flutter/test/cupertino/page_test.dart +++ b/packages/flutter/test/cupertino/page_test.dart @@ -8,8 +8,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('test iOS page transition (LTR)', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), + new CupertinoApp( onGenerateRoute: (RouteSettings settings) { return new CupertinoPageRoute( settings: settings, @@ -76,11 +75,10 @@ void main() { testWidgets('test iOS page transition (RTL)', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( + new CupertinoApp( localizationsDelegates: const >[ const RtlOverrideWidgetsDelegate(), ], - color: const Color(0xFFFFFFFF), onGenerateRoute: (RouteSettings settings) { return new CupertinoPageRoute( settings: settings, @@ -148,16 +146,8 @@ void main() { testWidgets('test iOS fullscreen dialog transition', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const Center(child: const Text('Page 1')); - } - ); - }, + new CupertinoApp( + home: const Center(child: const Text('Page 1')), ), ); @@ -216,8 +206,7 @@ void main() { testWidgets('test only edge swipes work (LTR)', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), + new CupertinoApp( onGenerateRoute: (RouteSettings settings) { return new CupertinoPageRoute( settings: settings, @@ -278,11 +267,10 @@ void main() { testWidgets('test only edge swipes work (RTL)', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( + new CupertinoApp( localizationsDelegates: const >[ const RtlOverrideWidgetsDelegate(), ], - color: const Color(0xFFFFFFFF), onGenerateRoute: (RouteSettings settings) { return new CupertinoPageRoute( settings: settings, diff --git a/packages/flutter/test/cupertino/scaffold_test.dart b/packages/flutter/test/cupertino/scaffold_test.dart index 943f21c6684..9753cb534ca 100644 --- a/packages/flutter/test/cupertino/scaffold_test.dart +++ b/packages/flutter/test/cupertino/scaffold_test.dart @@ -11,22 +11,14 @@ import '../painting/mocks_for_image_cache.dart'; void main() { testWidgets('Contents are behind translucent bar', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoPageScaffold( - // Default nav bar is translucent. - navigationBar: const CupertinoNavigationBar( - middle: const Text('Title'), - ), - child: const Center(), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoPageScaffold( + // Default nav bar is translucent. + navigationBar: const CupertinoNavigationBar( + middle: const Text('Title'), + ), + child: const Center(), + ), ), ); @@ -37,41 +29,33 @@ void main() { const Center page1Center = const Center(); await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabScaffold( - tabBar: new CupertinoTabBar( - backgroundColor: CupertinoColors.white, - items: const [ - const BottomNavigationBarItem( - icon: const ImageIcon(const TestImageProvider(24, 24)), - title: const Text('Tab 1'), - ), - const BottomNavigationBarItem( - icon: const ImageIcon(const TestImageProvider(24, 24)), - title: const Text('Tab 2'), - ), - ], - ), - tabBuilder: (BuildContext context, int index) { - return index == 0 - ? const CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - backgroundColor: CupertinoColors.white, - middle: const Text('Title'), - ), - child: page1Center, - ) - : new Stack(); - } - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabScaffold( + tabBar: new CupertinoTabBar( + backgroundColor: CupertinoColors.white, + items: const [ + const BottomNavigationBarItem( + icon: const ImageIcon(const TestImageProvider(24, 24)), + title: const Text('Tab 1'), + ), + const BottomNavigationBarItem( + icon: const ImageIcon(const TestImageProvider(24, 24)), + title: const Text('Tab 2'), + ), + ], + ), + tabBuilder: (BuildContext context, int index) { + return index == 0 + ? const CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + backgroundColor: CupertinoColors.white, + middle: const Text('Title'), + ), + child: page1Center, + ) + : new Stack(); + }, + ), ), ); @@ -82,48 +66,40 @@ void main() { final Container content = new Container(height: 600.0, width: 600.0); await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new MediaQuery( - data: const MediaQueryData( - padding: const EdgeInsets.symmetric(vertical: 20.0), + new CupertinoApp( + home: new MediaQuery( + data: const MediaQueryData( + padding: const EdgeInsets.symmetric(vertical: 20.0), + ), + child: new CupertinoTabScaffold( + tabBar: new CupertinoTabBar( + items: const [ + const BottomNavigationBarItem( + icon: const ImageIcon(const TestImageProvider(24, 24)), + title: const Text('Tab 1'), ), - child: new CupertinoTabScaffold( - tabBar: new CupertinoTabBar( - items: const [ - const BottomNavigationBarItem( - icon: const ImageIcon(const TestImageProvider(24, 24)), - title: const Text('Tab 1'), - ), - const BottomNavigationBarItem( - icon: const ImageIcon(const TestImageProvider(24, 24)), - title: const Text('Tab 2'), - ), - ], - ), - tabBuilder: (BuildContext context, int index) { - return index == 0 - ? new CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( - middle: const Text('Title'), - ), - child: new ListView( - children: [ - content, - ], - ), - ) - : new Stack(); - } + const BottomNavigationBarItem( + icon: const ImageIcon(const TestImageProvider(24, 24)), + title: const Text('Tab 2'), ), - ); - }, - ); - }, + ], + ), + tabBuilder: (BuildContext context, int index) { + return index == 0 + ? new CupertinoPageScaffold( + navigationBar: const CupertinoNavigationBar( + middle: const Text('Title'), + ), + child: new ListView( + children: [ + content, + ], + ), + ) + : new Stack(); + } + ), + ), ), ); @@ -132,6 +108,8 @@ void main() { // Overscroll to the bottom. await tester.drag(find.byWidget(content), const Offset(0.0, -400.0)); + // Let it bounce back. + await tester.pump(); await tester.pump(const Duration(seconds: 1)); // List content automatically padded by tab bar and bottom media query padding. @@ -142,68 +120,60 @@ void main() { // A full on iOS information architecture app with 2 tabs, and 2 pages // in each with independent navigation states. await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabScaffold( - tabBar: new CupertinoTabBar( - items: const [ - const BottomNavigationBarItem( - icon: const ImageIcon(const TestImageProvider(24, 24)), - title: const Text('Tab 1'), - ), - const BottomNavigationBarItem( - icon: const ImageIcon(const TestImageProvider(24, 24)), - title: const Text('Tab 2'), - ), - ], - ), - tabBuilder: (BuildContext context, int index) { - // For 1-indexed readability. - ++index; - return new CupertinoTabView( - builder: (BuildContext context) { - return new CupertinoPageScaffold( - navigationBar: new CupertinoNavigationBar( - middle: new Text('Page 1 of tab $index'), - ), - child: new Center( - child: new CupertinoButton( - child: const Text('Next'), - onPressed: () { - Navigator.of(context).push( - new CupertinoPageRoute( - builder: (BuildContext context) { - return new CupertinoPageScaffold( - navigationBar: new CupertinoNavigationBar( - middle: new Text('Page 2 of tab $index'), - ), - child: new Center( - child: new CupertinoButton( - child: const Text('Back'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - ); - }, + new CupertinoApp( + home: new CupertinoTabScaffold( + tabBar: new CupertinoTabBar( + items: const [ + const BottomNavigationBarItem( + icon: const ImageIcon(const TestImageProvider(24, 24)), + title: const Text('Tab 1'), + ), + const BottomNavigationBarItem( + icon: const ImageIcon(const TestImageProvider(24, 24)), + title: const Text('Tab 2'), + ), + ], + ), + tabBuilder: (BuildContext context, int index) { + // For 1-indexed readability. + ++index; + return new CupertinoTabView( + builder: (BuildContext context) { + return new CupertinoPageScaffold( + navigationBar: new CupertinoNavigationBar( + middle: new Text('Page 1 of tab $index'), + ), + child: new Center( + child: new CupertinoButton( + child: const Text('Next'), + onPressed: () { + Navigator.of(context).push( + new CupertinoPageRoute( + builder: (BuildContext context) { + return new CupertinoPageScaffold( + navigationBar: new CupertinoNavigationBar( + middle: new Text('Page 2 of tab $index'), + ), + child: new Center( + child: new CupertinoButton( + child: const Text('Back'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), ), ); }, ), - ), - ); - }, - ); - }, - ); - }, - ); - }, + ); + }, + ), + ), + ); + }, + ); + }, + ), ), ); @@ -257,18 +227,10 @@ void main() { testWidgets('Decorated with white background by default', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoPageScaffold( - child: const Center(), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoPageScaffold( + child: const Center(), + ), ), ); @@ -281,19 +243,11 @@ void main() { testWidgets('Overrides background color', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const CupertinoPageScaffold( - child: const Center(), - backgroundColor: const Color(0xFF010203), - ); - }, - ); - }, + new CupertinoApp( + home: const CupertinoPageScaffold( + child: const Center(), + backgroundColor: const Color(0xFF010203), + ), ), ); diff --git a/packages/flutter/test/cupertino/tab_scaffold_test.dart b/packages/flutter/test/cupertino/tab_scaffold_test.dart index 67a8c8b7f7c..2586ce107c6 100644 --- a/packages/flutter/test/cupertino/tab_scaffold_test.dart +++ b/packages/flutter/test/cupertino/tab_scaffold_test.dart @@ -20,26 +20,18 @@ void main() { final List tabsPainted = []; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabScaffold( - tabBar: _buildTabBar(), - tabBuilder: (BuildContext context, int index) { - return new CustomPaint( - child: new Text('Page ${index + 1}'), - painter: new TestCallbackPainter( - onPaint: () { tabsPainted.add(index); } - ) - ); - }, - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabScaffold( + tabBar: _buildTabBar(), + tabBuilder: (BuildContext context, int index) { + return new CustomPaint( + child: new Text('Page ${index + 1}'), + painter: new TestCallbackPainter( + onPaint: () { tabsPainted.add(index); } + ) + ); + }, + ), ), ); @@ -82,22 +74,14 @@ void main() { final List tabsBuilt = []; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabScaffold( - tabBar: _buildTabBar(), - tabBuilder: (BuildContext context, int index) { - tabsBuilt.add(index); - return new Text('Page ${index + 1}'); - }, - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabScaffold( + tabBar: _buildTabBar(), + tabBuilder: (BuildContext context, int index) { + tabsBuilt.add(index); + return new Text('Page ${index + 1}'); + }, + ), ), ); @@ -126,26 +110,18 @@ void main() { final List focusNodes = [new FocusNode(), new FocusNode()]; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new Material( - child: new CupertinoTabScaffold( - tabBar: _buildTabBar(), - tabBuilder: (BuildContext context, int index) { - return new TextField( - focusNode: focusNodes[index], - autofocus: true, - ); - }, - ), + new CupertinoApp( + home: new Material( + child: new CupertinoTabScaffold( + tabBar: _buildTabBar(), + tabBuilder: (BuildContext context, int index) { + return new TextField( + focusNode: focusNodes[index], + autofocus: true, ); }, - ); - }, + ), + ), ), ); @@ -170,38 +146,30 @@ void main() { ]; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new Material( - child: new CupertinoTabScaffold( - tabBar: _buildTabBar(), - tabBuilder: (BuildContext context, int index) { - return new Column( - children: [ - new TextField( - focusNode: focusNodes[index * 2], - decoration: const InputDecoration( - hintText: 'TextField 1', - ), - ), - new TextField( - focusNode: focusNodes[index * 2 + 1], - decoration: const InputDecoration( - hintText: 'TextField 2', - ), - ), - ], - ); - }, - ), + new CupertinoApp( + home: new Material( + child: new CupertinoTabScaffold( + tabBar: _buildTabBar(), + tabBuilder: (BuildContext context, int index) { + return new Column( + children: [ + new TextField( + focusNode: focusNodes[index * 2], + decoration: const InputDecoration( + hintText: 'TextField 1', + ), + ), + new TextField( + focusNode: focusNodes[index * 2 + 1], + decoration: const InputDecoration( + hintText: 'TextField 2', + ), + ), + ], ); }, - ); - }, + ), + ), ), ); @@ -242,42 +210,36 @@ void main() { final List tabsPainted = []; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - builder: (BuildContext context, Widget child) { - return new CupertinoTabScaffold( - tabBar: _buildTabBar(), - tabBuilder: (BuildContext context, int index) { - return new CustomPaint( - child: new Text('Page ${index + 1}'), - painter: new TestCallbackPainter( - onPaint: () { tabsPainted.add(index); } - ) - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabScaffold( + tabBar: _buildTabBar(), + tabBuilder: (BuildContext context, int index) { + return new CustomPaint( + child: new Text('Page ${index + 1}'), + painter: new TestCallbackPainter( + onPaint: () { tabsPainted.add(index); } + ) + ); + }, + ), ), ); expect(tabsPainted, [0]); await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - builder: (BuildContext context, Widget child) { - return new CupertinoTabScaffold( - tabBar: _buildTabBar(selectedTab: 1), // Programmatically change the tab now. - tabBuilder: (BuildContext context, int index) { - return new CustomPaint( - child: new Text('Page ${index + 1}'), - painter: new TestCallbackPainter( - onPaint: () { tabsPainted.add(index); } - ) - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabScaffold( + tabBar: _buildTabBar(selectedTab: 1), // Programmatically change the tab now. + tabBuilder: (BuildContext context, int index) { + return new CustomPaint( + child: new Text('Page ${index + 1}'), + painter: new TestCallbackPainter( + onPaint: () { tabsPainted.add(index); } + ) + ); + }, + ), ), ); diff --git a/packages/flutter/test/cupertino/tab_test.dart b/packages/flutter/test/cupertino/tab_test.dart index 6b5f0517c86..c162d0d4376 100644 --- a/packages/flutter/test/cupertino/tab_test.dart +++ b/packages/flutter/test/cupertino/tab_test.dart @@ -8,18 +8,10 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Use home', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabView( - builder: (BuildContext context) => const Text('home'), - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabView( + builder: (BuildContext context) => const Text('home'), + ), ), ); @@ -28,20 +20,12 @@ void main() { testWidgets('Use routes', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabView( - routes: { - '/': (BuildContext context) => const Text('first route'), - }, - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabView( + routes: { + '/': (BuildContext context) => const Text('first route'), + }, + ), ), ); @@ -50,28 +34,20 @@ void main() { testWidgets('Use home and named routes', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabView( - builder: (BuildContext context) { - return new CupertinoButton( - child: const Text('go to second page'), - onPressed: () { - Navigator.of(context).pushNamed('/2'); - }, - ); - }, - routes: { - '/2': (BuildContext context) => const Text('second named route'), - }, - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabView( + builder: (BuildContext context) { + return new CupertinoButton( + child: const Text('go to second page'), + onPressed: () { + Navigator.of(context).pushNamed('/2'); + }, + ); + }, + routes: { + '/2': (BuildContext context) => const Text('second named route'), + }, + ), ), ); @@ -84,27 +60,19 @@ void main() { testWidgets('Use onGenerateRoute', (WidgetTester tester) async { await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabView( - onGenerateRoute: (RouteSettings settings) { - if (settings.name == Navigator.defaultRouteName) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return const Text('generated home'); - } - ); - } - }, + new CupertinoApp( + home: new CupertinoTabView( + onGenerateRoute: (RouteSettings settings) { + if (settings.name == Navigator.defaultRouteName) { + return new CupertinoPageRoute( + settings: settings, + builder: (BuildContext context) { + return const Text('generated home'); + } ); - }, - ); - }, + } + }, + ), ), ); @@ -114,20 +82,12 @@ void main() { testWidgets('Use onUnknownRoute', (WidgetTester tester) async { String unknownForRouteCalled; await tester.pumpWidget( - new WidgetsApp( - color: const Color(0xFFFFFFFF), - onGenerateRoute: (RouteSettings settings) { - return new CupertinoPageRoute( - settings: settings, - builder: (BuildContext context) { - return new CupertinoTabView( - onUnknownRoute: (RouteSettings settings) { - unknownForRouteCalled = settings.name; - }, - ); - }, - ); - }, + new CupertinoApp( + home: new CupertinoTabView( + onUnknownRoute: (RouteSettings settings) { + unknownForRouteCalled = settings.name; + }, + ), ), );