Commit ce6a1041 authored by Hixie's avatar Hixie

New methods for manipulating Navigators.

* NavigatorState.replaceRouteBefore()
* NavigatorState.removeRouteBefore()
* NavigatorState.popUntil()
parent c951f66f
......@@ -224,6 +224,40 @@ class NavigatorState extends State<Navigator> {
assert(() { _debugLocked = false; return true; });
}
/// Like replace(), but affects the route before the given anchorRoute rather
/// than the anchorRoute itself.
///
/// If newRoute is already the route before anchorRoute, then the call is
/// ignored.
///
/// The conditions described for [replace()] apply; for instance, the route
/// before anchorRoute must have overlays.
void replaceRouteBefore({ Route anchorRoute, Route newRoute }) {
assert(anchorRoute != null);
assert(anchorRoute._navigator == this);
assert(_history.indexOf(anchorRoute) > 0);
replace(oldRoute: _history[_history.indexOf(anchorRoute)-1], newRoute: newRoute);
}
/// Removes the route prior to the given anchorRoute without notifying
/// neighbouring routes or the navigator observer, if any.
void removeRouteBefore(Route anchorRoute) {
assert(!_debugLocked);
assert(() { _debugLocked = true; return true; });
assert(anchorRoute._navigator == this);
int index = _history.indexOf(anchorRoute) - 1;
assert(index >= 0);
Route targetRoute = _history[index];
assert(targetRoute._navigator == this);
assert(targetRoute.overlayEntries.isEmpty || !overlay.debugIsVisible(targetRoute.overlayEntries.last));
setState(() {
_history.removeAt(index);
targetRoute.dispose();
targetRoute._navigator = null;
});
assert(() { _debugLocked = false; return true; });
}
/// Removes the current route, notifying the observer (if any), and the
/// previous routes (using [Route.didPopNext]).
///
......@@ -258,6 +292,14 @@ class NavigatorState extends State<Navigator> {
return true;
}
/// Calls pop() repeatedly until the given route is the current route.
/// If it is already the current route, nothing happens.
void popUntil(Route targetRoute) {
assert(_history.contains(targetRoute));
while (!targetRoute.isCurrent)
pop();
}
Widget build(BuildContext context) {
assert(!_debugLocked);
assert(_history.isNotEmpty);
......
......@@ -8,6 +8,8 @@ import 'package:test/test.dart';
final List<String> results = <String>[];
Set<TestRoute> routes = new Set<TestRoute>();
class TestRoute extends Route<String> {
TestRoute(this.name);
final String name;
......@@ -28,6 +30,7 @@ class TestRoute extends Route<String> {
);
_entries.add(entry);
overlay?.insert(entry, above: insertionPoint);
routes.add(this);
}
void didPush() {
......@@ -40,7 +43,10 @@ class TestRoute extends Route<String> {
bool didPop(String result) {
log('didPop $result');
return super.didPop(result);
bool returnValue;
if (returnValue = super.didPop(result))
dispose();
return returnValue;
}
void didPushNext(TestRoute nextRoute) {
......@@ -59,6 +65,7 @@ class TestRoute extends Route<String> {
log('dispose');
_entries.forEach((OverlayEntry entry) { entry.remove(); });
_entries.clear();
routes.remove(this);
}
}
......@@ -77,7 +84,7 @@ void runNavigatorTest(
}
void main() {
test('Route management', () {
test('Route management - push, replace, pop', () {
testWidgets((WidgetTester tester) {
GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
tester.pumpWidget(new Navigator(
......@@ -141,6 +148,7 @@ void main() {
},
[
'third: didPop hello',
'third: dispose',
'two: didPopNext third',
]
);
......@@ -152,9 +160,209 @@ void main() {
},
[
'two: didPop good bye',
'two: dispose',
'initial: didPopNext two',
]
);
tester.pumpWidget(new Container());
expect(results, equals(['initial: dispose']));
expect(routes.isEmpty, isTrue);
results.clear();
});
});
test('Route management - push, remove, pop', () {
testWidgets((WidgetTester tester) {
GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
tester.pumpWidget(new Navigator(
key: navigatorKey,
onGenerateRoute: (_) => new TestRoute('first')
));
NavigatorState host = navigatorKey.currentState;
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
},
[
'first: install',
'first: didPush',
]
);
TestRoute second;
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.push(second = new TestRoute('second'));
},
[
'second: install',
'second: didPush',
'first: didPushNext second',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.push(new TestRoute('third'));
},
[
'third: install',
'third: didPush',
'second: didPushNext third',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.removeRouteBefore(second);
},
[
'first: dispose',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.pop('good bye');
},
[
'third: didPop good bye',
'third: dispose',
'second: didPopNext third',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.push(new TestRoute('three'));
},
[
'three: install',
'three: didPush',
'second: didPushNext three',
]
);
TestRoute four;
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.push(four = new TestRoute('four'));
},
[
'four: install',
'four: didPush',
'three: didPushNext four',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.removeRouteBefore(four);
},
[
'three: dispose',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.pop('the end');
},
[
'four: didPop the end',
'four: dispose',
'second: didPopNext four',
]
);
tester.pumpWidget(new Container());
expect(results, equals(['second: dispose']));
expect(routes.isEmpty, isTrue);
results.clear();
});
});
test('Route management - push, replace, popUntil', () {
testWidgets((WidgetTester tester) {
GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
tester.pumpWidget(new Navigator(
key: navigatorKey,
onGenerateRoute: (_) => new TestRoute('A')
));
NavigatorState host = navigatorKey.currentState;
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
},
[
'A: install',
'A: didPush',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.push(new TestRoute('B'));
},
[
'B: install',
'B: didPush',
'A: didPushNext B',
]
);
TestRoute routeC;
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.push(routeC = new TestRoute('C'));
},
[
'C: install',
'C: didPush',
'B: didPushNext C',
]
);
TestRoute routeB;
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.replaceRouteBefore(anchorRoute: routeC, newRoute: routeB = new TestRoute('b'));
},
[
'b: install',
'b: didReplace B',
'A: didReplaceNext B b',
'B: dispose',
]
);
runNavigatorTest(
tester,
host,
(NavigatorState transaction) {
transaction.popUntil(routeB);
},
[
'C: didPop null',
'C: dispose',
'b: didPopNext C',
]
);
tester.pumpWidget(new Container());
expect(results, equals(['A: dispose', 'b: dispose']));
expect(routes.isEmpty, isTrue);
results.clear();
});
});
}
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