Pesto home stack (#5168)
This commit is contained in:
parent
fb3e64cce4
commit
56a2d2262c
@ -142,20 +142,25 @@ class _PestoDemoState extends State<PestoDemo> {
|
|||||||
new DrawerItem(
|
new DrawerItem(
|
||||||
child: new Text('Home'),
|
child: new Text('Home'),
|
||||||
selected: !config.showFavorites,
|
selected: !config.showFavorites,
|
||||||
onPressed: () => Navigator.pushNamed(context, PestoDemo.routeName)
|
onPressed: () {
|
||||||
|
Navigator.popUntil(context, ModalRoute.withName('/pesto'));
|
||||||
|
}
|
||||||
),
|
),
|
||||||
new DrawerItem(
|
new DrawerItem(
|
||||||
child: new Text('Favorites'),
|
child: new Text('Favorites'),
|
||||||
selected: config.showFavorites,
|
selected: config.showFavorites,
|
||||||
onPressed: () { _showFavorites(context); }
|
onPressed: () {
|
||||||
|
if (config.showFavorites)
|
||||||
|
Navigator.pop(context);
|
||||||
|
else
|
||||||
|
_showFavorites(context);
|
||||||
|
}
|
||||||
),
|
),
|
||||||
new Divider(),
|
new Divider(),
|
||||||
new DrawerItem(
|
new DrawerItem(
|
||||||
child: new Text('Return to Gallery'),
|
child: new Text('Return to Gallery'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context)
|
Navigator.popUntil(context, ModalRoute.withName('/'));
|
||||||
..pop() // Close the drawer.
|
|
||||||
..pop(); // Go back to the gallery.
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -193,6 +198,7 @@ class _PestoDemoState extends State<PestoDemo> {
|
|||||||
|
|
||||||
void _showFavorites(BuildContext context) {
|
void _showFavorites(BuildContext context) {
|
||||||
Navigator.push(context, new MaterialPageRoute<Null>(
|
Navigator.push(context, new MaterialPageRoute<Null>(
|
||||||
|
settings: const RouteSettings(name: "/pesto/favorites"),
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return new PestoDemo(showFavorites: true);
|
return new PestoDemo(showFavorites: true);
|
||||||
}
|
}
|
||||||
@ -201,6 +207,7 @@ class _PestoDemoState extends State<PestoDemo> {
|
|||||||
|
|
||||||
void _showRecipe(BuildContext context, Recipe recipe) {
|
void _showRecipe(BuildContext context, Recipe recipe) {
|
||||||
Navigator.push(context, new MaterialPageRoute<Null>(
|
Navigator.push(context, new MaterialPageRoute<Null>(
|
||||||
|
settings: const RouteSettings(name: "/pesto/recipe"),
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return new Theme(
|
return new Theme(
|
||||||
data: _kTheme,
|
data: _kTheme,
|
||||||
|
55
examples/flutter_gallery/test/pesto_test.dart
Normal file
55
examples/flutter_gallery/test/pesto_test.dart
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2016 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/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_gallery/main.dart' as flutter_gallery_main;
|
||||||
|
|
||||||
|
Finder byTooltip(WidgetTester tester, String message) {
|
||||||
|
return find.byWidgetPredicate((Widget widget) {
|
||||||
|
return widget is Tooltip && widget.message == message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Finder findNavigationMenuButton(WidgetTester tester) {
|
||||||
|
return byTooltip(tester, 'Open navigation menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding binding =
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
if (binding is LiveTestWidgetsFlutterBinding) binding.allowAllFrames = true;
|
||||||
|
|
||||||
|
// Regression test for https://github.com/flutter/flutter/pull/5168
|
||||||
|
testWidgets('Pesto route management', (WidgetTester tester) async {
|
||||||
|
flutter_gallery_main
|
||||||
|
.main(); // builds the app and schedules a frame but doesn't trigger one
|
||||||
|
await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
|
||||||
|
await tester.pump(); // triggers a frame
|
||||||
|
|
||||||
|
expect(find.text('Pesto'), findsOneWidget);
|
||||||
|
await tester.tap(find.text('Pesto'));
|
||||||
|
await tester.pump(); // Launch pesto
|
||||||
|
await tester.pump(const Duration(seconds: 1)); // transition is complete
|
||||||
|
|
||||||
|
Future<Null> tapDrawerItem(String title) async {
|
||||||
|
await tester.tap(findNavigationMenuButton(tester));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1)); // drawer opening animation
|
||||||
|
await tester.tap(find.text(title));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1)); // drawer closing animation
|
||||||
|
await tester.pump(); // maybe open a new page
|
||||||
|
return tester.pump(const Duration(seconds: 1)); // new page transition
|
||||||
|
}
|
||||||
|
await tapDrawerItem('Home');
|
||||||
|
await tapDrawerItem('Favorites');
|
||||||
|
await tapDrawerItem('Home');
|
||||||
|
await tapDrawerItem('Favorites');
|
||||||
|
await tapDrawerItem('Home');
|
||||||
|
await tapDrawerItem('Return to Gallery');
|
||||||
|
|
||||||
|
expect(find.text('Flutter gallery'), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
@ -161,6 +161,9 @@ class NavigatorObserver {
|
|||||||
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }
|
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signature for the [Navigator.popUntil] predicate argument.
|
||||||
|
typedef bool RoutePredicate(Route<dynamic> route);
|
||||||
|
|
||||||
/// A widget that manages a set of child widgets with a stack discipline.
|
/// A widget that manages a set of child widgets with a stack discipline.
|
||||||
///
|
///
|
||||||
/// Many apps have a navigator near the top of their widget hierarchy in order
|
/// Many apps have a navigator near the top of their widget hierarchy in order
|
||||||
@ -243,10 +246,11 @@ class Navigator extends StatefulWidget {
|
|||||||
return Navigator.of(context).pop(result);
|
return Navigator.of(context).pop(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls pop() repeatedly until the given route is the current route.
|
/// Calls [pop()] repeatedly until the predicate returns false.
|
||||||
/// If it is already the current route, nothing happens.
|
/// The predicate may be applied to the same route more than once if
|
||||||
static void popUntil(BuildContext context, Route<dynamic> targetRoute) {
|
/// [Route.willHandlePopInternally] is true.
|
||||||
Navigator.of(context).popUntil(targetRoute);
|
static void popUntil(BuildContext context, RoutePredicate predicate) {
|
||||||
|
Navigator.of(context).popUntil(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the navigator that most tightly encloses the given context can be popped.
|
/// Whether the navigator that most tightly encloses the given context can be popped.
|
||||||
@ -463,9 +467,8 @@ class NavigatorState extends State<Navigator> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void popUntil(Route<dynamic> targetRoute) {
|
void popUntil(RoutePredicate predicate) {
|
||||||
assert(_history.contains(targetRoute));
|
while (!predicate(_history.last))
|
||||||
while (!targetRoute.isCurrent)
|
|
||||||
pop();
|
pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,6 +487,17 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a predicate that's true if the route has the specified name and if
|
||||||
|
/// popping the route will not yield the same route, i.e. if the route's
|
||||||
|
/// [willHandlePopInternally] property is false.
|
||||||
|
///
|
||||||
|
/// This function is typically used with [Navigator.popUntil()].
|
||||||
|
static RoutePredicate withName(String name) {
|
||||||
|
return (Route<dynamic> route) {
|
||||||
|
return !route.willHandlePopInternally
|
||||||
|
&& route is ModalRoute && route.settings.name == name;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// The API for subclasses to override - used by _ModalScope
|
// The API for subclasses to override - used by _ModalScope
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ final List<String> results = <String>[];
|
|||||||
|
|
||||||
Set<TestRoute> routes = new HashSet<TestRoute>();
|
Set<TestRoute> routes = new HashSet<TestRoute>();
|
||||||
|
|
||||||
class TestRoute extends Route<String> {
|
class TestRoute extends LocalHistoryRoute<String> {
|
||||||
TestRoute(this.name);
|
TestRoute(this.name);
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ void main() {
|
|||||||
await runNavigatorTest(
|
await runNavigatorTest(
|
||||||
tester,
|
tester,
|
||||||
host,
|
host,
|
||||||
() { host.popUntil(routeB); },
|
() { host.popUntil((Route<dynamic> route) => route == routeB); },
|
||||||
<String>[
|
<String>[
|
||||||
'C: didPop null',
|
'C: didPop null',
|
||||||
'C: dispose',
|
'C: dispose',
|
||||||
@ -341,4 +341,42 @@ void main() {
|
|||||||
expect(routes.isEmpty, isTrue);
|
expect(routes.isEmpty, isTrue);
|
||||||
results.clear();
|
results.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Route localHistory - popUntil', (WidgetTester tester) async {
|
||||||
|
TestRoute routeA = new TestRoute('A');
|
||||||
|
routeA.addLocalHistoryEntry(new LocalHistoryEntry(
|
||||||
|
onRemove: () { routeA.log('onRemove 0'); }
|
||||||
|
));
|
||||||
|
routeA.addLocalHistoryEntry(new LocalHistoryEntry(
|
||||||
|
onRemove: () { routeA.log('onRemove 1'); }
|
||||||
|
));
|
||||||
|
GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
|
||||||
|
await tester.pumpWidget(new Navigator(
|
||||||
|
key: navigatorKey,
|
||||||
|
onGenerateRoute: (_) => routeA
|
||||||
|
));
|
||||||
|
NavigatorState host = navigatorKey.currentState;
|
||||||
|
await runNavigatorTest(
|
||||||
|
tester,
|
||||||
|
host,
|
||||||
|
() { host.popUntil((Route<dynamic> route) => !route.willHandlePopInternally); },
|
||||||
|
<String>[
|
||||||
|
'A: install',
|
||||||
|
'A: didPush',
|
||||||
|
'A: didChangeNext null',
|
||||||
|
'A: didPop null',
|
||||||
|
'A: onRemove 1',
|
||||||
|
'A: didPop null',
|
||||||
|
'A: onRemove 0',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
await runNavigatorTest(
|
||||||
|
tester,
|
||||||
|
host,
|
||||||
|
() { host.popUntil((Route<dynamic> route) => !route.willHandlePopInternally); },
|
||||||
|
<String>[
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user