Unverified Commit aba0379d authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Clean up the existing Navigator API. (#15718)

This is not a grand refactor yet, it's just cleaning up what we have
already, so that people who keep using this API (e.g. dialogs) have
something coherent to deal with.

The major changes are that Navigator and NavigatorState have the same
API now, that most of the examples use `<void>` instead of `<Null>`,
that the navigator observer can see replaces, and that the `settings`
is moved from ModalRoute to Route. I also cleaned up some of the API
documentation.
parent 3a2e0d93
......@@ -47,37 +47,37 @@ class _HomeState extends State<Home> {
child: const Text('Test Underlines'),
color: Colors.red.shade800,
textColor: Colors.white,
onPressed: () { Navigator.of(context).pushNamed('underlines'); },
onPressed: () { Navigator.pushNamed(context, 'underlines'); },
),
new FlatButton(
child: const Text('Test Font Fallback'),
color: Colors.orange.shade700,
textColor: Colors.white,
onPressed: () { Navigator.of(context).pushNamed('fallback'); },
onPressed: () { Navigator.pushNamed(context, 'fallback'); },
),
new FlatButton(
child: const Text('Test Bidi Formatting'),
color: Colors.yellow.shade700,
textColor: Colors.black,
onPressed: () { Navigator.of(context).pushNamed('bidi'); },
onPressed: () { Navigator.pushNamed(context, 'bidi'); },
),
new FlatButton(
child: const Text('TextSpan Fuzzer'),
color: Colors.green.shade400,
textColor: Colors.black,
onPressed: () { Navigator.of(context).pushNamed('fuzzer'); },
onPressed: () { Navigator.pushNamed(context, 'fuzzer'); },
),
new FlatButton(
child: const Text('Diacritics Fuzzer'),
color: Colors.blue.shade400,
textColor: Colors.white,
onPressed: () { Navigator.of(context).pushNamed('zalgo'); },
onPressed: () { Navigator.pushNamed(context, 'zalgo'); },
),
new FlatButton(
child: const Text('Painting Fuzzer'),
color: Colors.purple.shade200,
textColor: Colors.black,
onPressed: () { Navigator.of(context).pushNamed('painting'); },
onPressed: () { Navigator.pushNamed(context, 'painting'); },
),
],
),
......
......@@ -451,7 +451,7 @@ class _AnimationDemoHomeState extends State<AnimationDemoHome> {
if (_scrollController.offset >= midScrollOffset)
_scrollController.animateTo(0.0, curve: _kScrollCurve, duration: _kScrollDuration);
else
Navigator.of(context).maybePop();
Navigator.maybePop(context);
}
// Only enable paging for the heading when the user has scrolled to midScrollOffset.
......
......@@ -21,7 +21,7 @@ class _CupertinoDialogDemoState extends State<CupertinoDialogDemo> {
barrierDismissible: false,
builder: (BuildContext context) => child,
)
.then<Null>((T value) { // The value passed to Navigator.pop() or null.
.then<void>((T value) { // The value passed to Navigator.pop() or null.
if (value != null) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text('You selected: $value')
......
......@@ -173,7 +173,7 @@ class Tab1RowItem extends StatelessWidget {
final Widget row = new GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).push(new CupertinoPageRoute<Null>(
Navigator.of(context).push(new CupertinoPageRoute<void>(
builder: (BuildContext context) => new Tab1ItemPage(
color: color,
colorName: colorName,
......@@ -778,7 +778,7 @@ class Tab3Dialog extends StatelessWidget {
color: CupertinoColors.activeBlue,
child: const Text('Sign in'),
onPressed: () {
Navigator.of(context).pop();
Navigator.pop(context);
},
),
],
......
......@@ -112,7 +112,7 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
const Padding(padding: const EdgeInsets.only(top: 32.0)),
new GestureDetector(
onTap: () async {
await showModalBottomSheet<Null>(
await showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return _buildBottomPicker();
......
......@@ -70,7 +70,7 @@ class DialogDemoState extends State<DialogDemo> {
context: context,
builder: (BuildContext context) => child,
)
.then<Null>((T value) { // The value passed to Navigator.pop() or null.
.then<void>((T value) { // The value passed to Navigator.pop() or null.
if (value != null) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text('You selected: $value')
......
......@@ -67,7 +67,7 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
}
void _showNotImplementedMessage() {
Navigator.of(context).pop(); // Dismiss the drawer.
Navigator.pop(context); // Dismiss the drawer.
_scaffoldKey.currentState.showSnackBar(const SnackBar(
content: const Text("The drawer's items don't do anything")
));
......@@ -231,7 +231,7 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
}
void _onOtherAccountsTap(BuildContext context) {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
......@@ -240,7 +240,7 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
new FlatButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
Navigator.pop(context);
},
),
],
......
......@@ -165,7 +165,7 @@ class GridDemoPhotoItem extends StatelessWidget {
final BannerTapCallback onBannerTap; // User taps on the photo's header or footer.
void showPhoto(BuildContext context) {
Navigator.push(context, new MaterialPageRoute<Null>(
Navigator.push(context, new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Scaffold(
appBar: new AppBar(
......
......@@ -15,7 +15,7 @@ class ModalBottomSheetDemo extends StatelessWidget {
child: new RaisedButton(
child: const Text('SHOW BOTTOM SHEET'),
onPressed: () {
showModalBottomSheet<Null>(context: context, builder: (BuildContext context) {
showModalBottomSheet<void>(context: context, builder: (BuildContext context) {
return new Container(
child: new Padding(
padding: const EdgeInsets.all(32.0),
......
......@@ -54,7 +54,7 @@ class _PersistentBottomSheetDemoState extends State<PersistentBottomSheetDemo> {
}
void _showMessage() {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
......
......@@ -167,14 +167,14 @@ class _RecipeGridPageState extends State<RecipeGridPage> {
}
void showFavoritesPage(BuildContext context) {
Navigator.push(context, new MaterialPageRoute<Null>(
Navigator.push(context, new MaterialPageRoute<void>(
settings: const RouteSettings(name: '/pesto/favorites'),
builder: (BuildContext context) => new PestoFavorites(),
));
}
void showRecipePage(BuildContext context, Recipe recipe) {
Navigator.push(context, new MaterialPageRoute<Null>(
Navigator.push(context, new MaterialPageRoute<void>(
settings: const RouteSettings(name: '/pesto/recipe'),
builder: (BuildContext context) {
return new Theme(
......
......@@ -327,7 +327,7 @@ class ShrineOrderRoute extends ShrinePageRoute<Order> {
ShrineOrderRoute({
@required this.order,
WidgetBuilder builder,
RouteSettings settings: const RouteSettings(),
RouteSettings settings,
}) : assert(order != null),
super(builder: builder, settings: settings);
......@@ -336,5 +336,5 @@ class ShrineOrderRoute extends ShrinePageRoute<Order> {
@override
Order get currentResult => order;
static ShrineOrderRoute of(BuildContext context) => ModalRoute.of(context);
static ShrineOrderRoute of(BuildContext context) => ModalRoute.of<Order>(context);
}
......@@ -51,7 +51,7 @@ class ShrinePageState extends State<ShrinePage> {
}
void _showShoppingCart() {
showModalBottomSheet<Null>(context: context, builder: (BuildContext context) {
showModalBottomSheet<void>(context: context, builder: (BuildContext context) {
if (widget.shoppingCart.isEmpty) {
return const Padding(
padding: const EdgeInsets.all(24.0),
......
......@@ -25,7 +25,7 @@ Widget buildShrine(BuildContext context, Widget child) {
class ShrinePageRoute<T> extends MaterialPageRoute<T> {
ShrinePageRoute({
WidgetBuilder builder,
RouteSettings settings: const RouteSettings()
RouteSettings settings,
}) : super(builder: builder, settings: settings);
@override
......
......@@ -64,12 +64,12 @@ class VideoCard extends StatelessWidget {
}
void pushFullScreenWidget() {
final TransitionRoute<Null> route = new PageRouteBuilder<Null>(
final TransitionRoute<void> route = new PageRouteBuilder<void>(
settings: new RouteSettings(name: title, isInitialRoute: false),
pageBuilder: fullScreenRoutePageBuilder,
);
route.completed.then((Null _) {
route.completed.then((void result) {
controller.setVolume(0.0);
});
......
......@@ -165,7 +165,7 @@ class GalleryAppState extends State<GalleryApp> {
checkerboardRasterCacheImages: _checkerboardRasterCacheImages,
checkerboardOffscreenLayers: _checkerboardOffscreenLayers,
routes: _kRoutes,
home: _applyScaleFactor(home),
home: home,
builder: (BuildContext context, Widget child) {
return new Directionality(
textDirection: _overrideDirection,
......
......@@ -31,23 +31,23 @@ class UpdaterState extends State<Updater> {
}
static DateTime _lastUpdateCheck;
Future<Null> _checkForUpdates() async {
Future<void> _checkForUpdates() async {
// Only prompt once a day
if (_lastUpdateCheck != null &&
new DateTime.now().difference(_lastUpdateCheck) < const Duration(days: 1)) {
return; // We already checked for updates recently
return null; // We already checked for updates recently
}
_lastUpdateCheck = new DateTime.now();
final String updateUrl = await widget.updateUrlFetcher();
if (updateUrl != null) {
final bool wantsUpdate = await showDialog(context: context, builder: _buildDialog);
final bool wantsUpdate = await showDialog<bool>(context: context, builder: _buildDialog);
if (wantsUpdate != null && wantsUpdate)
launch(updateUrl);
}
}
Widget _buildDialog(BuildContext _) {
Widget _buildDialog(BuildContext context) {
final ThemeData theme = Theme.of(context);
final TextStyle dialogTextStyle =
theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color);
......@@ -59,13 +59,16 @@ class UpdaterState extends State<Updater> {
child: const Text('NO THANKS'),
onPressed: () {
Navigator.pop(context, false);
}),
},
),
new FlatButton(
child: const Text('UPDATE'),
onPressed: () {
Navigator.pop(context, true);
}),
]);
},
),
],
);
}
@override
......
......@@ -83,7 +83,7 @@ class StocksAppState extends State<StocksApp> {
return null;
}
Route<Null> _getRoute(RouteSettings settings) {
Route<dynamic> _getRoute(RouteSettings settings) {
// Routes, by convention, are split on slashes, like filesystem paths.
final List<String> path = settings.name.split('/');
// We only support paths that start with a slash, so bail if
......@@ -100,7 +100,7 @@ class StocksAppState extends State<StocksApp> {
// Extract the symbol part of "stock:..." and return a route
// for that symbol.
final String symbol = path[1].substring(6);
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) => new StockSymbolPage(symbol: symbol, stocks: stocks),
);
......
......@@ -93,7 +93,7 @@ class StockHomeState extends State<StockHome> {
});
break;
case _StockMenuItem.refresh:
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) => new _NotImplementedDialog(),
);
......@@ -297,7 +297,7 @@ class StockHomeState extends State<StockHome> {
}
void _handleCreateCompany() {
showModalBottomSheet<Null>(
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) => new _CreateCompanySheet(),
);
......
......@@ -397,7 +397,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
)
: const Text('Close'),
padding: EdgeInsets.zero,
onPressed: () { Navigator.of(context).maybePop(); },
onPressed: () { Navigator.maybePop(context); },
);
}
}
......
......@@ -76,16 +76,15 @@ final DecorationTween _kGradientShadowTween = new DecorationTween(
class CupertinoPageRoute<T> extends PageRoute<T> {
/// Creates a page route for use in an iOS designed app.
///
/// The [builder], [settings], [maintainState], and [fullscreenDialog]
/// arguments must not be null.
/// The [builder], [maintainState], and [fullscreenDialog] arguments must not
/// be null.
CupertinoPageRoute({
@required this.builder,
RouteSettings settings: const RouteSettings(),
RouteSettings settings,
this.maintainState: true,
bool fullscreenDialog: false,
this.hostRoute,
}) : assert(builder != null),
assert(settings != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
super(settings: settings, fullscreenDialog: fullscreenDialog) {
......@@ -149,7 +148,7 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
super.dispose();
}
_CupertinoBackGestureController _backGestureController;
_CupertinoBackGestureController<T> _backGestureController;
/// Whether a pop gesture is currently underway.
///
......@@ -217,11 +216,11 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
/// appropriate.
/// * [Route.startPopGesture], which describes the contract that this method
/// must implement.
_CupertinoBackGestureController _startPopGesture() {
_CupertinoBackGestureController<T> _startPopGesture() {
assert(!popGestureInProgress);
assert(popGestureEnabled);
final PageRoute<T> route = hostRoute ?? this;
_backGestureController = new _CupertinoBackGestureController(
_backGestureController = new _CupertinoBackGestureController<T>(
navigator: route.navigator,
controller: route.controller,
onEnded: _endPopGesture,
......@@ -265,7 +264,7 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
// In the middle of a back gesture drag, let the transition be linear to
// match finger motions.
linearTransition: popGestureInProgress,
child: new _CupertinoBackGestureDetector(
child: new _CupertinoBackGestureDetector<T>(
enabledCallback: () => popGestureEnabled,
onStartPopGesture: _startPopGesture,
child: child,
......@@ -392,7 +391,10 @@ class CupertinoFullscreenDialogTransition extends StatelessWidget {
///
/// The gesture data is converted from absolute coordinates to logical
/// coordinates by this widget.
class _CupertinoBackGestureDetector extends StatefulWidget {
///
/// The type `T` specifies the return type of the route with which this gesture
/// detector is associated.
class _CupertinoBackGestureDetector<T> extends StatefulWidget {
const _CupertinoBackGestureDetector({
Key key,
@required this.enabledCallback,
......@@ -407,14 +409,14 @@ class _CupertinoBackGestureDetector extends StatefulWidget {
final ValueGetter<bool> enabledCallback;
final ValueGetter<_CupertinoBackGestureController> onStartPopGesture;
final ValueGetter<_CupertinoBackGestureController<T>> onStartPopGesture;
@override
_CupertinoBackGestureDetectorState createState() => new _CupertinoBackGestureDetectorState();
_CupertinoBackGestureDetectorState<T> createState() => new _CupertinoBackGestureDetectorState<T>();
}
class _CupertinoBackGestureDetectorState extends State<_CupertinoBackGestureDetector> {
_CupertinoBackGestureController _backGestureController;
class _CupertinoBackGestureDetectorState<T> extends State<_CupertinoBackGestureDetector<T>> {
_CupertinoBackGestureController<T> _backGestureController;
HorizontalDragGestureRecognizer _recognizer;
......@@ -508,7 +510,10 @@ class _CupertinoBackGestureDetectorState extends State<_CupertinoBackGestureDete
///
/// This class works entirely in logical coordinates (0.0 is new page dismissed,
/// 1.0 is new page on top).
class _CupertinoBackGestureController {
///
/// The type `T` specifies the return type of the route with which this gesture
/// detector controller is associated.
class _CupertinoBackGestureController<T> {
/// Creates a controller for an iOS-style back gesture.
///
/// The [navigator] and [controller] arguments must not be null.
......@@ -566,7 +571,7 @@ class _CupertinoBackGestureController {
controller.removeStatusListener(_handleStatusChanged);
_animating = false;
if (status == AnimationStatus.dismissed)
navigator.pop(); // this will cause the route to get disposed, which will dispose us
navigator.pop<T>(); // this will cause the route to get disposed, which will dispose us
onEnded(); // this will call dispose if popping the route failed to do so
}
......
......@@ -149,9 +149,9 @@ void showAboutDialog({
String applicationVersion,
Widget applicationIcon,
String applicationLegalese,
List<Widget> children
List<Widget> children,
}) {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new AboutDialog(
......@@ -185,9 +185,7 @@ void showLicensePage({
Widget applicationIcon,
String applicationLegalese
}) {
// TODO(ianh): remove pop once https://github.com/flutter/flutter/issues/4667 is fixed
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute<Null>(
Navigator.push(context, new MaterialPageRoute<void>(
builder: (BuildContext context) => new LicensePage(
applicationName: applicationName,
applicationVersion: applicationVersion,
......
......@@ -607,10 +607,11 @@ class _MaterialAppState extends State<MaterialApp> {
Route<dynamic> _onGenerateRoute(RouteSettings settings) {
final String name = settings.name;
WidgetBuilder builder;
if (name == Navigator.defaultRouteName && widget.home != null)
if (name == Navigator.defaultRouteName && widget.home != null) {
builder = (BuildContext context) => widget.home;
else
} else {
builder = widget.routes[name];
}
if (builder != null) {
return new MaterialPageRoute<dynamic>(
builder: builder,
......
......@@ -86,7 +86,7 @@ class BackButton extends StatelessWidget {
color: color,
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () {
Navigator.of(context).maybePop();
Navigator.maybePop(context);
}
);
}
......@@ -118,7 +118,7 @@ class CloseButton extends StatelessWidget {
icon: const Icon(Icons.close),
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
onPressed: () {
Navigator.of(context).maybePop();
Navigator.maybePop(context);
},
);
}
......
......@@ -179,14 +179,10 @@ class _ModalBottomSheet<T> extends StatefulWidget {
}
class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
void _navigatorPop() {
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: _navigatorPop,
onTap: () => Navigator.pop(context),
child: new AnimatedBuilder(
animation: widget.route.animation,
builder: (BuildContext context, Widget child) {
......@@ -211,7 +207,8 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
this.builder,
this.theme,
this.barrierLabel,
});
RouteSettings settings,
}) : super(settings: settings);
final WidgetBuilder builder;
final ThemeData theme;
......
......@@ -488,8 +488,10 @@ class _DialogRoute<T> extends PopupRoute<T> {
bool barrierDismissible: true,
this.barrierLabel,
@required this.child,
RouteSettings settings,
}) : assert(barrierDismissible != null),
_barrierDismissible = barrierDismissible;
_barrierDismissible = barrierDismissible,
super(settings: settings);
final Widget child;
final ThemeData theme;
......
......@@ -600,7 +600,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
Navigator.push(context, _dropdownRoute).then<void>((_DropdownRouteResult<T> newValue) {
_dropdownRoute = null;
if (!mounted || newValue == null)
return null;
return;
if (widget.onChanged != null)
widget.onChanged(newValue.result);
});
......
......@@ -48,34 +48,38 @@ class _MountainViewPageTransition extends StatelessWidget {
}
}
/// A modal route that replaces the entire screen with a platform-adaptive transition.
/// A modal route that replaces the entire screen with a platform-adaptive
/// transition.
///
/// For Android, the entrance transition for the page slides the page upwards and fades it
/// in. The exit transition is the same, but in reverse.
/// For Android, the entrance transition for the page slides the page upwards
/// and fades it in. The exit transition is the same, but in reverse.
///
/// The transition is adaptive to the platform and on iOS, the page slides in from the right and
/// exits in reverse. The page also shifts to the left in parallax when another page enters to
/// cover it.
/// The transition is adaptive to the platform and on iOS, the page slides in
/// from the right and exits in reverse. The page also shifts to the left in
/// parallax when another page enters to cover it. (These directions are flipped
/// in environements with a right-to-left reading direction.)
///
/// By default, when a modal route is replaced by another, the previous route
/// remains in memory. To free all the resources when this is not necessary, set
/// [maintainState] to false.
///
/// Specify whether the incoming page is a fullscreen modal dialog. On iOS, those
/// pages animate bottom->up rather than right->left.
/// The `fullscreenDialog` property specifies whether the incoming page is a
/// fullscreen modal dialog. On iOS, those pages animate from the bottom to the
/// top rather than horizontally.
///
/// The type `T` specifies the return type of the route which can be supplied as
/// the route is popped from the stack via [Navigator.pop] when an optional
/// `result` can be provided.
/// the route is popped from the stack via [Navigator.pop] by providing the
/// optional `result` argument.
///
/// See also:
///
/// * [CupertinoPageRoute], that this [PageRoute] delegates transition animations to for iOS.
/// * [CupertinoPageRoute], which this [PageRoute] delegates transition
/// animations to for iOS.
class MaterialPageRoute<T> extends PageRoute<T> {
/// Creates a page route for use in a material design app.
MaterialPageRoute({
@required this.builder,
RouteSettings settings: const RouteSettings(),
RouteSettings settings,
this.maintainState: true,
bool fullscreenDialog: false,
}) : assert(builder != null),
......
......@@ -196,15 +196,44 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
bool represents(T value) => value == this.value;
@override
_PopupMenuItemState<PopupMenuItem<T>> createState() => new _PopupMenuItemState<PopupMenuItem<T>>();
PopupMenuItemState<T, PopupMenuItem<T>> createState() => new PopupMenuItemState<T, PopupMenuItem<T>>();
}
class _PopupMenuItemState<T extends PopupMenuItem<dynamic>> extends State<T> {
// Override this to put something else in the menu entry.
/// The [State] for [PopupMenuItem] subclasses.
///
/// By default this implements the basic styling and layout of Material Design
/// popup menu items.
///
/// The [buildChild] method can be overridden to adjust exactly what gets placed
/// in the menu. By default it returns [PopupMenuItem.child].
///
/// The [handleTap] method can be overridden to adjust exactly what happens when
/// the item is tapped. By default, it uses [Navigator.pop] to return the
/// [PopupMenuItem.value] from the menu route.
///
/// This class takes two type arguments. The second, `W`, is the exact type of
/// the [Widget] that is using this [State]. It must be a subclass of
/// [PopupMenuItem]. The first, `T`, must match the type argument of that widget
/// class, and is the type of values returned from this menu.
class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
/// The menu item contents.
///
/// Used by the [build] method.
///
/// By default, this returns [PopupMenuItem.child]. Override this to put
/// something else in the menu entry.
@protected
Widget buildChild() => widget.child;
/// The handler for when the user selects the menu item.
///
/// Used by the [InkWell] inserted by the [build] method.
///
/// By default, uses [Navigator.pop] to return the [PopupMenuItem.value] from
/// the menu route.
@protected
void handleTap() {
Navigator.pop(context, widget.value);
Navigator.pop<T>(context, widget.value);
}
@override
......@@ -238,8 +267,8 @@ class _PopupMenuItemState<T extends PopupMenuItem<dynamic>> extends State<T> {
height: widget.height,
padding: const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding),
child: item,
)
)
),
),
);
}
}
......@@ -349,7 +378,7 @@ class CheckedPopupMenuItem<T> extends PopupMenuItem<T> {
_CheckedPopupMenuItemState<T> createState() => new _CheckedPopupMenuItemState<T>();
}
class _CheckedPopupMenuItemState<T> extends _PopupMenuItemState<CheckedPopupMenuItem<T>> with SingleTickerProviderStateMixin {
class _CheckedPopupMenuItemState<T> extends PopupMenuItemState<T, CheckedPopupMenuItem<T>> with SingleTickerProviderStateMixin {
static const Duration _kFadeDuration = const Duration(milliseconds: 150);
AnimationController _controller;
Animation<double> get _opacity => _controller.view;
......
......@@ -680,6 +680,9 @@ class SemanticsProperties extends DiagnosticableTree {
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(new DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null));
}
@override
String toStringShort() => '$runtimeType'; // the hashCode isn't important since we're immutable
}
/// In tests use this function to reset the counter used to generate
......
......@@ -12,7 +12,7 @@ import 'routes.dart';
abstract class PageRoute<T> extends ModalRoute<T> {
/// Creates a modal route that replaces the entire screen.
PageRoute({
RouteSettings settings: const RouteSettings(),
RouteSettings settings,
this.fullscreenDialog: false,
}) : super(settings: settings);
......@@ -71,7 +71,7 @@ class PageRouteBuilder<T> extends PageRoute<T> {
/// The [pageBuilder], [transitionsBuilder], [opaque], [barrierDismissible],
/// and [maintainState] arguments must not be null.
PageRouteBuilder({
RouteSettings settings: const RouteSettings(),
RouteSettings settings,
@required this.pageBuilder,
this.transitionsBuilder: _defaultTransitionsBuilder,
this.transitionDuration: const Duration(milliseconds: 300),
......
......@@ -17,7 +17,7 @@ void main() {
builder: (BuildContext context) {
return new RaisedButton(
onPressed: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new CupertinoAlertDialog(
......@@ -110,7 +110,7 @@ void main() {
child: new Builder(builder: (BuildContext context) {
return new RaisedButton(
onPressed: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new MediaQuery(
......
......@@ -14,7 +14,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -36,7 +36,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -56,7 +56,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -76,7 +76,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -98,7 +98,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoPageScaffold(
......@@ -126,7 +126,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new MediaQuery(
......@@ -178,7 +178,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoPageScaffold(
......@@ -266,7 +266,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoPageScaffold(
......@@ -337,7 +337,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -351,7 +351,7 @@ void main() {
expect(find.byType(CupertinoButton), findsNothing);
tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<Null>(
tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<void>(
builder: (BuildContext context) {
return const CupertinoNavigationBar(
middle: const Text('Page 2'),
......@@ -365,7 +365,7 @@ void main() {
expect(find.byType(CupertinoButton), findsOneWidget);
expect(find.byType(Icon), findsOneWidget);
tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<Null>(
tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -401,7 +401,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -428,7 +428,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......@@ -462,7 +462,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoNavigationBar(
......
......@@ -11,7 +11,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
final String pageNumber = settings.name == '/' ? '1' : '2';
......@@ -82,7 +82,7 @@ void main() {
],
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
final String pageNumber = settings.name == '/' ? '1' : '2';
......@@ -151,7 +151,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const Center(child: const Text('Page 1'));
......@@ -163,7 +163,7 @@ void main() {
final Offset widget1InitialTopLeft = tester.getTopLeft(find.text('Page 1'));
tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<Null>(
tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<void>(
builder: (BuildContext context) {
return const Center(child: const Text('Page 2'));
},
......@@ -219,7 +219,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
final String pageNumber = settings.name == '/' ? '1' : '2';
......@@ -284,7 +284,7 @@ void main() {
],
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
final String pageNumber = settings.name == '/' ? '1' : '2';
......
......@@ -14,7 +14,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoPageScaffold(
......@@ -40,7 +40,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabScaffold(
......@@ -85,7 +85,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new MediaQuery(
......@@ -145,7 +145,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabScaffold(
......@@ -175,7 +175,7 @@ void main() {
child: const Text('Next'),
onPressed: () {
Navigator.of(context).push(
new CupertinoPageRoute<Null>(
new CupertinoPageRoute<void>(
builder: (BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
......@@ -260,7 +260,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoPageScaffold(
......@@ -284,7 +284,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const CupertinoPageScaffold(
......
......@@ -23,7 +23,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabScaffold(
......@@ -85,7 +85,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabScaffold(
......@@ -129,7 +129,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabScaffold(
......@@ -171,7 +171,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new Material(
......
......@@ -11,7 +11,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabView(
......@@ -31,7 +31,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabView(
......@@ -53,7 +53,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabView(
......@@ -87,13 +87,13 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabView(
onGenerateRoute: (RouteSettings settings) {
if (settings.name == Navigator.defaultRouteName) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return const Text('generated home');
......@@ -117,7 +117,7 @@ void main() {
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<Null>(
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabView(
......
......@@ -87,7 +87,7 @@ void main() {
expect(state2.marker, equals('original'));
});
testWidgets('Do not rebuild page on the second frame of the route transition', (WidgetTester tester) async {
testWidgets('Do not rebuild page during a route transition', (WidgetTester tester) async {
int buildCounter = 0;
await tester.pumpWidget(
new MaterialApp(
......@@ -128,10 +128,82 @@ void main() {
await tester.pump(const Duration(milliseconds: 10));
expect(buildCounter, 1);
await tester.pump(const Duration(seconds: 1));
expect(buildCounter, 2);
expect(buildCounter, 1);
expect(find.text('Y'), findsOneWidget);
});
testWidgets('Do rebuild the home page if it changes', (WidgetTester tester) async {
int buildCounter = 0;
await tester.pumpWidget(
new MaterialApp(
home: new Builder(
builder: (BuildContext context) {
++buildCounter;
return const Text('A');
}
),
),
);
expect(buildCounter, 1);
expect(find.text('A'), findsOneWidget);
await tester.pumpWidget(
new MaterialApp(
home: new Builder(
builder: (BuildContext context) {
++buildCounter;
return const Text('B');
}
),
),
);
expect(buildCounter, 2);
expect(find.text('B'), findsOneWidget);
});
testWidgets('Do not rebuild the home page if it does not actually change', (WidgetTester tester) async {
int buildCounter = 0;
final Widget home = new Builder(
builder: (BuildContext context) {
++buildCounter;
return const Placeholder();
}
);
await tester.pumpWidget(
new MaterialApp(
home: home,
),
);
expect(buildCounter, 1);
await tester.pumpWidget(
new MaterialApp(
home: home,
),
);
expect(buildCounter, 1);
});
testWidgets('Do rebuild pages that come from the routes table if the MaterialApp changes', (WidgetTester tester) async {
int buildCounter = 0;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) {
++buildCounter;
return const Placeholder();
},
};
await tester.pumpWidget(
new MaterialApp(
routes: routes,
),
);
expect(buildCounter, 1);
await tester.pumpWidget(
new MaterialApp(
routes: routes,
),
);
expect(buildCounter, 2);
});
testWidgets('Cannot pop the initial route', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp(home: const Text('Home')));
......
......@@ -21,7 +21,7 @@ void main() {
child: new RaisedButton(
child: const Text('X'),
onPressed: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
......@@ -71,7 +71,7 @@ void main() {
child: new RaisedButton(
child: const Text('X'),
onPressed: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return const AlertDialog(
......@@ -118,7 +118,7 @@ void main() {
final BuildContext context = tester.element(find.text('Go'));
final Future<int> result = showDialog(
final Future<int> result = showDialog<int>(
context: context,
builder: (BuildContext context) {
return new SimpleDialog(
......@@ -161,7 +161,7 @@ void main() {
final BuildContext context = tester.element(find.text('Go'));
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return new Container(
......@@ -182,7 +182,7 @@ void main() {
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Dialog1'), findsNothing);
showDialog<Null>(
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
......@@ -227,7 +227,7 @@ void main() {
final BuildContext context = tester.element(find.text(buttonText));
const String alertText = 'A button in an overlay alert';
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return const AlertDialog(title: const Text(alertText));
......@@ -260,7 +260,7 @@ void main() {
),
child: new Navigator(
onGenerateRoute: (_) {
return new PageRouteBuilder<Null>(
return new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
outerContext = context;
return new Container();
......@@ -271,7 +271,7 @@ void main() {
),
));
showDialog<Null>(
showDialog<void>(
context: outerContext,
barrierDismissible: false,
builder: (BuildContext context) {
......
......@@ -76,7 +76,7 @@ class _TestAppState extends State<TestApp> {
child: new Navigator(
onGenerateRoute: (RouteSettings settings) {
assert(settings.name == '/');
return new MaterialPageRoute<dynamic>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) => widget.child,
);
......@@ -159,7 +159,7 @@ void main() {
child: new Navigator(
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new Material(
......
......@@ -84,7 +84,7 @@ void main() {
),
),
);
Navigator.push(theContext, new PageRouteBuilder<Null>(
Navigator.push(theContext, new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return const Placeholder();
},
......@@ -107,7 +107,7 @@ void main() {
),
),
);
Navigator.push(theContext, new PageRouteBuilder<Null>(
Navigator.push(theContext, new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return const Placeholder();
},
......@@ -131,7 +131,7 @@ void main() {
),
),
);
Navigator.push(theContext, new PageRouteBuilder<Null>(
Navigator.push(theContext, new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return const Placeholder();
},
......
......@@ -27,7 +27,7 @@ void main() {
showModalBottomSheet<Null>(
context: savedContext,
builder: (BuildContext context) => const Text('BottomSheet')
).then<Null>((Null result) {
).then<void>((Null result) {
expectSync(result, isNull);
showBottomSheetThenCalled = true;
});
......@@ -49,7 +49,7 @@ void main() {
showModalBottomSheet<Null>(
context: savedContext,
builder: (BuildContext context) => const Text('BottomSheet'),
).then<Null>((Null result) {
).then<void>((Null result) {
expectSync(result, isNull);
showBottomSheetThenCalled = true;
});
......@@ -172,7 +172,7 @@ void main() {
),
child: new Navigator(
onGenerateRoute: (_) {
return new PageRouteBuilder<Null>(
return new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
outerContext = context;
return new Container();
......@@ -184,7 +184,7 @@ void main() {
),
));
showModalBottomSheet<Null>(
showModalBottomSheet<void>(
context: outerContext,
builder: (BuildContext context) {
innerContext = context;
......
......@@ -156,7 +156,7 @@ void main() {
final Offset widget1InitialTopLeft = tester.getTopLeft(find.text('Page 1'));
tester.state<NavigatorState>(find.byType(Navigator)).push(new MaterialPageRoute<Null>(
tester.state<NavigatorState>(find.byType(Navigator)).push(new MaterialPageRoute<void>(
builder: (BuildContext context) {
return const Material(child: const Text('Page 2'));
},
......@@ -355,7 +355,7 @@ void main() {
)
);
tester.state<NavigatorState>(find.byType(Navigator)).push(new MaterialPageRoute<Null>(
tester.state<NavigatorState>(find.byType(Navigator)).push(new MaterialPageRoute<void>(
builder: (BuildContext context) {
return const Scaffold(body: const Text('Page 2'));
},
......
......@@ -453,7 +453,7 @@ class _TestAppState extends State<TestApp> {
child: new Navigator(
onGenerateRoute: (RouteSettings settings) {
assert(settings.name == '/');
return new MaterialPageRoute<dynamic>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) => new Material(
child: widget.child,
......
......@@ -419,7 +419,7 @@ void main() {
)
);
tester.state<NavigatorState>(find.byType(Navigator)).push(new MaterialPageRoute<Null>(
tester.state<NavigatorState>(find.byType(Navigator)).push(new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Scaffold(appBar: new AppBar(), body: const Text('Page 2'));
},
......
......@@ -108,7 +108,7 @@ void main() {
final BuildContext context = tester.element(find.byType(TextField));
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) => const SimpleDialog(title: const Text('Dialog')),
);
......
......@@ -161,7 +161,7 @@ void main() {
builder: (BuildContext context) {
return new RaisedButton(
onPressed: () {
showModalBottomSheet<Null>(
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) => const Text('bottomSheet'),
);
......@@ -198,7 +198,7 @@ void main() {
builder: (BuildContext context) {
return new RaisedButton(
onPressed: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) => const Text('dialog'),
);
......@@ -230,7 +230,7 @@ void main() {
builder: (BuildContext context) {
return new GestureDetector(
onTap: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) {
return const Scaffold(
......
......@@ -240,7 +240,7 @@ void _tests() {
textDirection: TextDirection.ltr,
child: new Navigator(
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<dynamic>(builder: (BuildContext context) {
return new MaterialPageRoute<void>(builder: (BuildContext context) {
return new FlatButton(
onPressed: () {
showTimePicker(context: context, initialTime: initialTime);
......
......@@ -543,7 +543,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new Navigator(
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<dynamic>(
return new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Center(
child: new Tooltip(
......
......@@ -13,7 +13,7 @@ class SamplePage extends StatefulWidget {
}
class SamplePageState extends State<SamplePage> {
ModalRoute<Null> _route;
ModalRoute<void> _route;
Future<bool> _callback() async => willPopValue;
......@@ -66,7 +66,7 @@ class SampleForm extends StatelessWidget {
// Expose the protected hasScopedWillPopCallback getter
class TestPageRoute<T> extends MaterialPageRoute<T> {
TestPageRoute({ WidgetBuilder builder })
: super(builder: builder, maintainState: true, settings: const RouteSettings());
: super(builder: builder, maintainState: true);
bool get hasCallback => super.hasScopedWillPopCallback;
}
......@@ -84,7 +84,7 @@ void main() {
child: new FlatButton(
child: const Text('X'),
onPressed: () {
showDialog<Null>(
showDialog<void>(
context: context,
builder: (BuildContext context) => new SamplePage(),
);
......@@ -138,7 +138,7 @@ void main() {
child: new FlatButton(
child: const Text('X'),
onPressed: () {
Navigator.of(context).push(new MaterialPageRoute<Null>(
Navigator.of(context).push(new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new SampleForm(
callback: () => new Future<bool>.value(willPopValue),
......@@ -212,7 +212,7 @@ void main() {
child: new FlatButton(
child: const Text('X'),
onPressed: () {
Navigator.of(context).push(new MaterialPageRoute<Null>(
Navigator.of(context).push(new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new SampleForm(
callback: () => showYesNoAlert(context),
......
......@@ -5,7 +5,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
final RouteFactory generateRoute = (RouteSettings settings) => new PageRouteBuilder<Null>(
final RouteFactory generateRoute = (RouteSettings settings) => new PageRouteBuilder<void>(
settings: settings,
pageBuilder: (BuildContext context, Animation<double> animation1, Animation<double> animation2) {
return const Placeholder();
......
......@@ -19,7 +19,7 @@ Future<Null> pumpApp(WidgetTester tester, { GenerateAppTitle onGenerateTitle })
color: kTitleColor,
onGenerateTitle: onGenerateTitle,
onGenerateRoute: (RouteSettings settings) {
return new PageRouteBuilder<Null>(
return new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return new Container();
}
......
......@@ -81,7 +81,7 @@ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
};
class ThreeRoute extends MaterialPageRoute<Null> {
class ThreeRoute extends MaterialPageRoute<void> {
ThreeRoute() : super(builder: (BuildContext context) {
return new Material(
key: routeThreeKey,
......@@ -96,7 +96,7 @@ class ThreeRoute extends MaterialPageRoute<Null> {
});
}
class MutatingRoute extends MaterialPageRoute<Null> {
class MutatingRoute extends MaterialPageRoute<void> {
MutatingRoute() : super(builder: (BuildContext context) {
return new Hero(tag: 'a', child: const Text('MutatingRoute'), key: new UniqueKey());
});
......@@ -363,7 +363,7 @@ void main() {
testWidgets('Popping on first frame does not cause hero observer to crash', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp(
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) => new Hero(tag: 'test', child: new Container()),
);
......@@ -389,7 +389,7 @@ void main() {
testWidgets('Overlapping starting and ending a hero transition works ok', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp(
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) => new Hero(tag: 'test', child: new Container()),
);
......@@ -432,7 +432,7 @@ void main() {
return new FlatButton(
child: const Text('push'),
onPressed: () {
Navigator.push(context, new PageRouteBuilder<Null>(
Navigator.push(context, new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> __) {
return const Text('fail');
},
......@@ -569,9 +569,9 @@ void main() {
// After flying in the opposite direction for 50ms Hero 'a' will
// be smaller than it was, but bigger than its initial size.
await tester.pump(const Duration(milliseconds: 50));
final double height100ms = tester.getSize(find.byKey(firstKey)).height;
expect(height100ms, greaterThan(height150ms));
expect(finalHeight, lessThan(height100ms));
final double height200ms = tester.getSize(find.byKey(firstKey)).height;
expect(height200ms, greaterThan(height150ms));
expect(finalHeight, lessThan(height200ms));
// Hero a's return flight at 149ms. The outgoing (push) flight took
// 150ms so we should be just about back to where Hero 'a' started.
......@@ -593,7 +593,7 @@ void main() {
StateSetter heroCardSetState;
// Show a 200x200 Hero tagged 'H', with key routeHeroKey
final MaterialPageRoute<Null> route = new MaterialPageRoute<Null>(
final MaterialPageRoute<void> route = new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Material(
child: new ListView(
......@@ -695,7 +695,7 @@ void main() {
const Key routeContainerKey = const Key('route hero container');
// Show a 200x200 Hero tagged 'H', with key routeHeroKey
final MaterialPageRoute<Null> route = new MaterialPageRoute<Null>(
final MaterialPageRoute<void> route = new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Material(
child: new ListView(
......@@ -777,7 +777,7 @@ void main() {
const Key routeContainerKey = const Key('route hero container');
// Show a 200x200 Hero tagged 'H', with key routeHeroKey
final MaterialPageRoute<Null> route = new MaterialPageRoute<Null>(
final MaterialPageRoute<void> route = new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Material(
child: new ListView(
......@@ -855,7 +855,7 @@ void main() {
const Key heroBCKey = const Key('BC hero');
// Show a 150x150 Hero tagged 'BC'
final MaterialPageRoute<Null> routeC = new MaterialPageRoute<Null>(
final MaterialPageRoute<void> routeC = new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Material(
child: new ListView(
......@@ -872,7 +872,7 @@ void main() {
);
// Show a height=200 Hero tagged 'AB' and a height=50 Hero tagged 'BC'
final MaterialPageRoute<Null> routeB = new MaterialPageRoute<Null>(
final MaterialPageRoute<void> routeB = new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Material(
child: new ListView(
......@@ -956,7 +956,7 @@ void main() {
});
testWidgets('Stateful hero child state survives flight', (WidgetTester tester) async {
final MaterialPageRoute<Null> route = new MaterialPageRoute<Null>(
final MaterialPageRoute<void> route = new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new Material(
child: new ListView(
......
......@@ -91,6 +91,7 @@ class TestObserver extends NavigatorObserver {
OnObservation onPushed;
OnObservation onPopped;
OnObservation onRemoved;
OnObservation onReplaced;
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
......@@ -111,6 +112,12 @@ class TestObserver extends NavigatorObserver {
if (onRemoved != null)
onRemoved(route, previousRoute);
}
@override
void didReplace({ Route<dynamic> oldRoute, Route<dynamic> newRoute }) {
if (onReplaced != null)
onReplaced(newRoute, oldRoute);
}
}
void main() {
......@@ -193,19 +200,19 @@ void main() {
child: new Navigator(
onGenerateRoute: (RouteSettings settings) {
if (settings.isInitialRoute) {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new RaisedButton(
child: const Text('Next'),
onPressed: () {
Navigator.of(context).push(
new MaterialPageRoute<Null>(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
return new RaisedButton(
child: const Text('Inner page'),
onPressed: () {
Navigator.of(context, rootNavigator: true).push(
new MaterialPageRoute<Null>(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
return const Text('Dialog');
}
......@@ -633,5 +640,133 @@ void main() {
navigator.removeRoute(routes['/A']); // stack becomes /, pageValue will not complete
});
testWidgets('replacing route can be observed', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = new GlobalKey<NavigatorState>();
final List<String> log = <String>[];
final TestObserver observer = new TestObserver()
..onPushed = (Route<dynamic> route, Route<dynamic> previousRoute) {
log.add('pushed ${route.settings.name} (previous is ${previousRoute == null ? "<none>" : previousRoute.settings.name})');
}
..onPopped = (Route<dynamic> route, Route<dynamic> previousRoute) {
log.add('popped ${route.settings.name} (previous is ${previousRoute == null ? "<none>" : previousRoute.settings.name})');
}
..onRemoved = (Route<dynamic> route, Route<dynamic> previousRoute) {
log.add('removed ${route.settings.name} (previous is ${previousRoute == null ? "<none>" : previousRoute.settings.name})');
}
..onReplaced = (Route<dynamic> newRoute, Route<dynamic> oldRoute) {
log.add('replaced ${oldRoute.settings.name} with ${newRoute.settings.name}');
};
Route<void> routeB;
await tester.pumpWidget(new MaterialApp(
navigatorKey: key,
navigatorObservers: <NavigatorObserver>[observer],
home: new FlatButton(
child: const Text('A'),
onPressed: () {
key.currentState.push<void>(routeB = new MaterialPageRoute<void>(
settings: const RouteSettings(name: 'B'),
builder: (BuildContext context) {
return new FlatButton(
child: const Text('B'),
onPressed: () {
key.currentState.push<void>(new MaterialPageRoute<int>(
settings: const RouteSettings(name: 'C'),
builder: (BuildContext context) {
return new FlatButton(
child: const Text('C'),
onPressed: () {
key.currentState.replace(
oldRoute: routeB,
newRoute: new MaterialPageRoute<int>(
settings: const RouteSettings(name: 'D'),
builder: (BuildContext context) {
return const Text('D');
},
),
);
},
);
},
));
},
);
},
));
},
),
));
expect(log, <String>['pushed / (previous is <none>)']);
await tester.tap(find.text('A'));
await tester.pump();
await tester.pump(const Duration(seconds: 1));
expect(log, <String>['pushed / (previous is <none>)', 'pushed B (previous is /)']);
await tester.tap(find.text('B'));
await tester.pump();
await tester.pump(const Duration(seconds: 1));
expect(log, <String>['pushed / (previous is <none>)', 'pushed B (previous is /)', 'pushed C (previous is B)']);
await tester.tap(find.text('C'));
await tester.pump();
await tester.pump(const Duration(seconds: 1));
expect(log, <String>['pushed / (previous is <none>)', 'pushed B (previous is /)', 'pushed C (previous is B)', 'replaced B with D']);
});
testWidgets('ModalRoute.of sets up a route to rebuild if its state changes', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = new GlobalKey<NavigatorState>();
final List<String> log = <String>[];
Route<void> routeB;
await tester.pumpWidget(new MaterialApp(
navigatorKey: key,
home: new FlatButton(
child: const Text('A'),
onPressed: () {
key.currentState.push<void>(routeB = new MaterialPageRoute<void>(
settings: const RouteSettings(name: 'B'),
builder: (BuildContext context) {
log.add('building B');
return new FlatButton(
child: const Text('B'),
onPressed: () {
key.currentState.push<void>(new MaterialPageRoute<int>(
settings: const RouteSettings(name: 'C'),
builder: (BuildContext context) {
log.add('building C');
log.add('found ${ModalRoute.of(context).settings.name}');
return new FlatButton(
child: const Text('C'),
onPressed: () {
key.currentState.replace(
oldRoute: routeB,
newRoute: new MaterialPageRoute<int>(
settings: const RouteSettings(name: 'D'),
builder: (BuildContext context) {
log.add('building D');
return const Text('D');
},
),
);
},
);
},
));
},
);
},
));
},
),
));
expect(log, <String>[]);
await tester.tap(find.text('A'));
await tester.pumpAndSettle(const Duration(milliseconds: 10));
expect(log, <String>['building B']);
await tester.tap(find.text('B'));
await tester.pumpAndSettle(const Duration(milliseconds: 10));
expect(log, <String>['building B', 'building C', 'found C']);
await tester.tap(find.text('C'));
await tester.pumpAndSettle(const Duration(milliseconds: 10));
expect(log, <String>['building B', 'building C', 'found C', 'building D']);
key.currentState.pop<void>();
await tester.pumpAndSettle(const Duration(milliseconds: 10));
expect(log, <String>['building B', 'building C', 'found C', 'building D', 'building C', 'found C']);
});
}
......@@ -86,7 +86,7 @@ void main() {
child: new Builder(
key: insideKey,
builder: (BuildContext context) {
final PageRoute<Null> route = ModalRoute.of(context);
final PageRoute<void> route = ModalRoute.of(context);
return new Column(
children: <Widget>[
new TestTransition(
......
......@@ -5,7 +5,8 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
class TestOverlayRoute extends OverlayRoute<Null> {
class TestOverlayRoute extends OverlayRoute<void> {
TestOverlayRoute({ RouteSettings settings }) : super(settings: settings);
@override
Iterable<OverlayEntry> createOverlayEntries() sync* {
yield new OverlayEntry(builder: _build);
......@@ -311,7 +312,7 @@ void main() {
await tester.pumpWidget(new MaterialApp(routes: routes));
final PageRoute<Null> route = new MaterialPageRoute<Null>(
final PageRoute<void> route = new MaterialPageRoute<void>(
settings: const RouteSettings(name: '/page'),
builder: (BuildContext context) => const Center(child: const Text('page')),
);
......
......@@ -37,13 +37,13 @@ Future<Null> performTest(WidgetTester tester, bool maintainState) async {
key: navigatorKey,
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/') {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (_) => new Container(child: const ThePositiveNumbers(from: 0)),
maintainState: maintainState,
);
} else if (settings.name == '/second') {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
settings: settings,
builder: (_) => new Container(child: const ThePositiveNumbers(from: 10000)),
maintainState: maintainState,
......
......@@ -45,9 +45,11 @@ class TestRoute extends LocalHistoryRoute<String> {
}
@override
void didReplace(covariant TestRoute oldRoute) {
log('didReplace ${oldRoute.name}');
super.didReplace(oldRoute);
void didReplace(Route<dynamic> oldRoute) {
expect(oldRoute, const isInstanceOf<TestRoute>());
final TestRoute castRoute = oldRoute;
log('didReplace ${castRoute.name}');
super.didReplace(castRoute);
}
@override
......@@ -60,15 +62,19 @@ class TestRoute extends LocalHistoryRoute<String> {
}
@override
void didPopNext(covariant TestRoute nextRoute) {
log('didPopNext ${nextRoute.name}');
super.didPopNext(nextRoute);
void didPopNext(Route<dynamic> nextRoute) {
expect(nextRoute, const isInstanceOf<TestRoute>());
final TestRoute castRoute = nextRoute;
log('didPopNext ${castRoute.name}');
super.didPopNext(castRoute);
}
@override
void didChangeNext(covariant TestRoute nextRoute) {
log('didChangeNext ${nextRoute?.name}');
super.didChangeNext(nextRoute);
void didChangeNext(Route<dynamic> nextRoute) {
expect(nextRoute, anyOf(isNull, const isInstanceOf<TestRoute>()));
final TestRoute castRoute = nextRoute;
log('didChangeNext ${castRoute?.name}');
super.didChangeNext(castRoute);
}
@override
......
......@@ -54,7 +54,7 @@ Widget buildFrame({
localizationsDelegates: delegates,
localeResolutionCallback: localeResolutionCallback,
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<Null>(
return new MaterialPageRoute<void>(
builder: (BuildContext context) {
return buildContent(context);
}
......
......@@ -145,7 +145,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new Navigator(
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute<dynamic>(builder: (BuildContext context) {
return new MaterialPageRoute<void>(builder: (BuildContext context) {
return new FlatButton(
onPressed: () {
showTimePicker(context: context, initialTime: const TimeOfDay(hour: 7, minute: 0));
......
......@@ -153,7 +153,7 @@ Widget buildFrame({
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
onGenerateRoute: (RouteSettings settings) {
return new PageRouteBuilder<Null>(
return new PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> __) {
return buildContent(context);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment